3d
This commit is contained in:
+255
@@ -0,0 +1,255 @@
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
import math "core:math"
|
||||
import hm "core:container/handle_map"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
SQRT2_INV :: 0.7071067811865476
|
||||
|
||||
LOG_SIZE :: 8
|
||||
|
||||
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,
|
||||
log: [LOG_SIZE]string,
|
||||
log_count: i32,
|
||||
kills: i32,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
add_log :: proc(state: ^GameState, format: string, args: ..any) {
|
||||
msg := fmt.tprintf(format, ..args)
|
||||
if state.log_count >= LOG_SIZE {
|
||||
for i in 0 ..< LOG_SIZE - 1 {
|
||||
state.log[i] = state.log[i + 1]
|
||||
}
|
||||
state.log[LOG_SIZE - 1] = msg
|
||||
} else {
|
||||
state.log[state.log_count] = msg
|
||||
state.log_count += 1
|
||||
}
|
||||
}
|
||||
|
||||
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 e.v {
|
||||
case Attack:
|
||||
draw_arc_sector(b.pos, v.dir, v.radius, v.arc, v.color)
|
||||
case GroundItem:
|
||||
rl.DrawCylinder({b.pos.x, 0.02, b.pos.y}, 8, 8, 0.03, 24, {255, 255, 255, 30})
|
||||
case:
|
||||
}
|
||||
}
|
||||
|
||||
draw_arc_sector :: proc(pos: rl.Vector2, dir: rl.Vector2, radius: f32, arc: f32, color: rl.Color) {
|
||||
SEGMENTS :: 24
|
||||
base := math.atan2_f32(dir.y, dir.x)
|
||||
start := base - arc * 0.5
|
||||
|
||||
for i in 0 ..< SEGMENTS {
|
||||
a1 := start + arc * f32(i) / f32(SEGMENTS)
|
||||
a2 := start + arc * f32(i + 1) / f32(SEGMENTS)
|
||||
p1 := pos + rl.Vector2{math.cos_f32(a1), math.sin_f32(a1)} * radius
|
||||
p2 := pos + rl.Vector2{math.cos_f32(a2), math.sin_f32(a2)} * radius
|
||||
rl.DrawTriangle3D(
|
||||
{pos.x, 0.03, pos.y},
|
||||
{p1.x, 0.03, p1.y},
|
||||
{p2.x, 0.03, p2.y},
|
||||
color,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sys_draw :: proc(state: ^GameState) {
|
||||
rl.BeginDrawing()
|
||||
rl.ClearBackground({20, 20, 30, 255})
|
||||
|
||||
rl.BeginMode3D(state.camera)
|
||||
draw_grid_3d()
|
||||
|
||||
if ray := rl.GetScreenToWorldRay(rl.GetMousePosition(), state.camera); ray.direction.y != 0 {
|
||||
t := -ray.position.y / ray.direction.y
|
||||
hit := ray.position + ray.direction * t
|
||||
rl.DrawCylinder({hit.x, 0.02, hit.z}, 4, 4, 0.02, 16, {255, 255, 255, 90})
|
||||
}
|
||||
|
||||
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 {
|
||||
dmg := v.touch_dmg
|
||||
armor: i32 = 0
|
||||
if p.equip.armor.kind != .None do armor = p.equip.armor.armor
|
||||
dmg = max(dmg - armor / 2, 1)
|
||||
p.hp -= dmg
|
||||
p.invuln_timer = 0.5
|
||||
if p.hp <= 0 do p.hp = 0
|
||||
}
|
||||
case GroundItem:
|
||||
if p != nil && !v.marked && p.inv_count < INV_SIZE {
|
||||
p.inventory[p.inv_count] = v.item
|
||||
p.inv_count += 1
|
||||
v.marked = true
|
||||
add_log(state, "Picked up: %s", v.item.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user