wave recipes + tanks
This commit is contained in:
+14
-1
@@ -18,6 +18,7 @@ Enemy :: struct {
|
||||
Enemy_Kind :: enum {
|
||||
Grunt,
|
||||
Runner,
|
||||
Tank,
|
||||
}
|
||||
|
||||
Enemy_Archetype :: struct {
|
||||
@@ -43,10 +44,18 @@ get_enemy_archetype :: proc(kind: Enemy_Kind, wave: int) -> Enemy_Archetype {
|
||||
speed = 95 + f32(wave) * 3,
|
||||
color = rl.ORANGE,
|
||||
}
|
||||
case .Tank:
|
||||
return {
|
||||
kind = .Tank,
|
||||
health = 50 + wave * 12,
|
||||
speed = 35 + f32(wave) * 2,
|
||||
color = rl.DARKGREEN,
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
acquire_enemy :: proc(world: ^World) -> ^Enemy {
|
||||
// create an object pool with fixed slots to reuse instead of allocate/free every spawn
|
||||
for i in 0 ..< MAX_ENEMIES {
|
||||
@@ -120,13 +129,17 @@ render_enemies :: proc(world: ^World) {
|
||||
e := world.enemies[i]
|
||||
if !e.active do continue
|
||||
color := rl.MAROON
|
||||
size := 11
|
||||
switch e.kind {
|
||||
case .Grunt:
|
||||
color = rl.MAROON
|
||||
case .Runner:
|
||||
color = rl.ORANGE
|
||||
case .Tank:
|
||||
color = rl.DARKGREEN
|
||||
size = 14
|
||||
}
|
||||
rl.DrawCircle(i32(e.position.x), i32(e.position.y), 11, color)
|
||||
rl.DrawCircle(i32(e.position.x), i32(e.position.y), f32(size), color)
|
||||
if e.slow_timer > 0 {
|
||||
rl.DrawCircleLines(i32(e.position.x), i32(e.position.y), 14, rl.SKYBLUE)
|
||||
}
|
||||
|
||||
+69
-25
@@ -8,10 +8,11 @@ Game_Phase :: enum {
|
||||
}
|
||||
|
||||
Wave_Spawner :: struct {
|
||||
enemies_to_spawn: int,
|
||||
spawn_timer: f32,
|
||||
spawn_interval: f32,
|
||||
spawn_count: int,
|
||||
spawn_timer: f32,
|
||||
spawn_interval: f32,
|
||||
recipe: [dynamic]Wave_Entry,
|
||||
recipe_index: int,
|
||||
entry_remaining: int,
|
||||
}
|
||||
|
||||
start_wave :: proc(world: ^World) {
|
||||
@@ -19,37 +20,80 @@ start_wave :: proc(world: ^World) {
|
||||
world.wave += 1
|
||||
world.phase = .Combat
|
||||
world.spawner = Wave_Spawner {
|
||||
enemies_to_spawn = 5 + world.wave * 2,
|
||||
spawn_timer = 0,
|
||||
spawn_interval = 0.55,
|
||||
spawn_timer = 0,
|
||||
spawn_interval = 0.55,
|
||||
recipe = build_wave_recipe(world.wave),
|
||||
recipe_index = 0,
|
||||
entry_remaining = 0,
|
||||
}
|
||||
load_next_recipe_entry(world)
|
||||
}
|
||||
|
||||
Wave_Entry :: struct {
|
||||
kind: Enemy_Kind,
|
||||
count: int,
|
||||
}
|
||||
|
||||
build_wave_recipe :: proc(wave: int) -> [dynamic]Wave_Entry {
|
||||
recipe: [dynamic]Wave_Entry
|
||||
switch wave {
|
||||
case 1:
|
||||
append(&recipe, Wave_Entry{.Grunt, 6})
|
||||
case 2:
|
||||
append(&recipe, Wave_Entry{.Grunt, 4})
|
||||
append(&recipe, Wave_Entry{.Runner, 2})
|
||||
case:
|
||||
grunt := 3 + wave
|
||||
runners := wave / 2
|
||||
tanks := wave / 3
|
||||
append(&recipe, Wave_Entry{.Grunt, grunt})
|
||||
if runners > 0 do append(&recipe, Wave_Entry{.Runner, runners})
|
||||
if tanks > 0 do append(&recipe, Wave_Entry{.Tank, tanks})
|
||||
}
|
||||
|
||||
return recipe
|
||||
}
|
||||
|
||||
load_next_recipe_entry :: proc(world: ^World) {
|
||||
s := &world.spawner
|
||||
if s.recipe_index >= len(s.recipe) {
|
||||
s.entry_remaining = 0
|
||||
return
|
||||
}
|
||||
entry := s.recipe[s.recipe_index]
|
||||
s.entry_remaining = entry.count
|
||||
}
|
||||
|
||||
clear_wave :: proc(world: ^World) -> bool {
|
||||
s := world.spawner
|
||||
if s.recipe_index < len(s.recipe) do return false
|
||||
if s.entry_remaining > 0 do return false
|
||||
return active_enemy_count(world) == 0
|
||||
}
|
||||
|
||||
update_wave :: proc(world: ^World, dt: f32) {
|
||||
if world.phase != .Combat do return
|
||||
if world.spawner.enemies_to_spawn <= 0 do return
|
||||
if len(world.path) < 2 do return
|
||||
s := &world.spawner
|
||||
if s.entry_remaining <= 0 && s.recipe_index >= len(s.recipe) do return
|
||||
|
||||
world.spawner.spawn_timer -= dt
|
||||
kind := Enemy_Kind.Grunt
|
||||
if world.spawner.spawn_count % 3 == 2 do kind = .Runner
|
||||
if world.spawner.spawn_timer <= 0 {
|
||||
health := 15 + world.wave * 5
|
||||
speed := 55 + f32(world.wave) * 5
|
||||
spawn_enemy(world, kind)
|
||||
world.spawner.spawn_count += 1
|
||||
world.spawner.enemies_to_spawn -= 1
|
||||
world.spawner.spawn_timer = world.spawner.spawn_interval
|
||||
s.spawn_timer -= dt
|
||||
if s.spawn_timer > 0 do return
|
||||
|
||||
if s.entry_remaining <= 0 {
|
||||
s.recipe_index += 1
|
||||
load_next_recipe_entry(world)
|
||||
if s.entry_remaining <= 0 do return
|
||||
}
|
||||
}
|
||||
|
||||
wave_clear :: proc(world: ^World) -> bool {
|
||||
if world.spawner.enemies_to_spawn > 0 do return false
|
||||
return active_enemy_count(world) == 0
|
||||
kind := s.recipe[s.recipe_index].kind
|
||||
spawn_enemy(world, kind)
|
||||
s.entry_remaining -= 1
|
||||
s.spawn_timer = s.spawn_interval
|
||||
|
||||
}
|
||||
|
||||
update_phase :: proc(world: ^World) {
|
||||
if world.phase == .Combat && wave_clear(world) {
|
||||
if world.phase == .Combat && clear_wave(world) {
|
||||
world.phase = .Build
|
||||
push_event(world, .Wave_Survived, gold_reward = 25)
|
||||
}
|
||||
@@ -60,7 +104,7 @@ check_end :: proc(world: ^World) {
|
||||
world.phase = .Game_Over
|
||||
}
|
||||
|
||||
if world.wave >= MAX_WAVES && world.phase == .Build && wave_clear(world) {
|
||||
if world.wave >= MAX_WAVES && world.phase == .Build && clear_wave(world) {
|
||||
world.phase = .Victory
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user