init2
@@ -0,0 +1 @@
|
|||||||
|
src.bin
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
require("custom.runner").setup({
|
||||||
|
commands = {
|
||||||
|
run = { cmd = "odin run ." }, -- активная команда
|
||||||
|
-- test = { cmd = "go test ./..." },
|
||||||
|
-- build = { cmd = "odin build ./src/" },
|
||||||
|
-- lint = { cmd = "go vet ./..." },
|
||||||
|
},
|
||||||
|
focus = false, -- фокус на нижний терминал при запуске
|
||||||
|
|
||||||
|
-- ── Опции редактора для проекта (применяются к буферам) ──
|
||||||
|
-- options = {
|
||||||
|
-- tabstop = 4,
|
||||||
|
-- shiftwidth = 4,
|
||||||
|
-- expandtab = false,
|
||||||
|
-- },
|
||||||
|
|
||||||
|
-- ── Исключения для snacks-файндеров (поверх базовых .git, .gitignore и т.п.) ──
|
||||||
|
exclude = {
|
||||||
|
"assets",
|
||||||
|
}, -- например: { "_build", "vendor" }
|
||||||
|
})
|
||||||
|
|
||||||
|
-- ── Проектная автокоманда (пример) ──
|
||||||
|
-- vim.api.nvim_create_autocmd("BufWritePre", {
|
||||||
|
-- pattern = "*.odin",
|
||||||
|
-- callback = function()
|
||||||
|
-- vim.notify("Файл сохраняется в проекте", vim.log.levels.INFO)
|
||||||
|
-- end,
|
||||||
|
-- })
|
||||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
@@ -0,0 +1,123 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
|
||||||
|
import hm "core:container/handle_map"
|
||||||
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
|
ent_handle :: hm.Handle32
|
||||||
|
|
||||||
|
Entity :: struct {
|
||||||
|
position: rl.Vector2,
|
||||||
|
hp: i32,
|
||||||
|
speed: f32,
|
||||||
|
col: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
Player :: struct {
|
||||||
|
using ent: Entity,
|
||||||
|
walk_speed: f32,
|
||||||
|
run_speed: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
Enemy :: struct {
|
||||||
|
using ent: Entity,
|
||||||
|
handle: ent_handle,
|
||||||
|
}
|
||||||
|
|
||||||
|
create_enemy :: proc(scene: ^Scene, pos: rl.Vector2) {
|
||||||
|
if scene.enemy_count >= MAX_ENEMIES {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
scene.enemies[scene.enemy_count] = Enemy {
|
||||||
|
position = pos,
|
||||||
|
hp = 100,
|
||||||
|
speed = 100,
|
||||||
|
col = 32,
|
||||||
|
}
|
||||||
|
scene.enemy_count += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
update_player :: proc(player: ^Player, dt: f32) {
|
||||||
|
input: rl.Vector2
|
||||||
|
if rl.IsKeyDown(.W) {
|
||||||
|
input.y += -1
|
||||||
|
}
|
||||||
|
if rl.IsKeyDown(.D) {
|
||||||
|
input.x += 1
|
||||||
|
}
|
||||||
|
if rl.IsKeyDown(.S) {
|
||||||
|
input.y += 1
|
||||||
|
}
|
||||||
|
if rl.IsKeyDown(.A) {
|
||||||
|
input.x += -1
|
||||||
|
}
|
||||||
|
input = rl.Vector2Normalize(input)
|
||||||
|
|
||||||
|
if rl.IsKeyDown(.LEFT_SHIFT) {
|
||||||
|
player.speed = player.run_speed
|
||||||
|
} else {
|
||||||
|
player.speed = player.walk_speed
|
||||||
|
}
|
||||||
|
|
||||||
|
player.position += input * player.speed * dt
|
||||||
|
}
|
||||||
|
|
||||||
|
calc_col_box :: proc(center: rl.Vector2, side: f32) -> rl.Rectangle {
|
||||||
|
return rl.Rectangle {
|
||||||
|
x = center.x - side / 2,
|
||||||
|
y = center.y - side / 2,
|
||||||
|
width = side,
|
||||||
|
height = side,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Разлепляет два ентерия — выталкивает по оси наименьшего проникновения.
|
||||||
|
// Чтоб не стояли друг в друге как родные.
|
||||||
|
resolve_entity_overlap :: proc(a, b: ^Entity) {
|
||||||
|
ra := calc_col_box(a.position, a.col)
|
||||||
|
rb := calc_col_box(b.position, b.col)
|
||||||
|
|
||||||
|
if !rl.CheckCollisionRecs(ra, rb) do return
|
||||||
|
|
||||||
|
overlap_x := min(ra.x + ra.width - rb.x, rb.x + rb.width - ra.x)
|
||||||
|
overlap_y := min(ra.y + ra.height - rb.y, rb.y + rb.height - ra.y)
|
||||||
|
|
||||||
|
if overlap_x < overlap_y {
|
||||||
|
half := overlap_x / 2
|
||||||
|
if a.position.x < b.position.x {
|
||||||
|
a.position.x -= half
|
||||||
|
b.position.x += half
|
||||||
|
} else {
|
||||||
|
a.position.x += half
|
||||||
|
b.position.x -= half
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
half := overlap_y / 2
|
||||||
|
if a.position.y < b.position.y {
|
||||||
|
a.position.y -= half
|
||||||
|
b.position.y += half
|
||||||
|
} else {
|
||||||
|
a.position.y += half
|
||||||
|
b.position.y -= half
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_collisions :: proc(scene: ^Scene) {
|
||||||
|
// Враг против врага — чтоб толпой не слипались
|
||||||
|
for i in 0 ..< scene.enemy_count {
|
||||||
|
for j in i + 1 ..< scene.enemy_count {
|
||||||
|
resolve_entity_overlap(
|
||||||
|
&scene.enemies[i].ent,
|
||||||
|
&scene.enemies[j].ent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Игрок против врагов — чтоб не захавывали друг друга
|
||||||
|
for &enemy in scene.enemies[:scene.enemy_count] {
|
||||||
|
resolve_entity_overlap(&scene.player.ent, &enemy.ent)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import hm "core:container/handle_map"
|
||||||
|
import "core:fmt"
|
||||||
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
|
SCREEN_W :: 1280
|
||||||
|
SCREEN_H :: 720
|
||||||
|
|
||||||
|
|
||||||
|
MAX_ENEMIES :: 10
|
||||||
|
|
||||||
|
Scene :: struct {
|
||||||
|
player: Player,
|
||||||
|
camera: rl.Camera2D,
|
||||||
|
enemies: hm.Static_Handle_Map(128, Enemy, ent_handle),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
create_scene :: proc() -> Scene {
|
||||||
|
scene := Scene {
|
||||||
|
player = Player {
|
||||||
|
position = rl.Vector2{100, 100},
|
||||||
|
hp = 100,
|
||||||
|
walk_speed = 300,
|
||||||
|
run_speed = 500,
|
||||||
|
col = 32,
|
||||||
|
},
|
||||||
|
camera = rl.Camera2D {
|
||||||
|
zoom = 1,
|
||||||
|
offset = rl.Vector2{SCREEN_W / 2, SCREEN_H / 2},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_ = hm.add(
|
||||||
|
&scene.enemies,
|
||||||
|
Enemy {
|
||||||
|
position = rl.Vector2{500, 500},
|
||||||
|
hp = 100,
|
||||||
|
speed = 100,
|
||||||
|
col = 32,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
_ = hm.add(
|
||||||
|
&scene.enemies,
|
||||||
|
Enemy {
|
||||||
|
position = rl.Vector2{600, 600},
|
||||||
|
hp = 100,
|
||||||
|
speed = 100,
|
||||||
|
col = 32,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return scene
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_scene :: proc(scene: ^Scene) {
|
||||||
|
rl.BeginMode2D(scene.camera)
|
||||||
|
rl.DrawCircle(
|
||||||
|
cast(i32)scene.player.position.x,
|
||||||
|
cast(i32)scene.player.position.y,
|
||||||
|
32,
|
||||||
|
rl.GREEN,
|
||||||
|
)
|
||||||
|
rl.DrawRectangle(300, 300, 100, 100, rl.BLUE)
|
||||||
|
|
||||||
|
for enemy in scene.enemies[:scene.enemy_count] {
|
||||||
|
rl.DrawCircle(
|
||||||
|
cast(i32)enemy.position.x,
|
||||||
|
cast(i32)enemy.position.y,
|
||||||
|
32,
|
||||||
|
rl.RED,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.EndMode2D()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
update_scene :: proc(scene: ^Scene, dt: f32) {
|
||||||
|
update_player(&scene.player, dt)
|
||||||
|
scene.camera.target = scene.player.position
|
||||||
|
|
||||||
|
// Сначала двигаем всех врагов к игроку — без страха и упрёка
|
||||||
|
for &enemy in scene.enemies[:scene.enemy_count] {
|
||||||
|
dir := rl.Vector2Normalize(scene.player.position - enemy.position)
|
||||||
|
enemy.position += dir * enemy.speed * dt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Потом разлепляем коллизии — выталкиваем кто кого переехал
|
||||||
|
resolve_collisions(scene)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
fmt.println("Hello, world!")
|
||||||
|
|
||||||
|
scene := create_scene()
|
||||||
|
|
||||||
|
rl.InitWindow(SCREEN_W, SCREEN_H, "HUI")
|
||||||
|
defer rl.CloseWindow()
|
||||||
|
rl.SetTargetFPS(200)
|
||||||
|
|
||||||
|
|
||||||
|
for !rl.WindowShouldClose() {
|
||||||
|
dt := rl.GetFrameTime()
|
||||||
|
update_scene(&scene, dt)
|
||||||
|
rl.BeginDrawing()
|
||||||
|
{
|
||||||
|
rl.ClearBackground(rl.BLACK)
|
||||||
|
draw_scene(&scene)
|
||||||
|
|
||||||
|
}
|
||||||
|
rl.EndDrawing()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/odinfmt.schema.json",
|
||||||
|
"character_width": 80,
|
||||||
|
"tabs": true,
|
||||||
|
"spaces": 2,
|
||||||
|
"tabs_width": 4
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||