From e166076e112e1f5cfbfa189a16909304e3621ec0 Mon Sep 17 00:00:00 2001 From: codegirl-007 Date: Sat, 27 Jun 2026 23:58:18 -0700 Subject: [PATCH] split tower and tower placement --- game/tower.odin | 133 ------------------------------------ game/tower_placement.odin | 139 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 133 deletions(-) create mode 100644 game/tower_placement.odin diff --git a/game/tower.odin b/game/tower.odin index fea6c16..f8764da 100644 --- a/game/tower.odin +++ b/game/tower.odin @@ -85,121 +85,11 @@ tower_stats_at_level :: proc(tower: Tower) -> (damage: int, range: f32) { return } -can_place_tower :: proc(world: ^World, gx, gy: int, kind: Tower_Kind) -> bool { - if world.phase != .Build do return false - arch := get_archetype(kind) - if world.gold < arch.cost do return false - if !footprint_fits_map(&world.world_map, gx, gy, arch.footprint_w, arch.footprint_h) do return false - if footprint_blocked(world, gx, gy, arch.footprint_w, arch.footprint_h) do return false - return true -} - -rects_overlap :: proc( - left_a, top_a, width_a, height_a: int, - left_b, top_b, width_b, height_b: int, -) -> bool { - return( - left_a < left_b + width_b && - left_a + width_a > left_b && - top_a < top_b + height_b && - top_a + height_a > top_b \ - ) -} - -footprint_center :: proc(gx, gy, footprint_w, footprint_h: int) -> Vec2 { - return Vec2 { - f32(gx * TILE_SIZE + (footprint_w * TILE_SIZE) / 2), - f32(gy * TILE_SIZE + (footprint_h * TILE_SIZE) / 2), - } -} - -footprint_fits_map :: proc(world_map: ^Map, gx, gy, footprint_w, footprint_h: int) -> bool { - if gx < 0 || gy < 0 do return false - if gx + footprint_w > MAP_W do return false - if gy + footprint_h > MAP_H do return false - - for column in 0 ..< footprint_h { - for row in 0 ..< footprint_w { - cell_column := gx + column - cell_row := gy + row - if !can_build_at(world_map, cell_column, cell_row) do return false - } - } - - return true -} - -footprint_blocked :: proc(world: ^World, gx, gy, footprint_w, footprint_h: int) -> bool { - for placed_tower in world.towers { - archetype := get_archetype(placed_tower.kind) - if rects_overlap( - gx, - gy, - footprint_w, - footprint_h, - placed_tower.anchor_gx, - placed_tower.anchor_gy, - archetype.footprint_w, - archetype.footprint_h, - ) { - return true - } - } - - return false -} get_archetype :: proc(kind: Tower_Kind) -> Tower_Archetype { return TOWER_ARCHETYPES[kind] } -try_upgrade_tower :: proc(world: ^World, gx, gy: int) -> bool { - if world.phase != .Build do return false - t := tower_at_grid(world, gx, gy) - if t == nil do return false - - arch := get_archetype(t.kind) - if t.upgrade_level >= arch.max_upgrade do return false - - cost := arch.upgrade_cost + t.upgrade_level * 25 - if world.gold < cost do return false - - world.gold -= cost - t.upgrade_level += 1 - return true -} - -try_place_tower :: proc(world: ^World, gx, gy: int, kind: Tower_Kind) -> bool { - if !can_place_tower(world, gx, gy, kind) do return false - - arch := get_archetype(kind) - world.gold -= arch.cost - append( - &world.towers, - Tower { - position = footprint_center(gx, gy, arch.footprint_w, arch.footprint_h), - anchor_gx = gx, - anchor_gy = gy, - kind = kind, - cooldown = 0, - }, - ) - return true -} - -tower_at_grid :: proc(world: ^World, gx, gy: int) -> ^Tower { - for i in 0 ..< len(world.towers) { - t := &world.towers[i] - arch := get_archetype(t.kind) - if gx >= t.anchor_gx && - gx < t.anchor_gx + arch.footprint_w && - gy >= t.anchor_gy && - gy < t.anchor_gy + arch.footprint_h { - return t - } - } - return nil -} find_target_slot :: proc(tower: Tower, world: ^World) -> int { best_slot: int = -1 @@ -298,26 +188,3 @@ render_towers :: proc(world: ^World) { } } -render_placement_preview :: proc(world: ^World) { - if world.phase != .Build do return - - mouse := game_mouse() - gx, gy := screen_to_grid(mouse.x, mouse.y) - arch := get_archetype(world.selected_tower) - ok := can_place_tower(world, gx, gy, world.selected_tower) - - center := footprint_center(gx, gy, arch.footprint_w, arch.footprint_h) - rl.DrawCircleLines(i32(center.x), i32(center.y), arch.range, rl.Fade(rl.SKYBLUE, 0.4)) - - tint := rl.Fade(rl.GREEN, 0.3) - if !ok do tint = rl.Fade(rl.RED, 0.35) - - for row in 0 ..< arch.footprint_h { - for col in 0 ..< arch.footprint_w { - px := (gx + col) * TILE_SIZE - py := (gy + row) * TILE_SIZE - rl.DrawRectangle(i32(px), i32(py), TILE_SIZE, TILE_SIZE, tint) - } - } -} - diff --git a/game/tower_placement.odin b/game/tower_placement.odin new file mode 100644 index 0000000..cbcdc4f --- /dev/null +++ b/game/tower_placement.odin @@ -0,0 +1,139 @@ +package game + +import rl "vendor:raylib" + +can_place_tower :: proc(world: ^World, gx, gy: int, kind: Tower_Kind) -> bool { + if world.phase != .Build do return false + arch := get_archetype(kind) + if world.gold < arch.cost do return false + if !footprint_fits_map(&world.world_map, gx, gy, arch.footprint_w, arch.footprint_h) do return false + if footprint_blocked(world, gx, gy, arch.footprint_w, arch.footprint_h) do return false + return true +} + +rects_overlap :: proc( + left_a, top_a, width_a, height_a: int, + left_b, top_b, width_b, height_b: int, +) -> bool { + return( + left_a < left_b + width_b && + left_a + width_a > left_b && + top_a < top_b + height_b && + top_a + height_a > top_b \ + ) +} + +footprint_center :: proc(gx, gy, footprint_w, footprint_h: int) -> Vec2 { + return Vec2 { + f32(gx * TILE_SIZE + (footprint_w * TILE_SIZE) / 2), + f32(gy * TILE_SIZE + (footprint_h * TILE_SIZE) / 2), + } +} + +footprint_fits_map :: proc(world_map: ^Map, gx, gy, footprint_w, footprint_h: int) -> bool { + if gx < 0 || gy < 0 do return false + if gx + footprint_w > MAP_W do return false + if gy + footprint_h > MAP_H do return false + + for column in 0 ..< footprint_h { + for row in 0 ..< footprint_w { + cell_column := gx + column + cell_row := gy + row + if !can_build_at(world_map, cell_column, cell_row) do return false + } + } + + return true +} + +footprint_blocked :: proc(world: ^World, gx, gy, footprint_w, footprint_h: int) -> bool { + for placed_tower in world.towers { + archetype := get_archetype(placed_tower.kind) + if rects_overlap( + gx, + gy, + footprint_w, + footprint_h, + placed_tower.anchor_gx, + placed_tower.anchor_gy, + archetype.footprint_w, + archetype.footprint_h, + ) { + return true + } + } + + return false +} + +try_place_tower :: proc(world: ^World, gx, gy: int, kind: Tower_Kind) -> bool { + if !can_place_tower(world, gx, gy, kind) do return false + + arch := get_archetype(kind) + world.gold -= arch.cost + append( + &world.towers, + Tower { + position = footprint_center(gx, gy, arch.footprint_w, arch.footprint_h), + anchor_gx = gx, + anchor_gy = gy, + kind = kind, + cooldown = 0, + }, + ) + return true +} + +tower_at_grid :: proc(world: ^World, gx, gy: int) -> ^Tower { + for i in 0 ..< len(world.towers) { + t := &world.towers[i] + arch := get_archetype(t.kind) + if gx >= t.anchor_gx && + gx < t.anchor_gx + arch.footprint_w && + gy >= t.anchor_gy && + gy < t.anchor_gy + arch.footprint_h { + return t + } + } + return nil +} + +try_upgrade_tower :: proc(world: ^World, gx, gy: int) -> bool { + if world.phase != .Build do return false + t := tower_at_grid(world, gx, gy) + if t == nil do return false + + arch := get_archetype(t.kind) + if t.upgrade_level >= arch.max_upgrade do return false + + cost := arch.upgrade_cost + t.upgrade_level * 25 + if world.gold < cost do return false + + world.gold -= cost + t.upgrade_level += 1 + return true +} + +render_placement_preview :: proc(world: ^World) { + if world.phase != .Build do return + + mouse := game_mouse() + gx, gy := screen_to_grid(mouse.x, mouse.y) + arch := get_archetype(world.selected_tower) + ok := can_place_tower(world, gx, gy, world.selected_tower) + + center := footprint_center(gx, gy, arch.footprint_w, arch.footprint_h) + rl.DrawCircleLines(i32(center.x), i32(center.y), arch.range, rl.Fade(rl.SKYBLUE, 0.4)) + + tint := rl.Fade(rl.GREEN, 0.3) + if !ok do tint = rl.Fade(rl.RED, 0.35) + + for row in 0 ..< arch.footprint_h { + for col in 0 ..< arch.footprint_w { + px := (gx + col) * TILE_SIZE + py := (gy + row) * TILE_SIZE + rl.DrawRectangle(i32(px), i32(py), TILE_SIZE, TILE_SIZE, tint) + } + } +} +