This commit is contained in:
2026-08-03 21:11:00 +03:00
parent 8f596735e1
commit 06e41fc7da
33 changed files with 2180 additions and 356 deletions
+202
View File
@@ -0,0 +1,202 @@
package main
import hm "core:container/handle_map"
import rl "vendor:raylib"
SQRT2_INV :: 0.7071067811865476
CollisionPair :: struct {
a: Handle,
b: Handle,
}
GameState :: struct {
player: Handle,
camera: rl.Camera3D,
entities: hm.Static_Handle_Map(MAX_ENTITIES, Entity, Handle),
collisions: [dynamic]CollisionPair,
}
get_entity :: proc(state: ^GameState, h: Handle) -> ^Entity {
e, ok := hm.get(&state.entities, h)
return e if ok else nil
}
spawn :: proc(state: ^GameState, e: Entity) -> Handle {
return hm.add(&state.entities, e)
}
entity_rect :: #force_inline proc(b: ^BaseEntity) -> rl.Rectangle {
return rl.Rectangle{b.pos.x - b.col_size / 2, b.pos.y - b.col_size / 2, b.col_size, b.col_size}
}
check_col :: proc(a, b: ^BaseEntity) -> (overlap: rl.Rectangle, colliding: bool) {
c := rl.GetCollisionRec(entity_rect(a), entity_rect(b))
if c.width <= 0 || c.height <= 0 do return rl.Rectangle{}, false
return c, true
}
resolve_aabb :: proc(a, b: ^BaseEntity) -> bool {
c, ok := check_col(a, b)
if !ok do return false
if c.width < c.height {
sign: f32 = 1 if a.pos.x < b.pos.x else -1
a.pos.x -= sign * c.width * 0.5
b.pos.x += sign * c.width * 0.5
} else {
sign: f32 = 1 if a.pos.y < b.pos.y else -1
a.pos.y -= sign * c.height * 0.5
b.pos.y += sign * c.height * 0.5
}
return true
}
sys_update :: proc(state: ^GameState, dt: f32) {
it := hm.iterator_make(&state.entities)
for e, h in hm.iterate(&it) {
#partial switch &v in e.v {
case Player:
player_update(state, &v, h, dt)
case Enemy:
enemy_update(state, &v, h, dt)
case GroundItem:
item_update(state, &v, h, dt)
}
}
it2 := hm.iterator_make(&state.entities)
for e, _ in hm.iterate(&it2) {
b := get_base(e)
b.pos += b.vel * dt
}
it3 := hm.iterator_make(&state.entities)
for e, h in hm.iterate(&it3) {
#partial switch &v in e.v {
case FX:
fx_update(state, &v, h, dt)
case Attack:
attack_update(state, &v, h, dt)
}
}
resolve_collisions(state)
apply_collision_effects(state)
}
sys_sweep :: proc(state: ^GameState) {
it := hm.iterator_make(&state.entities)
for e, h in hm.iterate(&it) {
if get_base(e).marked {
hm.remove(&state.entities, h)
}
}
}
entity_draw :: proc(state: ^GameState, e: ^Entity) {
b := get_base(e)
sp := rl.GetWorldToScreen({b.pos.x, 0, b.pos.y}, state.camera)
scale := SCREEN_H / state.camera.fovy
#partial switch v in b.gfx[0] {
case GfxCircle:
rl.DrawCircleV(sp, v.radius * scale, v.color)
case:
}
}
ground_effect_draw :: proc(state: ^GameState, e: ^Entity) {
b := get_base(e)
#partial switch v in b.gfx[0] {
case GfxRing:
rl.DrawCylinder({b.pos.x, 0.02, b.pos.y}, v.outer_radius, v.outer_radius, 0.04, 48, v.color)
case:
}
}
sys_draw :: proc(state: ^GameState) {
rl.BeginDrawing()
rl.ClearBackground({20, 20, 30, 255})
rl.BeginMode3D(state.camera)
draw_grid_3d()
it := hm.iterator_make(&state.entities)
for e, _ in hm.iterate(&it) {
ground_effect_draw(state, e)
}
rl.EndMode3D()
scale := SCREEN_H / state.camera.fovy
it2 := hm.iterator_make(&state.entities)
for e, _ in hm.iterate(&it2) {
entity_draw(state, e)
if a := get_actor(e); a != nil {
b := get_base(e)
sp := rl.GetWorldToScreen({b.pos.x, 0, b.pos.y}, state.camera)
hp_ratio := f32(a.hp) / f32(a.max_hp)
bar_w := b.col_size * 3 * scale
bar_pos := sp - rl.Vector2{bar_w / 2, b.col_size * 2 * scale + 12}
rl.DrawRectangleV(bar_pos, {bar_w, 5}, {60, 60, 60, 255})
rl.DrawRectangleV(bar_pos, {bar_w * hp_ratio, 5}, {220, 40, 40, 255})
}
}
ui_draw(state)
rl.EndDrawing()
}
resolve_collisions :: proc(state: ^GameState) {
clear(&state.collisions)
hs: [MAX_ENTITIES]Handle
n := 0
it := hm.iterator_make(&state.entities)
for _, h in hm.iterate(&it) {
hs[n] = h
n += 1
}
for i in 0 ..< n {
for j in i + 1 ..< n {
a := get_entity(state, hs[i])
b := get_entity(state, hs[j])
if resolve_aabb(get_base(a), get_base(b)) {
append(&state.collisions, CollisionPair{a = hs[i], b = hs[j]})
}
}
}
}
apply_collision_effects :: proc(state: ^GameState) {
_, p := get_player(state)
for c in state.collisions {
other_h: Handle
if c.a == state.player {
other_h = c.b
} else if c.b == state.player {
other_h = c.a
} else do continue
e := get_entity(state, other_h)
if e == nil do continue
#partial switch &v in e.v {
case Enemy:
if p != nil && p.invuln_timer <= 0 {
p.hp -= v.touch_dmg
p.invuln_timer = 0.5
if p.hp <= 0 do p.hp = 0
}
case GroundItem:
if p != nil && !v.marked {
p.hp = min(p.hp + v.heal, p.max_hp)
v.marked = true
}
}
}
}