diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e9669d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +src.bin diff --git a/.nvim.lua b/.nvim.lua new file mode 100644 index 0000000..d1d8713 --- /dev/null +++ b/.nvim.lua @@ -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, +-- }) diff --git a/assets/3rdparty/Swordsman_lvl3_Death_without_shadow.png b/assets/3rdparty/Swordsman_lvl3_Death_without_shadow.png new file mode 100644 index 0000000..e82ef4e Binary files /dev/null and b/assets/3rdparty/Swordsman_lvl3_Death_without_shadow.png differ diff --git a/assets/3rdparty/Swordsman_lvl3_Hurt_without_shadow.png b/assets/3rdparty/Swordsman_lvl3_Hurt_without_shadow.png new file mode 100644 index 0000000..a95e8bb Binary files /dev/null and b/assets/3rdparty/Swordsman_lvl3_Hurt_without_shadow.png differ diff --git a/assets/3rdparty/Swordsman_lvl3_Run_Attack_without_shadow.png b/assets/3rdparty/Swordsman_lvl3_Run_Attack_without_shadow.png new file mode 100644 index 0000000..58cad08 Binary files /dev/null and b/assets/3rdparty/Swordsman_lvl3_Run_Attack_without_shadow.png differ diff --git a/assets/3rdparty/Swordsman_lvl3_Run_without_shadow.png b/assets/3rdparty/Swordsman_lvl3_Run_without_shadow.png new file mode 100644 index 0000000..fdb16f1 Binary files /dev/null and b/assets/3rdparty/Swordsman_lvl3_Run_without_shadow.png differ diff --git a/assets/3rdparty/Swordsman_lvl3_Walk_Attack_without_shadow.png b/assets/3rdparty/Swordsman_lvl3_Walk_Attack_without_shadow.png new file mode 100644 index 0000000..69ed9d8 Binary files /dev/null and b/assets/3rdparty/Swordsman_lvl3_Walk_Attack_without_shadow.png differ diff --git a/assets/3rdparty/Swordsman_lvl3_Walk_without_shadow.png b/assets/3rdparty/Swordsman_lvl3_Walk_without_shadow.png new file mode 100644 index 0000000..82be044 Binary files /dev/null and b/assets/3rdparty/Swordsman_lvl3_Walk_without_shadow.png differ diff --git a/assets/3rdparty/Swordsman_lvl3_attack_without_shadow.png b/assets/3rdparty/Swordsman_lvl3_attack_without_shadow.png new file mode 100644 index 0000000..675e81a Binary files /dev/null and b/assets/3rdparty/Swordsman_lvl3_attack_without_shadow.png differ diff --git a/assets/3rdparty/player_idle.png b/assets/3rdparty/player_idle.png new file mode 100644 index 0000000..45c2185 Binary files /dev/null and b/assets/3rdparty/player_idle.png differ diff --git a/assets/3rdparty/player_idle.png-autosave.kra b/assets/3rdparty/player_idle.png-autosave.kra new file mode 100644 index 0000000..1e9ff6f Binary files /dev/null and b/assets/3rdparty/player_idle.png-autosave.kra differ diff --git a/entity.odin b/entity.odin new file mode 100644 index 0000000..c12f035 --- /dev/null +++ b/entity.odin @@ -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) + } +} diff --git a/hello b/hello new file mode 100755 index 0000000..76c6bf0 Binary files /dev/null and b/hello differ diff --git a/main.odin b/main.odin new file mode 100644 index 0000000..f0390a6 --- /dev/null +++ b/main.odin @@ -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() + } +} diff --git a/odinfmt.json b/odinfmt.json new file mode 100644 index 0000000..7ffb1ba --- /dev/null +++ b/odinfmt.json @@ -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 +} diff --git a/ols.json b/ols.json new file mode 100644 index 0000000..c488e1b --- /dev/null +++ b/ols.json @@ -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 +} diff --git a/test.odin b/test.odin new file mode 100644 index 0000000..06ab7d0 --- /dev/null +++ b/test.odin @@ -0,0 +1 @@ +package main