Files
tower-defense-prototype/game/enemy.odin
T

154 lines
3.0 KiB
Odin

package game
import "core:fmt"
import "core:sys/posix"
import rl "vendor:raylib"
Enemy :: struct {
position: Vec2,
health: int,
max_health: int,
active: bool,
path_index: int,
base_speed: f32,
slow_timer: f32,
kind: Enemy_Kind,
}
Enemy_Kind :: enum {
Grunt,
Runner,
Tank,
}
Enemy_Def :: struct {
base_health: int,
health_per_wave: int,
base_speed: f32,
speed_per_wave: f32,
color: rl.Color,
draw_radius: f32,
gold_reward: int,
}
ENEMY_DEF: [Enemy_Kind]Enemy_Def = {
.Grunt = {
base_health = 15,
health_per_wave = 5,
base_speed = 55,
speed_per_wave = 5,
color = rl.MAROON,
draw_radius = 11,
gold_reward = 5,
},
.Runner = {
base_health = 10,
health_per_wave = 2,
base_speed = 95,
speed_per_wave = 3,
color = rl.ORANGE,
draw_radius = 11,
gold_reward = 5,
},
.Tank = {
base_health = 50,
health_per_wave = 12,
base_speed = 35,
speed_per_wave = 2,
color = rl.DARKGREEN,
draw_radius = 14,
gold_reward = 10,
},
}
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 {
if !world.enemies[i].active {
return &world.enemies[i]
}
}
return nil
}
spawn_enemy :: proc(world: ^World, kind: Enemy_Kind) {
if len(world.path) == 0 do return // don't spawn enemies if no paths exist
e := acquire_enemy(world) // grab a free slot
if e == nil do return // if there are no slots, skil spawning
def := ENEMY_DEF[kind]
health := def.base_health + world.wave * def.health_per_wave
speed := def.base_speed + f32(world.wave) * def.speed_per_wave
e^ = Enemy {
position = world.path[0],
health = health,
max_health = health,
base_speed = speed,
slow_timer = 0,
path_index = 1,
kind = kind,
active = true,
}
}
update_enemies :: proc(world: ^World, dt: f32) {
if len(world.path) < 2 do return
for i in 0 ..< MAX_ENEMIES {
e := &world.enemies[i]
if !e.active do continue
if e.path_index >= len(world.path) {
world.base_health -= 1
e.active = false
continue
}
target := world.path[e.path_index]
move_speed := e.base_speed
if e.slow_timer > 0 {
move_speed *= ICE_SLOW_FACTOR
e.slow_timer -= dt
}
move_toward(&e.position, target, move_speed, dt)
dx := target.x - e.position.x
dy := target.y - e.position.y
if dx * dx + dy * dy < 16.0 {
e.path_index += 1
}
}
}
active_enemy_count :: proc(world: ^World) -> int {
count := 0
for i in 0 ..< MAX_ENEMIES {
if world.enemies[i].active do count += 1
}
return count
}
render_enemies :: proc(world: ^World) {
for i in 0 ..< MAX_ENEMIES {
e := world.enemies[i]
if !e.active do continue
def := ENEMY_DEF[e.kind]
rl.DrawCircle(i32(e.position.x), i32(e.position.y), def.draw_radius, def.color)
if e.slow_timer > 0 {
rl.DrawCircleLines(i32(e.position.x), i32(e.position.y), 14, rl.SKYBLUE)
}
rl.DrawText(
fmt.ctprintf("%d", e.health),
i32(e.position.x) - 4,
i32(e.position.y) - 18,
12,
rl.WHITE,
)
}
}