From 285b434499ec156a933bd14a90545cda1b76e982 Mon Sep 17 00:00:00 2001 From: codegirl-007 Date: Thu, 11 Jun 2026 01:05:17 -0700 Subject: [PATCH] add event queue, tower archetypes, and enemy damage. no projectiles yet --- game/commands.odin | 2 +- game/events.odin | 25 +++++++++++++++++ game/tower.odin | 69 ++++++++++++++++++++++++++++++++++++++++++++-- game/world.odin | 3 ++ 4 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 game/events.odin diff --git a/game/commands.odin b/game/commands.odin index 73f1d9e..7cd70fe 100644 --- a/game/commands.odin +++ b/game/commands.odin @@ -34,7 +34,7 @@ execute_command :: proc(world: ^World, cmd: Command) { case .Spawn_Test: spawn_group(world, 3) case .Place_Tower: - try_place_tower(world, cmd.gx, cmd.gy) + try_place_tower(world, cmd.gx, cmd.gy, .Archer) } } diff --git a/game/events.odin b/game/events.odin new file mode 100644 index 0000000..c5f4481 --- /dev/null +++ b/game/events.odin @@ -0,0 +1,25 @@ +package game + +Event_Kind :: enum { + Enemy_Killed, +} + +Event :: struct { + kind: Event_Kind, + gold_reward: int, +} + +push_event :: proc(world: ^World, kind: Event_Kind, gold_reward: int = 0) { + append(&world.events, Event{kind = kind, gold_reward = gold_reward}) +} + +process_events :: proc(world: ^World) { + for ev in world.events { + switch ev.kind { + case .Enemy_Killed: + world.gold += ev.gold_reward + } + } + clear(&world.events) +} + diff --git a/game/tower.odin b/game/tower.odin index b1138e3..02ecc1d 100644 --- a/game/tower.odin +++ b/game/tower.odin @@ -2,9 +2,32 @@ package game Tower :: struct { position: Vec2, + kind: Tower_Kind, + cooldown: f32, } -try_place_tower :: proc(world: ^World, gx, gy: int) -> bool { +Tower_Kind :: enum { + Archer, +} + +Tower_Archetype :: struct { + kind: Tower_Kind, + range: f32, + damage: int, + fire_rate: f32, + cost: int, +} + +TOWER_ARCHETYPES: [Tower_Kind]Tower_Archetype = { + .Archer = {kind = .Archer, range = 95, damage = 12, fire_rate = 0.45, cost = TOWER_COST}, +} + +get_archetype :: proc(kind: Tower_Kind) -> Tower_Archetype { + return TOWER_ARCHETYPES[kind] +} + +try_place_tower :: proc(world: ^World, gx, gy: int, kind: Tower_Kind) -> bool { + arch := get_archetype(kind) if !can_build_at(&world.world_map, gx, gy) do return false if world.gold < TOWER_COST do return false @@ -13,8 +36,48 @@ try_place_tower :: proc(world: ^World, gx, gy: int) -> bool { if distance(t.position, pos) < 1 do return false } - world.gold -= TOWER_COST - append(&world.towers, Tower{position = pos}) + world.gold -= arch.cost + append(&world.towers, Tower{position = pos, kind = kind, cooldown = 0}) return true } +find_target :: proc(tower: Tower, world: ^World) -> ^Enemy { + arch := get_archetype(tower.kind) + best: ^Enemy = nil + best_dist: f32 = 999999 + + for i in 0 ..< MAX_ENEMIES { + e := &world.enemies[i] + + if !e.active do continue + + d := distance(tower.position, e.position) + if d <= arch.range && d < best_dist { + best = e + best_dist = d + } + } + return best +} + +update_towers :: proc(world: ^World, dt: f32) { + for i in 0 ..< len(world.towers) { + t := &world.towers[i] + t.cooldown -= dt + + if t.cooldown > 0 do continue + + target := find_target(t^, world) + if target == nil do continue + + arch := get_archetype(t.kind) + target.health -= arch.damage + t.cooldown = arch.fire_rate + + if target.health <= 0 { + target.active = false + push_event(world, .Enemy_Killed, gold_reward = 5) + } + } +} + diff --git a/game/world.odin b/game/world.odin index 1cdf09f..9d2b5e8 100644 --- a/game/world.odin +++ b/game/world.odin @@ -9,6 +9,7 @@ World :: struct { path: [dynamic]Vec2, enemies: [MAX_ENEMIES]Enemy, towers: [dynamic]Tower, + events: [dynamic]Event, } init_world :: proc() -> World { @@ -27,5 +28,7 @@ update_world :: proc(world: ^World, dt: f32) { execute_command(world, cmd) update_enemies(world, dt) + update_towers(world, dt) + process_events(world) }