package main import "core:math" import rl "vendor:raylib" Game :: struct { running: bool, time: f32, // total time elapsed tilemap: Tilemap, camera: Camera2D, selection: Selection, world: World, player_colonist: Entity, } Camera2D :: struct { offset: rl.Vector2, zoom: f32, } Tile_Coord :: struct { x, y: int, } Selection :: struct { active: bool, tile: Tile_Coord, } CAMERA_PAN_SPEED :: 400 game_init :: proc() -> Game { g := Game { running = true, time = 0, } g.tilemap = tilemap_init() g.camera = camera_init() g.world = world_init() center := Tile_Coord{MAP_WIDTH / 2, MAP_HEIGHT / 2} g.player_colonist = entity_spawn_colonist(&g.world.entities, center) return g } camera_init :: proc() -> Camera2D { camera_x := f32(MAP_WIDTH * TILE_SIZE) * 0.5 camera_y := f32(MAP_HEIGHT * TILE_SIZE) * 0.5 return Camera2D{offset = {camera_x, camera_y}, zoom = 1.0} } camera_update :: proc(camera: ^Camera2D, dt: f32) { pan := rl.Vector2{0, 0} if rl.IsKeyDown(.W) || rl.IsKeyDown(.UP) {pan.y -= 1} if rl.IsKeyDown(.S) || rl.IsKeyDown(.DOWN) {pan.y += 1} if rl.IsKeyDown(.A) || rl.IsKeyDown(.LEFT) {pan.x -= 1} if rl.IsKeyDown(.D) || rl.IsKeyDown(.RIGHT) {pan.x += 1} if pan.x != 0 || pan.y != 0 { len := math.sqrt(pan.x * pan.x + pan.y * pan.y) pan.x /= len pan.y /= len camera.offset.x += pan.x * CAMERA_PAN_SPEED * dt / camera.zoom camera.offset.y += pan.y * CAMERA_PAN_SPEED * dt / camera.zoom } wheel := rl.GetMouseWheelMove() if wheel != 0 { camera.zoom = math.clamp(camera.zoom + wheel * 0.15, 0.35, 3.0) } } world_to_screen :: proc(camera: ^Camera2D, wx, wy: f32) -> (f32, f32) { screen_x := (wx - camera.offset.x) * camera.zoom + f32(WINDOW_WIDTH) * 0.5 screen_y := (wy - camera.offset.y) * camera.zoom + f32(WINDOW_HEIGHT) * 0.5 return screen_x, screen_y } screen_to_world :: proc(camera: ^Camera2D, sx, sy: f32) -> (f32, f32) { world_x := (sx - f32(WINDOW_WIDTH) * 0.5) / camera.zoom + camera.offset.x world_y := (sy - f32(WINDOW_HEIGHT) * 0.5) / camera.zoom + camera.offset.y return world_x, world_y } world_to_tile :: proc(wx, wy: f32) -> (int, int) { tile_x := int(math.floor(wx / f32(TILE_SIZE))) tile_y := int(math.floor(wy / f32(TILE_SIZE))) return tile_x, tile_y } game_handle_input :: proc(g: ^Game) { if rl.IsMouseButtonPressed(.LEFT) { mouse_x := f32(rl.GetMouseX()) mouse_y := f32(rl.GetMouseY()) world_x, world_y := screen_to_world(&g.camera, mouse_x, mouse_y) tile_x, tile_y := world_to_tile(world_x, world_y) if tile_in_bounds(&g.tilemap, tile_x, tile_y) { g.selection.active = true g.selection.tile = {tile_x, tile_y} } else { g.selection.active = false } } if rl.IsMouseButtonPressed(.RIGHT) { mx, my := f32(rl.GetMouseX()), f32(rl.GetMouseY()) wx, wy := screen_to_world(&g.camera, mx, my) tx, ty := world_to_tile(wx, wy) if tile_in_bounds(&g.tilemap, tx, ty) { entity_set_move_target(&g.world.entities, g.player_colonist, &g.tilemap, {tx, ty}) g.selection.active = true g.selection.tile = {tx, ty} } } } game_update :: proc(g: ^Game, dt: f32) { g.time += dt if rl.IsKeyPressed(.ESCAPE) { g.running = false } camera_update(&g.camera, dt) game_handle_input(g) world_update(&g.world, dt) } game_draw :: proc(g: ^Game) { tilemap_draw(&g.tilemap, &g.camera) tilemap_draw_selection(&g.tilemap, &g.camera, &g.selection) world_draw(&g.world, &g.camera) if g.selection.active { rl.DrawText( rl.TextFormat("Selection: (%i,%i)", g.selection.tile.x, g.selection.tile.y), 10, 108, 20, rl.GOLD, ) } fps := rl.GetFPS() rl.DrawText(rl.TextFormat("FPS: %i", fps), 10, 36, 20, rl.GREEN) rl.DrawText(rl.TextFormat("Time: %.1fs", g.time), 10, 62, 20, rl.GRAY) rl.DrawText(rl.TextFormat("Zoom: %.2f", g.camera.zoom), 10, 88, 20, rl.GRAY) } game_shutdown :: proc(g: ^Game) { entity_world_destroy(&g.world.entities) tilemap_destroy(&g.tilemap) }