aboutsummaryrefslogtreecommitdiff
path: root/src/enemy.odin
blob: e806b3738c03920c2625054c328d1bf8834a5478 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package demonchime

import "base:intrinsics"

import "core:math"
import "core:log"
import hm "core:container/handle_map"

import "fw"
import "phys"

Enemy_Ai :: enum(u16) {
  None,
  Corpse,
  Muntik,
}

Enemy_Update_Callback :: #type proc(^Enemy, f32)

update_funcs := [Enemy_Ai]Enemy_Update_Callback{
  .None = noop_ai,
  .Corpse = corpse_ai,
  .Muntik = update_muntik,
}

Enemy_Stats :: struct {
  max_health: i16,
  defense: i16,
}

Enemy :: struct {
  handle: int,

  sprite: Sprite,
  body: phys.Body_Handle,

  stats: ^Enemy_Stats,

  hit_timestamp: f32,

  ai: Enemy_Ai,
  health: i16,

  variant: union {
    ^Muntik,
  },
}

make_enemy :: proc(
  $T: typeid,
  stats: ^Enemy_Stats,
  sprite: Sprite,
  body: phys.Body_Handle
) -> ^T
  where 
    intrinsics.type_has_field(T, "enemy"),
    intrinsics.type_field_type(T, "enemy") == Enemy {
  e := new(T)

  e.enemy.stats = stats
  e.enemy.sprite = sprite
  e.enemy.body = body
  e.enemy.health = stats.max_health
  e.enemy.variant = e

  phys.set_udata(body, &e.enemy)

  e.enemy.handle = len(state.enemy_list)
  append(&state.enemy_list, e)

  return e
}

enemy_take_damage :: proc(e: ^Enemy, damage: i16, kb_dir: Vec2) {
  kb_dir := kb_dir

  e.hit_timestamp = f32(fw.get_time())

  total_damage := math.max(damage - e.stats.defense, 1)
  e.health -= total_damage
  e.health = math.max(e.health, 0)

  if e.health == 0 {
    e.ai = .Corpse
    phys.set_velocity(e.body, kb_dir)
  } else {
    vel := phys.get_velocity(e.body) + kb_dir
    phys.set_velocity(e.body, vel)
  }
}

update_enemies :: proc(dt: f32) {
  for e in state.enemy_list {
    update_funcs[e^.ai](e, dt)

    e.sprite.pos = phys.get_position(e.body)
    update_sprite(&e.sprite, dt)
    phys.update_body(e.body)
  }
}

draw_enemies :: proc() {
  for e in state.enemy_list {
    draw_sprite(e^.sprite)
  }
}

clear_enemies :: proc() {
  for e in state.enemy_list {
    phys.remove_body(e^.body)
    free(e)
  }

  clear(&state.enemy_list)
}

on_room_open_enemies :: proc(room_id: Room_Id) {
  clear_enemies()
}

noop_ai :: proc(e: ^Enemy, _: f32) {}

corpse_ai :: proc(e: ^Enemy, dt: f32) {
  e.sprite.tint = Color{0.5, 0.5, 0.5, 1}

  vel := phys.get_velocity(e.body)
  vel.x = dt_lerp(vel.x, 0, 20)
  vel.y = math.min(vel.y + GRAVITY * dt, TERMINAL_VELOCITY)
  phys.set_velocity(e.body, vel)

  set_sprite_active_tag(&e.sprite, "dead")
}