Init
This commit is contained in:
@@ -0,0 +1 @@
|
||||
src.bin
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/ols.schema.json",
|
||||
"enable_semantic_tokens": true,
|
||||
"enable_document_symbols": true,
|
||||
"enable_hover": true,
|
||||
"enable_snippets": true,
|
||||
"enable_checker_only_saved": true,
|
||||
"enable_inlay_hints_params": true,
|
||||
"enable_inlay_hints_default_params": true,
|
||||
"enable_inlay_hints_implicit_return": true,
|
||||
"enable_inlay_hints_optional_result": true,
|
||||
"enable_inlay_hints_struct_fields": true
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import hm "core:container/handle_map"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
Enemy :: struct {
|
||||
using actor: Actor,
|
||||
touch_dmg: i32,
|
||||
}
|
||||
|
||||
enemy_update :: proc(state: ^GameState, p, e: ^Entity) {
|
||||
p := hm.get(&state.entities, state.player)
|
||||
}
|
||||
|
||||
// sys_enemy_ai :: proc(state: ^GameState) {
|
||||
// for &enemy in &state.enemies {
|
||||
// dir := state.player.pos - enemy.pos
|
||||
// set_vel(cast(^Entity2)&enemy, dir, enemy.speed)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// sys_death :: proc(state: ^GameState3) {
|
||||
// for &enemy in &state.enemies {
|
||||
// if enemy.hp > 0 || enemy.marked do continue
|
||||
// enemy.marked = true
|
||||
// init_entity(
|
||||
// state,
|
||||
// GroundItem3 {
|
||||
// ent = Entity2 {
|
||||
// GroundItem3 = enemy.pos,
|
||||
// GroundItem3 = {GroundItem3 = GfxCircle{GroundItem3 = {0, 0}, GroundItem3 = 6, GroundItem3 = {255, 220, 50, 255}}},
|
||||
// GroundItem3 = 10,
|
||||
// },
|
||||
// heal = 25,
|
||||
// },
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,87 @@
|
||||
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 {
|
||||
// TODO: Implement texure rendering
|
||||
}
|
||||
|
||||
FX :: struct {
|
||||
parentId: EntityID,
|
||||
lifetime: f32,
|
||||
permanent: bool,
|
||||
pos: rl.Vector2,
|
||||
gfx: [MAX_GFX]Graphics,
|
||||
marked: bool,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
fx_update :: proc(state: ^GameState3, fx: ^FX, dt: f32) {
|
||||
if fx.marked do return
|
||||
if fx.parentId != 0 {
|
||||
parent, ok := get_ent_by_id(state, fx.parentId)
|
||||
if ok do fx.pos = parent.pos
|
||||
}
|
||||
if !fx.permanent {
|
||||
if fx.lifetime > 0 {fx.lifetime -= dt} else {fx.marked = true}
|
||||
}
|
||||
}
|
||||
|
||||
fx_draw :: proc(fx: ^FX) {
|
||||
for g in fx.gfx {
|
||||
gfx_draw(fx.pos, g)
|
||||
}
|
||||
}
|
||||
|
||||
init_fx :: proc(state: ^GameState3, fx: FX) {
|
||||
append(&state.fxs, fx)
|
||||
}
|
||||
|
||||
//TODO: Создание и удаление FX с лайфтаймами
|
||||
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
GroundItem3 :: struct {
|
||||
using ent: Entity2,
|
||||
heal: i32,
|
||||
}
|
||||
|
||||
sys_pickup :: proc(state: ^GameState3) {
|
||||
for &item in &state.items {
|
||||
if item.marked do continue
|
||||
_, ok := check_col(cast(^Entity2)&item, cast(^Entity2)&state.player)
|
||||
if ok {
|
||||
state.player.hp = min(state.player.hp + item.heal, state.player.max_hp)
|
||||
item.marked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
import hm "core:container/handle_map"
|
||||
import "core:fmt"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
main :: proc() {
|
||||
rl.InitWindow(SCREEN_W, SCREEN_H, "Iso Arena")
|
||||
rl.SetTargetFPS(TARGET_FPS)
|
||||
defer rl.CloseWindow()
|
||||
|
||||
state := GameState {
|
||||
camera = {zoom = 1.5, offset = {SCREEN_W / 2, SCREEN_H / 2}},
|
||||
}
|
||||
|
||||
p := hm.add(
|
||||
&state.entities,
|
||||
Entity {
|
||||
pos = {400, 300},
|
||||
col_size = 16,
|
||||
variant = Player {
|
||||
actor = Actor {
|
||||
hp = 100,
|
||||
max_hp = 100,
|
||||
speed = 220,
|
||||
gfx = {0 = GfxCircle{pos = {0, 0}, radius = 14, color = {80, 220, 100, 255}}},
|
||||
},
|
||||
attack_range = 75,
|
||||
attack_dmg = 35,
|
||||
attack_cd = 0.3,
|
||||
invuln_timer = 0,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
state.player = p
|
||||
|
||||
//TODO: Коллизия
|
||||
|
||||
// state.collisions = make([dynamic]CollisionEntry)
|
||||
// defer delete(state.collisions)
|
||||
|
||||
hm.add(
|
||||
&state.entities,
|
||||
Entity {
|
||||
pos = {600, 500},
|
||||
col_size = 16,
|
||||
variant = Enemy {
|
||||
actor = Actor {
|
||||
hp = 40,
|
||||
max_hp = 40,
|
||||
speed = 55,
|
||||
gfx = {0 = GfxCircle{pos = {0, 0}, radius = 11, color = {220, 70, 70, 255}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
hm.add(
|
||||
&state.entities,
|
||||
Entity {
|
||||
pos = {200, 500},
|
||||
col_size = 16,
|
||||
variant = Enemy {
|
||||
actor = Actor {
|
||||
hp = 40,
|
||||
max_hp = 40,
|
||||
speed = 55,
|
||||
gfx = {0 = GfxCircle{pos = {0, 0}, radius = 11, color = {220, 70, 70, 255}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
hm.add(
|
||||
&state.entities,
|
||||
Entity {
|
||||
pos = {600, 500},
|
||||
col_size = 16,
|
||||
variant = Enemy {
|
||||
actor = Actor {
|
||||
hp = 40,
|
||||
max_hp = 40,
|
||||
speed = 55,
|
||||
gfx = {0 = GfxCircle{pos = {0, 0}, radius = 11, color = {220, 70, 70, 255}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
for !rl.WindowShouldClose() {
|
||||
dt := rl.GetFrameTime()
|
||||
|
||||
sys_input(&state)
|
||||
sys_enemy_ai(&state)
|
||||
sys_move(&state, dt)
|
||||
sys_collisions(&state)
|
||||
sys_damage(&state)
|
||||
sys_death(&state)
|
||||
sys_pickup(&state)
|
||||
sys_update(&state, dt)
|
||||
sys_sweep(&state)
|
||||
|
||||
state.camera.target = state.player.pos
|
||||
sys_render(&state)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import rl "vendor:raylib"
|
||||
|
||||
Player2 :: struct {
|
||||
using actor: Actor,
|
||||
attack_range: f32,
|
||||
attack_dmg: i32,
|
||||
attack_cd: f32,
|
||||
attack_timer: f32,
|
||||
invuln_timer: f32,
|
||||
}
|
||||
|
||||
sys_input :: proc(state: ^GameState3) {
|
||||
p := &state.player
|
||||
|
||||
dir: rl.Vector2
|
||||
if rl.IsKeyDown(.W) || rl.IsKeyDown(.UP) do dir.y -= 1
|
||||
if rl.IsKeyDown(.S) || rl.IsKeyDown(.DOWN) do dir.y += 1
|
||||
if rl.IsKeyDown(.A) || rl.IsKeyDown(.LEFT) do dir.x -= 1
|
||||
if rl.IsKeyDown(.D) || rl.IsKeyDown(.RIGHT) do dir.x += 1
|
||||
set_vel(cast(^Entity2)p, dir, p.speed)
|
||||
|
||||
if p.attack_timer > 0 do p.attack_timer -= rl.GetFrameTime()
|
||||
if rl.IsKeyPressed(.SPACE) && p.attack_timer <= 0 {
|
||||
p.attack_timer = p.attack_cd
|
||||
init_fx(
|
||||
state,
|
||||
FX {
|
||||
parentId = p.id,
|
||||
lifetime = p.attack_cd,
|
||||
permanent = false,
|
||||
gfx = {
|
||||
0 = GfxRing {
|
||||
inner_radius = p.attack_range * 0.7,
|
||||
outer_radius = p.attack_range,
|
||||
color = {255, 255, 255, 40},
|
||||
},
|
||||
1 = GfxCircleLines{radius = p.attack_range, color = {255, 255, 255, 80}},
|
||||
},
|
||||
},
|
||||
)
|
||||
for &enemy in &state.enemies {
|
||||
if dist(Entity2(p), Entity2(enemy)) <= p.attack_range {
|
||||
enemy.hp -= p.attack_dmg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
package main
|
||||
|
||||
import hm "core:container/handle_map"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
SCREEN_W :: 1280
|
||||
SCREEN_H :: 720
|
||||
TARGET_FPS :: 60
|
||||
SQRT2_INV :: 0.7071067811865476
|
||||
|
||||
// ─── Базовые типы ───
|
||||
|
||||
MAX_GFX :: 2
|
||||
MAX_ENTITIES :: 1024
|
||||
|
||||
Handle :: hm.Handle32
|
||||
|
||||
// ─── Коллизии ───
|
||||
|
||||
// EntityKind :: enum {
|
||||
// Player,
|
||||
// Enemy,
|
||||
// }
|
||||
|
||||
// CollisionEntry :: struct {
|
||||
// a_kind: EntityKind,
|
||||
// a_idx: int,
|
||||
// b_kind: EntityKind,
|
||||
// b_idx: int,
|
||||
// }
|
||||
|
||||
// ─── Игровое состояние ───
|
||||
|
||||
|
||||
Entity :: struct {
|
||||
handle: Handle,
|
||||
pos: rl.Vector2,
|
||||
col_size: f32,
|
||||
marked: bool,
|
||||
v: EntityVariant,
|
||||
}
|
||||
|
||||
Actor :: struct {
|
||||
vel: rl.Vector2,
|
||||
hp: i32,
|
||||
max_hp: i32,
|
||||
speed: f32,
|
||||
gfx: [MAX_GFX]Graphics,
|
||||
}
|
||||
|
||||
Player :: struct {
|
||||
using actor: Actor,
|
||||
attack_range: f32,
|
||||
attack_dmg: i32,
|
||||
attack_cd: f32,
|
||||
attack_timer: f32,
|
||||
invuln_timer: f32,
|
||||
}
|
||||
|
||||
|
||||
GroundItem :: struct {
|
||||
heal: i32,
|
||||
gfx: [MAX_GFX]Graphics,
|
||||
}
|
||||
|
||||
EntityVariant :: union {
|
||||
Player,
|
||||
Enemy,
|
||||
GroundItem,
|
||||
}
|
||||
|
||||
GameState :: struct {
|
||||
player: Handle,
|
||||
camera: rl.Camera2D,
|
||||
entities: hm.Static_Handle_Map(MAX_ENTITIES, Entity, Handle),
|
||||
// particles: hm.Static_Handle_Map(MAX_PARTICLE_FX, Fx2, Handle),
|
||||
}
|
||||
|
||||
|
||||
// ─── Хелперы ───
|
||||
|
||||
set_vel :: #force_inline proc(e: ^Entity, dir: rl.Vector2, speed: f32) {
|
||||
if rl.Vector2Length(dir) > 0 {
|
||||
e.v.vel = rl.Vector2Normalize(dir) * speed
|
||||
} else {
|
||||
e.v.vel = {}
|
||||
}
|
||||
}
|
||||
|
||||
move_ent :: #force_inline proc(e: ^Entity, dt: f32) {
|
||||
e.v.pos += e.v.vel * dt
|
||||
}
|
||||
|
||||
dist :: #force_inline proc(a, b: Entity) -> f32 {
|
||||
return rl.Vector2Distance(a.v.pos, b.v.pos)
|
||||
}
|
||||
|
||||
// entity_rect :: #force_inline proc(e: ^Entity2) -> rl.Rectangle {
|
||||
// return rl.Rectangle{e.pos.x - e.col_size / 2, e.pos.y - e.col_size / 2, e.col_size, e.col_size}
|
||||
// }
|
||||
//
|
||||
// check_col :: proc(a, b: ^Entity2) -> (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: ^Entity2) -> 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_move :: proc(state: ^GameState, dt: f32) {
|
||||
it := hm.iterator_make(&state.entities)
|
||||
for e, _ in hm.iterate(&it) {
|
||||
#partial switch &v in e.v {
|
||||
case Player:
|
||||
e.pos += v.vel * dt
|
||||
case Enemy:
|
||||
e.pos += v.vel * dt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sys_collisions :: proc(state: ^GameState3) {
|
||||
// clear(&state.collisions)
|
||||
//
|
||||
// p_ent := cast(^Entity2)&state.player
|
||||
//
|
||||
// for &enemy, i in &state.enemies {
|
||||
// if resolve_aabb(p_ent, cast(^Entity2)&enemy) {
|
||||
// append(
|
||||
// &state.collisions,
|
||||
// CollisionEntry{a_kind = .Player, a_idx = 0, b_kind = .Enemy, b_idx = i},
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for i in 0 ..< len(state.enemies) {
|
||||
// for j in i + 1 ..< len(state.enemies) {
|
||||
// resolve_aabb(cast(^Entity2)&state.enemies[i], cast(^Entity2)&state.enemies[j])
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
sys_damage :: proc(state: ^GameState) {
|
||||
p := hm.get(&state.entities, state.player)
|
||||
if p.invuln_timer > 0 {
|
||||
p.invuln_timer -= rl.GetFrameTime()
|
||||
return
|
||||
}
|
||||
|
||||
// for c in state.collisions {
|
||||
// if c.a_kind == .Player && c.b_kind == .Enemy {
|
||||
// state.player.hp -= state.enemies[c.b_idx].touch_dmg
|
||||
// state.player.invuln_timer = 0.5
|
||||
// if state.player.hp <= 0 {
|
||||
// state.player.hp = 0
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
//TODO: УБрать вообще? мапа фиксит инвалидацию поинтеров
|
||||
sys_sweep :: proc(state: ^GameState) {
|
||||
|
||||
it := hm.iterator_make(&state.entities)
|
||||
for e, i in hm.iterate(&it) {
|
||||
if e.marked do hm.remove(&state.entities, i)
|
||||
}
|
||||
|
||||
// i = 0
|
||||
// for i < len(state.fxs) {
|
||||
// if state.fxs[i].marked {
|
||||
// ordered_remove(&state.fxs, i)
|
||||
// } else {
|
||||
// i += 1
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// entity_draw :: proc(e: ^Entity2) {
|
||||
// for g in e.gfx {
|
||||
// gfx_draw(e.pos, g)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
sys_update :: proc(state: ^GameState, dt: f32) {
|
||||
p := hm.get(&state.entities, state.player)
|
||||
|
||||
it := hm.iterator_make(&state.entities)
|
||||
for e, _ in hm.iterate(&it) {
|
||||
#partial switch &v in e.v {
|
||||
case Enemy:
|
||||
enemy_update(state, p, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sys_render :: proc(state: ^GameState) {
|
||||
rl.BeginDrawing()
|
||||
rl.ClearBackground({20, 20, 30, 255})
|
||||
rl.BeginMode2D(state.camera)
|
||||
{
|
||||
it := hm.iterator_make(&state.entities)
|
||||
for e, _ in hm.iterate(&it) {
|
||||
#partial switch &v in e.v {
|
||||
case Player:
|
||||
for g in &v.gfx do gfx_draw(e.pos, g)
|
||||
case Enemy:
|
||||
for g in &v.gfx do gfx_draw(e.pos, g)
|
||||
}
|
||||
}
|
||||
|
||||
// for &e in state.enemies {
|
||||
// entity_draw(&e)
|
||||
// ui_draw_world_hp_bar(cast(^Actor)&e)
|
||||
// }
|
||||
//
|
||||
// for &it in state.items {
|
||||
// entity_draw(&it)
|
||||
// }
|
||||
//
|
||||
// for &fx in state.fxs {
|
||||
// fx_draw(&fx)
|
||||
// }
|
||||
//
|
||||
// entity_draw(&state.player)
|
||||
// ui_draw_world_hp_bar(cast(^Actor)&state.player)
|
||||
}
|
||||
rl.EndMode2D()
|
||||
|
||||
// ui_draw(state)
|
||||
|
||||
rl.EndDrawing()
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import rl "vendor:raylib"
|
||||
|
||||
|
||||
ui_draw_world_hp_bar :: proc(actor: ^Actor) {
|
||||
hp_ratio := f32(actor.hp) / f32(actor.max_hp)
|
||||
bar_w := actor.col_size * 3
|
||||
bar_pos := actor.pos + rl.Vector2{-bar_w / 2, -actor.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: ^GameState3) {
|
||||
rl.DrawText(
|
||||
rl.TextFormat("HP: %d/%d", state.player.hp, state.player.max_hp),
|
||||
12,
|
||||
12,
|
||||
20,
|
||||
rl.RAYWHITE,
|
||||
)
|
||||
rl.DrawText(rl.TextFormat("Enemies: %d", len(state.enemies)), 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