3d
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import hm "core:container/handle_map"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
MAX_ATTACK_HITS :: 32
|
||||
|
||||
Attack :: struct {
|
||||
using base: BaseEntity,
|
||||
parent: Handle,
|
||||
dmg: i32,
|
||||
radius: f32,
|
||||
lifetime: f32,
|
||||
hit: [MAX_ATTACK_HITS]Handle,
|
||||
hit_count: u8,
|
||||
}
|
||||
|
||||
init_attack :: proc(state: ^GameState, a: Attack) -> Handle {
|
||||
return spawn(state, Entity{v = a})
|
||||
}
|
||||
|
||||
attack_update :: proc(state: ^GameState, a: ^Attack, h: Handle, dt: f32) {
|
||||
if a.parent != {} {
|
||||
if pe := get_entity(state, a.parent); pe != nil {
|
||||
a.pos = get_base(pe).pos
|
||||
} else {
|
||||
a.marked = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
it := hm.iterator_make(&state.entities)
|
||||
for e, eh in hm.iterate(&it) {
|
||||
#partial switch &v in e.v {
|
||||
case Enemy:
|
||||
if v.hp <= 0 || v.marked do continue
|
||||
if rl.Vector2Distance(a.pos, v.pos) <= a.radius && !attack_has_hit(a, eh) {
|
||||
v.hp -= a.dmg
|
||||
attack_add_hit(a, eh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if a.lifetime > 0 {
|
||||
a.lifetime -= dt
|
||||
} else {
|
||||
a.marked = true
|
||||
}
|
||||
}
|
||||
|
||||
attack_has_hit :: proc(a: ^Attack, h: Handle) -> bool {
|
||||
for i in 0 ..< a.hit_count {
|
||||
if a.hit[i] == h do return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
attack_add_hit :: proc(a: ^Attack, h: Handle) {
|
||||
if a.hit_count >= MAX_ATTACK_HITS do return
|
||||
a.hit[a.hit_count] = h
|
||||
a.hit_count += 1
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
import rl "vendor:raylib"
|
||||
|
||||
Enemy :: struct {
|
||||
using actor: Actor,
|
||||
touch_dmg: i32,
|
||||
}
|
||||
|
||||
make_enemy :: proc(pos: rl.Vector2) -> Entity {
|
||||
return Entity {
|
||||
v = Enemy {
|
||||
actor = Actor {
|
||||
base = BaseEntity {
|
||||
pos = pos,
|
||||
col_size = 16,
|
||||
gfx = {0 = GfxCircle{pos = {}, radius = 11, color = {220, 70, 70, 255}}},
|
||||
},
|
||||
hp = 40,
|
||||
max_hp = 40,
|
||||
speed = 55,
|
||||
},
|
||||
touch_dmg = 10,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
enemy_update :: proc(state: ^GameState, e: ^Enemy, h: Handle, dt: f32) {
|
||||
pe := get_entity(state, state.player)
|
||||
if pe != nil {
|
||||
dir := get_base(pe).pos - e.pos
|
||||
e.vel = rl.Vector2Normalize(dir) * e.speed
|
||||
}
|
||||
|
||||
if e.hp <= 0 && !e.marked {
|
||||
e.marked = true
|
||||
spawn(state, make_ground_item(e.pos))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package main
|
||||
|
||||
import hm "core:container/handle_map"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
MAX_GFX :: 2
|
||||
|
||||
Handle :: hm.Handle32
|
||||
|
||||
MAX_ENTITIES :: 1024
|
||||
|
||||
BaseEntity :: struct {
|
||||
pos: rl.Vector2,
|
||||
vel: rl.Vector2,
|
||||
col_size: f32,
|
||||
marked: bool,
|
||||
gfx: [MAX_GFX]Graphics,
|
||||
}
|
||||
|
||||
Actor :: struct {
|
||||
using base: BaseEntity,
|
||||
hp: i32,
|
||||
max_hp: i32,
|
||||
speed: f32,
|
||||
}
|
||||
|
||||
EntityVariant :: union {
|
||||
Player,
|
||||
Enemy,
|
||||
GroundItem,
|
||||
FX,
|
||||
Attack,
|
||||
}
|
||||
|
||||
Entity :: struct {
|
||||
handle: Handle,
|
||||
v: EntityVariant,
|
||||
}
|
||||
|
||||
get_base :: proc(e: ^Entity) -> ^BaseEntity {
|
||||
#partial switch &v in e.v {
|
||||
case Player:
|
||||
return cast(^BaseEntity)&v
|
||||
case Enemy:
|
||||
return cast(^BaseEntity)&v
|
||||
case GroundItem:
|
||||
return cast(^BaseEntity)&v
|
||||
case FX:
|
||||
return cast(^BaseEntity)&v
|
||||
case Attack:
|
||||
return cast(^BaseEntity)&v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
get_actor :: proc(e: ^Entity) -> ^Actor {
|
||||
#partial switch &v in e.v {
|
||||
case Player:
|
||||
return &v.actor
|
||||
case Enemy:
|
||||
return &v.actor
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
get_player :: proc(state: ^GameState) -> (^Entity, ^Player) {
|
||||
e := get_entity(state, state.player)
|
||||
if e == nil do return nil, nil
|
||||
p, ok := &e.v.(Player)
|
||||
if !ok do return nil, nil
|
||||
return e, p
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
|
||||
FX :: struct {
|
||||
using base: BaseEntity,
|
||||
parent: Handle,
|
||||
lifetime: f32,
|
||||
permanent: bool,
|
||||
}
|
||||
|
||||
init_fx :: proc(state: ^GameState, fx: FX) -> Handle {
|
||||
return spawn(state, Entity{v = fx})
|
||||
}
|
||||
|
||||
fx_update :: proc(state: ^GameState, fx: ^FX, h: Handle, dt: f32) {
|
||||
if fx.parent != {} {
|
||||
if pe := get_entity(state, fx.parent); pe != nil {
|
||||
fx.pos = get_base(pe).pos
|
||||
} else {
|
||||
fx.marked = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !fx.permanent {
|
||||
if fx.lifetime > 0 {
|
||||
fx.lifetime -= dt
|
||||
} else {
|
||||
fx.marked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import rl "vendor:raylib"
|
||||
|
||||
Graphics :: union {
|
||||
GfxNone,
|
||||
GfxCircle,
|
||||
GfxRect,
|
||||
GfxRing,
|
||||
GfxCircleLines,
|
||||
GfxTexture,
|
||||
}
|
||||
|
||||
GfxNone :: struct {}
|
||||
|
||||
GfxCircle :: struct {
|
||||
pos: rl.Vector2,
|
||||
radius: f32,
|
||||
color: rl.Color,
|
||||
}
|
||||
|
||||
GfxRect :: struct {
|
||||
pos: rl.Vector2,
|
||||
size: rl.Vector2,
|
||||
color: rl.Color,
|
||||
}
|
||||
|
||||
GfxRing :: struct {
|
||||
pos: rl.Vector2,
|
||||
inner_radius: f32,
|
||||
outer_radius: f32,
|
||||
color: rl.Color,
|
||||
}
|
||||
|
||||
GfxCircleLines :: struct {
|
||||
pos: rl.Vector2,
|
||||
radius: f32,
|
||||
color: rl.Color,
|
||||
}
|
||||
|
||||
GfxTexture :: struct {
|
||||
}
|
||||
|
||||
gfx_draw :: proc(pos: rl.Vector2, g: Graphics) {
|
||||
#partial switch v in g {
|
||||
case GfxCircle:
|
||||
rl.DrawCircleV(pos + v.pos, v.radius, v.color)
|
||||
case GfxRect:
|
||||
rl.DrawRectangleV(pos - v.size / 2 + v.pos, v.size, v.color)
|
||||
case GfxRing:
|
||||
rl.DrawRing(pos + v.pos, v.inner_radius, v.outer_radius, 0, 360, 0, v.color)
|
||||
case GfxCircleLines:
|
||||
rl.DrawCircleLinesV(pos + v.pos, v.radius, v.color)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
import rl "vendor:raylib"
|
||||
|
||||
GroundItem :: struct {
|
||||
using base: BaseEntity,
|
||||
heal: i32,
|
||||
}
|
||||
|
||||
make_ground_item :: proc(pos: rl.Vector2) -> Entity {
|
||||
return Entity{
|
||||
v = GroundItem{
|
||||
base = BaseEntity{
|
||||
pos = pos, col_size = 10,
|
||||
gfx = {0 = GfxCircle{pos = {}, radius = 6, color = {255, 220, 50, 255}}},
|
||||
},
|
||||
heal = 25,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
item_update :: proc(state: ^GameState, i: ^GroundItem, h: Handle, dt: f32) {
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import rl "vendor:raylib"
|
||||
|
||||
SCREEN_W :: 1280
|
||||
SCREEN_H :: 720
|
||||
TARGET_FPS :: 60
|
||||
|
||||
|
||||
main :: proc() {
|
||||
rl.InitWindow(SCREEN_W, SCREEN_H, "Iso Arena 3D")
|
||||
rl.SetTargetFPS(TARGET_FPS)
|
||||
defer rl.CloseWindow()
|
||||
|
||||
camera := rl.Camera3D {
|
||||
position = {0, 80, 80},
|
||||
target = {0, 0, 0},
|
||||
up = {0, 1, 0},
|
||||
fovy = 400,
|
||||
projection = .ORTHOGRAPHIC,
|
||||
}
|
||||
|
||||
state := GameState {
|
||||
camera = camera,
|
||||
}
|
||||
state.collisions = make([dynamic]CollisionPair)
|
||||
defer delete(state.collisions)
|
||||
|
||||
state.player = spawn(&state, make_player())
|
||||
spawn(&state, make_enemy({200, 200}))
|
||||
spawn(&state, make_enemy({-200, 200}))
|
||||
spawn(&state, make_enemy({200, -200}))
|
||||
|
||||
for !rl.WindowShouldClose() {
|
||||
dt := rl.GetFrameTime()
|
||||
|
||||
sys_input(&state, dt)
|
||||
sys_update(&state, dt)
|
||||
sys_sweep(&state)
|
||||
|
||||
if pe := get_entity(&state, state.player); pe != nil {
|
||||
p := get_base(pe).pos
|
||||
state.camera.target = {p.x, 0, p.y}
|
||||
state.camera.position = state.camera.target + {600, 600, 600}
|
||||
}
|
||||
sys_draw(&state)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import rl "vendor:raylib"
|
||||
|
||||
TILE_W :: 64
|
||||
MAP_SIZE :: 20
|
||||
|
||||
depth_key :: #force_inline proc(pos: rl.Vector2) -> f32 {
|
||||
return pos.x + pos.y
|
||||
}
|
||||
|
||||
draw_grid_3d :: proc() {
|
||||
middle := MAP_SIZE / 2
|
||||
for x in 0 ..< MAP_SIZE {
|
||||
for y in 0 ..< MAP_SIZE {
|
||||
wx := f32(x - middle) * TILE_W
|
||||
wy := f32(y - middle) * TILE_W
|
||||
color := ((x + y) % 2 == 0) ? rl.Color{64, 64, 76, 255} : rl.Color{51, 51, 63, 255}
|
||||
rl.DrawPlane({wx, 0, wy}, {TILE_W, TILE_W}, color)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import hm "core:container/handle_map"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
Player :: struct {
|
||||
using actor: Actor,
|
||||
attack_range: f32,
|
||||
attack_dmg: i32,
|
||||
attack_cd: f32,
|
||||
attack_timer: f32,
|
||||
invuln_timer: f32,
|
||||
}
|
||||
|
||||
PlayerInput :: struct {
|
||||
move: rl.Vector2,
|
||||
attack: bool,
|
||||
}
|
||||
|
||||
make_player :: proc() -> Entity {
|
||||
return Entity {
|
||||
v = Player {
|
||||
actor = Actor {
|
||||
base = BaseEntity {
|
||||
pos = {0, 0},
|
||||
col_size = 16,
|
||||
gfx = {0 = GfxCircle{pos = {}, radius = 14, color = {80, 220, 100, 255}}},
|
||||
},
|
||||
hp = 100,
|
||||
max_hp = 100,
|
||||
speed = 220,
|
||||
},
|
||||
attack_range = 75,
|
||||
attack_dmg = 35,
|
||||
attack_cd = 0.3,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
sys_input :: proc(state: ^GameState, dt: f32) {
|
||||
input: PlayerInput
|
||||
|
||||
if rl.IsKeyDown(.W) || rl.IsKeyDown(.UP) do input.move += {-1, -1}
|
||||
if rl.IsKeyDown(.S) || rl.IsKeyDown(.DOWN) do input.move += {1, 1}
|
||||
if rl.IsKeyDown(.A) || rl.IsKeyDown(.LEFT) do input.move += {-1, 1}
|
||||
if rl.IsKeyDown(.D) || rl.IsKeyDown(.RIGHT) do input.move += {1, -1}
|
||||
input.attack = rl.IsKeyPressed(.SPACE)
|
||||
|
||||
player_input(state, input, dt)
|
||||
}
|
||||
|
||||
player_input :: proc(state: ^GameState, input: PlayerInput, dt: f32) {
|
||||
_, p := get_player(state)
|
||||
if p == nil do return
|
||||
|
||||
p.vel = rl.Vector2Normalize(input.move) * p.speed
|
||||
|
||||
if p.attack_timer > 0 do p.attack_timer -= dt
|
||||
if input.attack && p.attack_timer <= 0 {
|
||||
p.attack_timer = p.attack_cd
|
||||
|
||||
init_attack(
|
||||
state,
|
||||
Attack {
|
||||
base = BaseEntity {
|
||||
gfx = {
|
||||
0 = GfxRing {
|
||||
inner_radius = p.attack_range * 0.7,
|
||||
outer_radius = p.attack_range,
|
||||
color = {255, 255, 255, 40},
|
||||
},
|
||||
},
|
||||
},
|
||||
parent = state.player,
|
||||
dmg = p.attack_dmg,
|
||||
radius = p.attack_range,
|
||||
lifetime = p.attack_cd,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
player_update :: proc(state: ^GameState, p: ^Player, h: Handle, dt: f32) {
|
||||
if p.invuln_timer > 0 do p.invuln_timer -= dt
|
||||
}
|
||||
+202
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
import hm "core:container/handle_map"
|
||||
|
||||
import rl "vendor:raylib"
|
||||
|
||||
ui_draw_world_hp_bar :: proc(b: ^BaseEntity, actor: ^Actor) {
|
||||
hp_ratio := f32(actor.hp) / f32(actor.max_hp)
|
||||
bar_w := b.col_size * 3
|
||||
bar_pos := b.pos + rl.Vector2{-bar_w / 2, -b.col_size - 10}
|
||||
rl.DrawRectangleV(bar_pos, {bar_w, 4}, {60, 60, 60, 255})
|
||||
rl.DrawRectangleV(bar_pos, {bar_w * hp_ratio, 4}, {220, 40, 40, 255})
|
||||
}
|
||||
|
||||
ui_draw :: proc(state: ^GameState) {
|
||||
_, p := get_player(state)
|
||||
hp: i32 = 0
|
||||
max_hp: i32 = 1
|
||||
if p != nil {
|
||||
hp = p.hp
|
||||
max_hp = p.max_hp
|
||||
}
|
||||
|
||||
rl.DrawText(rl.TextFormat("HP: %d/%d", hp, max_hp), 12, 12, 20, rl.RAYWHITE)
|
||||
rl.DrawText(rl.TextFormat("Entities: %d", hm.len(state.entities)), 12, 36, 16, {180, 180, 180, 255})
|
||||
rl.DrawText("[SPACE] attack [WASD] move", 12, SCREEN_H - 28, 14, {120, 120, 120, 255})
|
||||
}
|
||||
Reference in New Issue
Block a user