163 lines
3.7 KiB
Odin
163 lines
3.7 KiB
Odin
package game
|
|
|
|
import rl "vendor:raylib"
|
|
|
|
Projectile :: struct {
|
|
active: bool,
|
|
position: Vec2,
|
|
target_slot: int,
|
|
damage: int,
|
|
speed: f32,
|
|
mode: Projectile_Mode,
|
|
direction: Vec2,
|
|
splash_radius: f32,
|
|
}
|
|
|
|
Projectile_Mode :: enum {
|
|
Homing, // chases target
|
|
Ballistic, // flies in a fixed direction
|
|
}
|
|
|
|
acquire_projectile :: proc(world: ^World) -> ^Projectile {
|
|
for i in 0 ..< MAX_PROJECTILES {
|
|
if !world.combat.projectiles[i].active {
|
|
return &world.combat.projectiles[i]
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
enemy_still_on_map :: proc(world: ^World, slot: int) -> (^Enemy, bool) {
|
|
if slot < 0 || slot >= MAX_ENEMIES do return nil, false
|
|
e := &world.combat.enemies[slot]
|
|
if !e.active do return nil, false
|
|
if len(world.path) == 0 do return nil, false
|
|
if e.path_index >= len(world.path) do return nil, false
|
|
return e, true
|
|
}
|
|
|
|
spawn_projectile :: proc(world: ^World, from: Vec2, target_slot: int, damage: int) {
|
|
p := acquire_projectile(world)
|
|
if p == nil do return
|
|
|
|
p.mode = .Homing
|
|
p^ = Projectile {
|
|
active = true,
|
|
position = from,
|
|
target_slot = target_slot,
|
|
damage = damage,
|
|
speed = PROJECTILE_SPEED,
|
|
splash_radius = 0,
|
|
}
|
|
}
|
|
|
|
spawn_ballistic :: proc(world: ^World, from, dir: Vec2, damage: int, splash: f32) {
|
|
p := acquire_projectile(world)
|
|
if p == nil do return
|
|
p^ = Projectile {
|
|
active = true,
|
|
position = from,
|
|
direction = dir,
|
|
mode = .Ballistic,
|
|
damage = damage,
|
|
speed = CANNON_PROJECTILES_SPEED,
|
|
splash_radius = splash,
|
|
}
|
|
}
|
|
|
|
hit_enemy_at :: proc(world: ^World, pos: Vec2, damage: int) -> bool {
|
|
for i in 0 ..< MAX_ENEMIES {
|
|
e := &world.combat.enemies[i]
|
|
if !e.active do continue
|
|
if distance(pos, e.position) <= PROJECTILE_HIT_RADIUS {
|
|
apply_tower_hit(world, e, damage)
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
update_projectiles :: proc(world: ^World, dt: f32) {
|
|
for i in 0 ..< MAX_PROJECTILES {
|
|
p := &world.combat.projectiles[i]
|
|
if !p.active do continue
|
|
|
|
switch p.mode {
|
|
case .Homing:
|
|
update_homing_projectile(world, p, dt)
|
|
case .Ballistic:
|
|
update_ballistic_projectile(world, p, dt)
|
|
}
|
|
}
|
|
}
|
|
|
|
update_homing_projectile :: proc(world: ^World, p: ^Projectile, dt: f32) {
|
|
target, ok := enemy_still_on_map(world, p.target_slot)
|
|
if !ok {
|
|
p.active = false
|
|
return
|
|
}
|
|
|
|
move_toward(&p.position, target.position, p.speed, dt)
|
|
|
|
if distance(p.position, target.position) <= PROJECTILE_HIT_RADIUS {
|
|
target.health -= p.damage
|
|
p.active = false
|
|
|
|
if target.health <= 0 {
|
|
target.active = false
|
|
push_event(world, .Enemy_Killed, gold_reward = 5)
|
|
}
|
|
}
|
|
}
|
|
|
|
update_ballistic_projectile :: proc(world: ^World, p: ^Projectile, dt: f32) {
|
|
step := p.speed * dt
|
|
p.position.x += p.direction.x * step
|
|
p.position.y += p.direction.y * step
|
|
|
|
if apply_splash_damage(world, p.position, p.damage, p.splash_radius) {
|
|
p.active = false
|
|
return
|
|
}
|
|
|
|
if p.position.x < 0 ||
|
|
p.position.y < 0 ||
|
|
p.position.x > MAP_W * TILE_SIZE ||
|
|
p.position.y > MAP_H * TILE_SIZE {
|
|
p.active = false
|
|
}
|
|
}
|
|
|
|
render_projectiles :: proc(world: ^World) {
|
|
for i in 0 ..< MAX_PROJECTILES {
|
|
p := world.combat.projectiles[i]
|
|
if !p.active do continue
|
|
color := rl.GOLD
|
|
radius: f32 = 4
|
|
if p.mode == .Ballistic {
|
|
color = rl.GRAY
|
|
radius = 6
|
|
}
|
|
rl.DrawCircle(i32(p.position.x), i32(p.position.y), radius, color)
|
|
}
|
|
}
|
|
|
|
apply_splash_damage :: proc(world: ^World, center: Vec2, damage: int, radius: f32) -> bool {
|
|
if radius <= 0 do return hit_enemy_at(world, center, damage)
|
|
|
|
hit_any := false
|
|
for i in 0 ..< MAX_ENEMIES {
|
|
e := &world.combat.enemies[i]
|
|
if !e.active do continue
|
|
if distance(center, e.position) <= radius {
|
|
apply_tower_hit(world, e, damage)
|
|
hit_any = true
|
|
}
|
|
}
|
|
|
|
return hit_any
|
|
}
|
|
|