Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279package game
import rl "vendor:raylib"import simulation "../sim"import "core:c"import "core:fmt"import "core:strings"
BACKGROUND_COLOR :: rl.Color{13, 15, 22, 255}ROOM_COLOR :: rl.Color{24, 29, 42, 255}ROOM_EDGE_COLOR :: rl.Color{82, 94, 122, 255}PLAYER_COLOR :: rl.Color{225, 232, 255, 255}PLAYER_EDGE_COLOR :: rl.Color{88, 166, 255, 255}ECHO_COLOR :: rl.Color{117, 218, 192, 180}ECHO_EDGE_COLOR :: rl.Color{56, 126, 118, 230}TEXT_COLOR :: rl.Color{200, 210, 230, 255}SUBTLE_COLOR :: rl.Color{120, 132, 160, 255}PANEL_COLOR :: rl.Color{8, 10, 15, 204}PANEL_EDGE_COLOR :: rl.Color{42, 50, 68, 230}
RENDER_PIXELS_PER_UNIT :: f32(32)PLAYER_RADIUS_PIXELS :: f32(12)ROOM_WALL_PIXELS :: f32(3)DEBUG_LINE_HEIGHT :: f32(20)
render_init :: proc(app: ^App_State) { font := rl.LoadFontEx(DEBUG_FONT_PATH, c.int(DEBUG_FONT_SIZE), nil, 0) if rl.IsFontValid(font) { app.debug_font = font app.debug_font_loaded = true return }
app.debug_font = rl.GetFontDefault()}
render_shutdown :: proc(app: ^App_State) { if app != nil && app.debug_font_loaded { rl.UnloadFont(app.debug_font) }}
render_frame :: proc(app: ^App_State) { rl.BeginDrawing() rl.ClearBackground(BACKGROUND_COLOR)
alpha := interpolation_alpha(app) player := simulation.world_player(&app.session.sim.world) player_position := app.previous_player_position if player != nil { player_position = player.position } player_screen := sim_vec_to_render_lerped( app.previous_player_position, player_position, alpha, ) camera := rl.Camera2D{ offset = {f32(rl.GetScreenWidth()) * 0.5, f32(rl.GetScreenHeight()) * 0.5}, target = player_screen, rotation = 0, zoom = 1, }
rl.BeginMode2D(camera) render_world(app, player_screen) rl.EndMode2D()
render_debug_overlay(app) rl.EndDrawing()}
render_world :: proc(app: ^App_State, player_screen: rl.Vector2) { render_arena(app.session.sim.world.arena) for index in 0..<app.session.sim.world.actor_count { actor := app.session.sim.world.actors[index] if actor.kind == .Player { render_actor(actor, player_screen) } else { render_actor(actor, sim_vec_to_render(actor.position)) } }}
render_arena :: proc(arena: simulation.Arena_State) { min := sim_vec_to_render(arena.min) max := sim_vec_to_render(arena.max) rect := rl.Rectangle{ x = min.x, y = min.y, width = max.x - min.x, height = max.y - min.y, }
rl.DrawRectangleRec(rect, ROOM_COLOR) rl.DrawRectangleLinesEx(rect, ROOM_WALL_PIXELS, ROOM_EDGE_COLOR)}
render_actor :: proc(actor: simulation.Actor_State, center: rl.Vector2) { switch actor.kind { case .Player: rl.DrawCircleV(center, PLAYER_RADIUS_PIXELS, PLAYER_COLOR) rl.DrawCircleLinesV(center, PLAYER_RADIUS_PIXELS, PLAYER_EDGE_COLOR) case .Echo: rl.DrawCircleV(center, PLAYER_RADIUS_PIXELS, ECHO_COLOR) rl.DrawCircleLinesV(center, PLAYER_RADIUS_PIXELS, ECHO_EDGE_COLOR) case .None: }}
render_debug_overlay :: proc(app: ^App_State) { rl.DrawRectangleRec(rl.Rectangle{x = 16, y = 16, width = 500, height = 196}, PANEL_COLOR) rl.DrawRectangleLinesEx(rl.Rectangle{x = 16, y = 16, width = 500, height = 196}, 1, PANEL_EDGE_COLOR)
x := f32(28) y := f32(28) draw_debug_text(app, "Thanatograph debug", x, y, TEXT_COLOR) y += DEBUG_LINE_HEIGHT + 6 draw_debug_text( app, fmt.tprintf( "run %d tick %d frame %d", app.session.current_run_id, app.session.sim.tick, app.frame_index, ), x, y, SUBTLE_COLOR, ) y += DEBUG_LINE_HEIGHT draw_debug_text( app, fmt.tprintf( "input %s commands %d archived runs %d", move_direction_name(app.last_input.move), simulation.run_history_command_count(&app.session.current_history), len(app.session.archive.histories), ), x, y, SUBTLE_COLOR, ) y += DEBUG_LINE_HEIGHT player := simulation.world_player(&app.session.sim.world) if player != nil { draw_debug_text( app, fmt.tprintf( "player %.2f, %.2f units fixed %d,%d", sim_fixed_to_units(player.position.x), sim_fixed_to_units(player.position.y), i32(player.position.x), i32(player.position.y), ), x, y, SUBTLE_COLOR, ) } y += DEBUG_LINE_HEIGHT draw_debug_text( app, fmt.tprintf( "actors %d digest %d", app.session.sim.world.actor_count, simulation.sim_digest(&app.session.sim), ), x, y, SUBTLE_COLOR, ) y += DEBUG_LINE_HEIGHT draw_debug_text(app, first_echo_debug_line(app), x, y, SUBTLE_COLOR) y += DEBUG_LINE_HEIGHT draw_debug_text(app, "WASD move R complete run Esc quit F5 reload F6 restart", x, y, SUBTLE_COLOR)}
first_echo_debug_line :: proc(app: ^App_State) -> string { for index in 0..<app.session.sim.world.actor_count { actor := app.session.sim.world.actors[index] if actor.kind == .Echo { return fmt.tprintf( "echo actor %d source run %d cursor %d mode %s", actor.id.index, actor.echo.source_run_id, actor.echo.replay_tick, echo_mode_name(actor.echo.mode), ) } } return "echo none"}
sim_vec_to_render :: proc(value: simulation.Vec2_Fixed) -> rl.Vector2 { return {sim_fixed_to_render(value.x), sim_fixed_to_render(value.y)}}
sim_vec_to_render_lerped :: proc( previous, current: simulation.Vec2_Fixed, alpha: f32,) -> rl.Vector2 { prev := sim_vec_to_render(previous) next := sim_vec_to_render(current) return { prev.x + (next.x - prev.x) * alpha, prev.y + (next.y - prev.y) * alpha, }}
sim_fixed_to_render :: proc(value: simulation.Fixed) -> f32 { return sim_fixed_to_units(value) * RENDER_PIXELS_PER_UNIT}
sim_fixed_to_units :: proc(value: simulation.Fixed) -> f32 { return f32(i32(value)) / f32(i32(simulation.FIXED_ONE))}
interpolation_alpha :: proc(app: ^App_State) -> f32 { alpha := app.sim_accumulator / simulation.SIM_DT if alpha < 0 { return 0 } if alpha > 1 { return 1 } return f32(alpha)}
draw_debug_text :: proc(app: ^App_State, text: string, x, y: f32, color: rl.Color) { c_text := strings.clone_to_cstring(text, context.temp_allocator) rl.DrawTextEx(debug_font(app), c_text, {x, y}, DEBUG_FONT_SIZE, 1, color)}
debug_font :: proc(app: ^App_State) -> rl.Font { if app != nil && rl.IsFontValid(app.debug_font) { return app.debug_font } return rl.GetFontDefault()}
move_direction_name :: proc(direction: simulation.Move_Direction) -> string { switch direction { case .None: return "idle" case .N: return "north" case .NE: return "north-east" case .E: return "east" case .SE: return "south-east" case .S: return "south" case .SW: return "south-west" case .W: return "west" case .NW: return "north-west" }
return "unknown"}
echo_mode_name :: proc(mode: simulation.Echo_Replay_Mode) -> string { switch mode { case .None: return "none" case .Following: return "following" case .Timeline_Ended: return "timeline-ended" }
return "unknown"}