Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
#pragma once
|
|
|
|
|
|
#include <algorithm>
|
Tile2D: движок отдельным продуктом, игра — проектом в нём (M11-M20)
Десять вех одним состоянием: разделять их пофайлово нечем, потому что
src/game.cpp менялся почти в каждой. Собирается начисто без предупреждений,
все проверки проходят.
ДВИЖОК (engine/, цель tile2d)
Каталог свой, и решение «это движок» принимается тем, куда положен файл.
Компилятор нашёл три зависимости движка от игры, и все сняты: размеры кадра
и карты уехали в engine/config.h, потолок населения этажа стал своим
(MAX_LEVEL_SPAWNS против игровой MAX_TARGETS), запасная точка старта стала
центром карты вместо позиции отряда.
engine/projection.* единственное место, где движок знает про изометрию;
видов сразу два — абстракция с одной реализацией ничем
не проверена и обычно оказывается неправильной
engine/project.* манифест проекта: пути, вид, кривая глубины
engine/app.* окно, кадр, fixed timestep — вместо двух копий цикла
engine/tilemap_draw движок научился показывать свою карту
РЕДАКТОР (editor/, цель tile2d_editor)
Открывает любой проект и ни одной игры по имени не знает.
--tool одиннадцать безоконных команд: комнаты, каталог, кривая, сборка
этажа, ASCII-карта, проверка набора. Правку делают файлами,
последствия читают командой (docs/19-agent.md)
терминал снизу: те же инструменты, git, сборка и запросы к claude/codex,
открытый проект уходит потомкам через TILE2D_PROJECT
ПРИМЕР (samples/walk, цель tile2d_walk)
Второй потребитель движка. Игра и редактор самостоятельность движка
проверить не могут: редактор мира не рисует, а игра — то место, куда общий
код и уезжает незаметно.
ИГРА (src/)
Экстракшен: вес и два порога переноски, конечный боезапас, расходники
применяются сами, общая сумка отряда, трата опыта на улучшения.
Каждое из правил проверяется прогоном, а не глазами, и проверки поймали:
вес дважды не связывал ничего (мерил по бинтам прогона, а не по добыче);
боец с клинком не тратил найденные патроны при запасном стволе в сумке;
массив виджетов экрана переполнился и молча съел хвост схрона; проверка
улучшений сама ломала забег, вызывая NewRun посреди спуска.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 01:07:19 +07:00
|
|
|
|
|
|
|
|
|
|
#include "engine/config.h"
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
// -----------------------------------------------------------------------------
|
Tile2D: движок отдельным продуктом, игра — проектом в нём (M11-M20)
Десять вех одним состоянием: разделять их пофайлово нечем, потому что
src/game.cpp менялся почти в каждой. Собирается начисто без предупреждений,
все проверки проходят.
ДВИЖОК (engine/, цель tile2d)
Каталог свой, и решение «это движок» принимается тем, куда положен файл.
Компилятор нашёл три зависимости движка от игры, и все сняты: размеры кадра
и карты уехали в engine/config.h, потолок населения этажа стал своим
(MAX_LEVEL_SPAWNS против игровой MAX_TARGETS), запасная точка старта стала
центром карты вместо позиции отряда.
engine/projection.* единственное место, где движок знает про изометрию;
видов сразу два — абстракция с одной реализацией ничем
не проверена и обычно оказывается неправильной
engine/project.* манифест проекта: пути, вид, кривая глубины
engine/app.* окно, кадр, fixed timestep — вместо двух копий цикла
engine/tilemap_draw движок научился показывать свою карту
РЕДАКТОР (editor/, цель tile2d_editor)
Открывает любой проект и ни одной игры по имени не знает.
--tool одиннадцать безоконных команд: комнаты, каталог, кривая, сборка
этажа, ASCII-карта, проверка набора. Правку делают файлами,
последствия читают командой (docs/19-agent.md)
терминал снизу: те же инструменты, git, сборка и запросы к claude/codex,
открытый проект уходит потомкам через TILE2D_PROJECT
ПРИМЕР (samples/walk, цель tile2d_walk)
Второй потребитель движка. Игра и редактор самостоятельность движка
проверить не могут: редактор мира не рисует, а игра — то место, куда общий
код и уезжает незаметно.
ИГРА (src/)
Экстракшен: вес и два порога переноски, конечный боезапас, расходники
применяются сами, общая сумка отряда, трата опыта на улучшения.
Каждое из правил проверяется прогоном, а не глазами, и проверки поймали:
вес дважды не связывал ничего (мерил по бинтам прогона, а не по добыче);
боец с клинком не тратил найденные патроны при запасном стволе в сумке;
массив виджетов экрана переполнился и молча съел хвост схрона; проверка
улучшений сама ломала забег, вызывая NewRun посреди спуска.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 01:07:19 +07:00
|
|
|
|
// Единственное место, где живут числа ИГРЫ.
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
// В коде не должно быть магических констант — всё берётся отсюда.
|
|
|
|
|
|
//
|
Tile2D: движок отдельным продуктом, игра — проектом в нём (M11-M20)
Десять вех одним состоянием: разделять их пофайлово нечем, потому что
src/game.cpp менялся почти в каждой. Собирается начисто без предупреждений,
все проверки проходят.
ДВИЖОК (engine/, цель tile2d)
Каталог свой, и решение «это движок» принимается тем, куда положен файл.
Компилятор нашёл три зависимости движка от игры, и все сняты: размеры кадра
и карты уехали в engine/config.h, потолок населения этажа стал своим
(MAX_LEVEL_SPAWNS против игровой MAX_TARGETS), запасная точка старта стала
центром карты вместо позиции отряда.
engine/projection.* единственное место, где движок знает про изометрию;
видов сразу два — абстракция с одной реализацией ничем
не проверена и обычно оказывается неправильной
engine/project.* манифест проекта: пути, вид, кривая глубины
engine/app.* окно, кадр, fixed timestep — вместо двух копий цикла
engine/tilemap_draw движок научился показывать свою карту
РЕДАКТОР (editor/, цель tile2d_editor)
Открывает любой проект и ни одной игры по имени не знает.
--tool одиннадцать безоконных команд: комнаты, каталог, кривая, сборка
этажа, ASCII-карта, проверка набора. Правку делают файлами,
последствия читают командой (docs/19-agent.md)
терминал снизу: те же инструменты, git, сборка и запросы к claude/codex,
открытый проект уходит потомкам через TILE2D_PROJECT
ПРИМЕР (samples/walk, цель tile2d_walk)
Второй потребитель движка. Игра и редактор самостоятельность движка
проверить не могут: редактор мира не рисует, а игра — то место, куда общий
код и уезжает незаметно.
ИГРА (src/)
Экстракшен: вес и два порога переноски, конечный боезапас, расходники
применяются сами, общая сумка отряда, трата опыта на улучшения.
Каждое из правил проверяется прогоном, а не глазами, и проверки поймали:
вес дважды не связывал ничего (мерил по бинтам прогона, а не по добыче);
боец с клинком не тратил найденные патроны при запасном стволе в сумке;
массив виджетов экрана переполнился и молча съел хвост схрона; проверка
улучшений сама ломала забег, вызывая NewRun посреди спуска.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 01:07:19 +07:00
|
|
|
|
// * блок COMPILE-TIME — размеры буферов игры, менять только пересборкой;
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
// * struct Tuning — всё, что подкручивается в рантайме ([ ] выбор, - = шаг).
|
Tile2D: движок отдельным продуктом, игра — проектом в нём (M11-M20)
Десять вех одним состоянием: разделять их пофайлово нечем, потому что
src/game.cpp менялся почти в каждой. Собирается начисто без предупреждений,
все проверки проходят.
ДВИЖОК (engine/, цель tile2d)
Каталог свой, и решение «это движок» принимается тем, куда положен файл.
Компилятор нашёл три зависимости движка от игры, и все сняты: размеры кадра
и карты уехали в engine/config.h, потолок населения этажа стал своим
(MAX_LEVEL_SPAWNS против игровой MAX_TARGETS), запасная точка старта стала
центром карты вместо позиции отряда.
engine/projection.* единственное место, где движок знает про изометрию;
видов сразу два — абстракция с одной реализацией ничем
не проверена и обычно оказывается неправильной
engine/project.* манифест проекта: пути, вид, кривая глубины
engine/app.* окно, кадр, fixed timestep — вместо двух копий цикла
engine/tilemap_draw движок научился показывать свою карту
РЕДАКТОР (editor/, цель tile2d_editor)
Открывает любой проект и ни одной игры по имени не знает.
--tool одиннадцать безоконных команд: комнаты, каталог, кривая, сборка
этажа, ASCII-карта, проверка набора. Правку делают файлами,
последствия читают командой (docs/19-agent.md)
терминал снизу: те же инструменты, git, сборка и запросы к claude/codex,
открытый проект уходит потомкам через TILE2D_PROJECT
ПРИМЕР (samples/walk, цель tile2d_walk)
Второй потребитель движка. Игра и редактор самостоятельность движка
проверить не могут: редактор мира не рисует, а игра — то место, куда общий
код и уезжает незаметно.
ИГРА (src/)
Экстракшен: вес и два порога переноски, конечный боезапас, расходники
применяются сами, общая сумка отряда, трата опыта на улучшения.
Каждое из правил проверяется прогоном, а не глазами, и проверки поймали:
вес дважды не связывал ничего (мерил по бинтам прогона, а не по добыче);
боец с клинком не тратил найденные патроны при запасном стволе в сумке;
массив виджетов экрана переполнился и молча съел хвост схрона; проверка
улучшений сама ломала забег, вызывая NewRun посреди спуска.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 01:07:19 +07:00
|
|
|
|
//
|
|
|
|
|
|
// Размеры кадра, тайла и карты сюда БОЛЬШЕ НЕ ВХОДЯТ: это хозяйство движка, оно
|
|
|
|
|
|
// в engine/config.h. Имена те же, файл включён выше — по коду игры ничего не
|
|
|
|
|
|
// меняется, но движок теперь собирается без единого файла игры.
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
// --- compile-time константы --------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
// Точка старта отряда (центральная арена).
|
|
|
|
|
|
constexpr float SQUAD_START_X = 26.5f; // ближе к кучке: дальность стала 7.2
|
|
|
|
|
|
constexpr float SQUAD_START_Y = 26.5f;
|
|
|
|
|
|
|
|
|
|
|
|
constexpr int NUM_AGENTS = 5;
|
Tile2D: движок отдельным продуктом, игра — проектом в нём (M11-M20)
Десять вех одним состоянием: разделять их пофайлово нечем, потому что
src/game.cpp менялся почти в каждой. Собирается начисто без предупреждений,
все проверки проходят.
ДВИЖОК (engine/, цель tile2d)
Каталог свой, и решение «это движок» принимается тем, куда положен файл.
Компилятор нашёл три зависимости движка от игры, и все сняты: размеры кадра
и карты уехали в engine/config.h, потолок населения этажа стал своим
(MAX_LEVEL_SPAWNS против игровой MAX_TARGETS), запасная точка старта стала
центром карты вместо позиции отряда.
engine/projection.* единственное место, где движок знает про изометрию;
видов сразу два — абстракция с одной реализацией ничем
не проверена и обычно оказывается неправильной
engine/project.* манифест проекта: пути, вид, кривая глубины
engine/app.* окно, кадр, fixed timestep — вместо двух копий цикла
engine/tilemap_draw движок научился показывать свою карту
РЕДАКТОР (editor/, цель tile2d_editor)
Открывает любой проект и ни одной игры по имени не знает.
--tool одиннадцать безоконных команд: комнаты, каталог, кривая, сборка
этажа, ASCII-карта, проверка набора. Правку делают файлами,
последствия читают командой (docs/19-agent.md)
терминал снизу: те же инструменты, git, сборка и запросы к claude/codex,
открытый проект уходит потомкам через TILE2D_PROJECT
ПРИМЕР (samples/walk, цель tile2d_walk)
Второй потребитель движка. Игра и редактор самостоятельность движка
проверить не могут: редактор мира не рисует, а игра — то место, куда общий
код и уезжает незаметно.
ИГРА (src/)
Экстракшен: вес и два порога переноски, конечный боезапас, расходники
применяются сами, общая сумка отряда, трата опыта на улучшения.
Каждое из правил проверяется прогоном, а не глазами, и проверки поймали:
вес дважды не связывал ничего (мерил по бинтам прогона, а не по добыче);
боец с клинком не тратил найденные патроны при запасном стволе в сумке;
массив виджетов экрана переполнился и молча съел хвост схрона; проверка
улучшений сама ломала забег, вызывая NewRun посреди спуска.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 01:07:19 +07:00
|
|
|
|
constexpr int NUM_TARGETS = 12; // мишени приёмки: расстановка захардкожена
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
constexpr int MAX_BULLETS = 512;
|
|
|
|
|
|
|
Tile2D: движок отдельным продуктом, игра — проектом в нём (M11-M20)
Десять вех одним состоянием: разделять их пофайлово нечем, потому что
src/game.cpp менялся почти в каждой. Собирается начисто без предупреждений,
все проверки проходят.
ДВИЖОК (engine/, цель tile2d)
Каталог свой, и решение «это движок» принимается тем, куда положен файл.
Компилятор нашёл три зависимости движка от игры, и все сняты: размеры кадра
и карты уехали в engine/config.h, потолок населения этажа стал своим
(MAX_LEVEL_SPAWNS против игровой MAX_TARGETS), запасная точка старта стала
центром карты вместо позиции отряда.
engine/projection.* единственное место, где движок знает про изометрию;
видов сразу два — абстракция с одной реализацией ничем
не проверена и обычно оказывается неправильной
engine/project.* манифест проекта: пути, вид, кривая глубины
engine/app.* окно, кадр, fixed timestep — вместо двух копий цикла
engine/tilemap_draw движок научился показывать свою карту
РЕДАКТОР (editor/, цель tile2d_editor)
Открывает любой проект и ни одной игры по имени не знает.
--tool одиннадцать безоконных команд: комнаты, каталог, кривая, сборка
этажа, ASCII-карта, проверка набора. Правку делают файлами,
последствия читают командой (docs/19-agent.md)
терминал снизу: те же инструменты, git, сборка и запросы к claude/codex,
открытый проект уходит потомкам через TILE2D_PROJECT
ПРИМЕР (samples/walk, цель tile2d_walk)
Второй потребитель движка. Игра и редактор самостоятельность движка
проверить не могут: редактор мира не рисует, а игра — то место, куда общий
код и уезжает незаметно.
ИГРА (src/)
Экстракшен: вес и два порога переноски, конечный боезапас, расходники
применяются сами, общая сумка отряда, трата опыта на улучшения.
Каждое из правил проверяется прогоном, а не глазами, и проверки поймали:
вес дважды не связывал ничего (мерил по бинтам прогона, а не по добыче);
боец с клинком не тратил найденные патроны при запасном стволе в сумке;
массив виджетов экрана переполнился и молча съел хвост схрона; проверка
улучшений сама ломала забег, вызывая NewRun посреди спуска.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 01:07:19 +07:00
|
|
|
|
// Твари, которых боевой режим доставляет СВЕРХ мишеней. В песочнице приёмки их
|
|
|
|
|
|
// нет: расстановка мишеней и все замеры на ней остаются прежними.
|
|
|
|
|
|
constexpr int NUM_EXTRA_ENEMIES = 12;
|
|
|
|
|
|
|
|
|
|
|
|
// Разрушаемые объекты (ящики, бочки, шкафчики). Спавнятся ТОЛЬКО в боевом режиме;
|
|
|
|
|
|
// приёмка их не видит. Приоритет — ниже всех: ИИ переключается на них только
|
|
|
|
|
|
// если ни одного живого врага в поле зрения нет.
|
|
|
|
|
|
constexpr int NUM_PROPS = 8;
|
|
|
|
|
|
|
|
|
|
|
|
// Потолок на число целей в мире. Он не «на всякий случай»: MeleeWeapon::hitMask —
|
|
|
|
|
|
// это маска «кого уже задел ЭТИМ взмахом», индексируемая номером цели, и цель
|
|
|
|
|
|
// за её пределами молча получала бы удары дважды за взмах.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Было 32. Игра про бесконечный спуск упёрлась в этот потолок примерно на
|
|
|
|
|
|
// шестнадцатом этаже: бюджет опасности перестал влезать в число тел, и глубже
|
|
|
|
|
|
// этажи переставали становиться страшнее. Маска расширена до 64 бит — это не
|
|
|
|
|
|
// «запас на будущее», а ровно та цифра, которой сейчас не хватало.
|
|
|
|
|
|
constexpr int MAX_TARGETS = 64;
|
|
|
|
|
|
static_assert(NUM_TARGETS + NUM_EXTRA_ENEMIES + NUM_PROPS <= MAX_TARGETS,
|
|
|
|
|
|
"целей больше, чем битов в MeleeWeapon::hitMask");
|
|
|
|
|
|
// У движка потолок населения свой (MAX_LEVEL_SPAWNS в engine/level_gen.h):
|
|
|
|
|
|
// генератор уровней не обязан знать про маску удара клинком. Что одно влезает
|
|
|
|
|
|
// в другое, проверяется там, где видны оба, — в src/game.h.
|
|
|
|
|
|
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
// Сколько слотов строя занимают бойцы ближнего боя (слоты 0..N-1).
|
|
|
|
|
|
// В клине это остриё и левое крыло — те, кто и так идёт первым.
|
|
|
|
|
|
constexpr int DEFAULT_MELEE_COUNT = 2;
|
|
|
|
|
|
|
|
|
|
|
|
// Сколько ПОСЛЕДНИХ слотов носят пистолет вместо карабина.
|
|
|
|
|
|
// Короткий ствол уезжает в хвост клина: ему там и место.
|
|
|
|
|
|
constexpr int DEFAULT_PISTOL_COUNT = 1;
|
|
|
|
|
|
|
|
|
|
|
|
// Сколько тайлов пола могут нести запечённую кровь. Каждый — своя копия
|
|
|
|
|
|
// спрайта 32x16 (2 КБ), то есть потолок ~2.4 МБ. Дальше новые пятна ложатся
|
|
|
|
|
|
// только на уже запачканные тайлы: декали вечные, но не бесконечные.
|
|
|
|
|
|
constexpr int BLOOD_TILE_BUDGET = 1200;
|
|
|
|
|
|
|
|
|
|
|
|
// Решатель огневых позиций: 3 кольца x 12 направлений + текущая позиция.
|
|
|
|
|
|
constexpr int SOLVER_RINGS = 3;
|
|
|
|
|
|
constexpr int SOLVER_DIRS = 12;
|
|
|
|
|
|
constexpr int SOLVER_CANDIDATES = SOLVER_RINGS * SOLVER_DIRS + 1;
|
|
|
|
|
|
|
|
|
|
|
|
// --- runtime-тюнинг ----------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
struct Tuning
|
|
|
|
|
|
{
|
|
|
|
|
|
// движение агента
|
Пистолет не пропадает, эвакуация откуда угодно, кнопки в HUD (M36)
БАГ С ПИСТОЛЕТОМ - на самом деле ДВА бага.
Первый: экран отряда правит ЖИВОЙ МИР, а вылазка собирает бойцов заново
из профиля (NewRun пересоздаёт ecs::World и раздаёт снаряжение через
ApplyProfileLoadout). Всё, переложенное в меню, стиралось на старте, а
вещь, вынутая из схрона, исчезала вместе с миром - схрон её уже отдал.
Теперь всякая правка снаряжения пишется в профиль сразу.
Второй, рядом: перетаскивание мышью НЕ РАБОТАЛО ВООБЩЕ. Флаг dropOk
нигде не выставлялся в true - только сбрасывался, - и сброс молча
отменялся. Единственным рабочим способом переодеться оставался ENTER.
Теперь флаг считается каждый кадр, и ячейка честно красится в "нельзя".
Заодно: вне забега сумка не принимает вещей. Она принадлежит вылазке и
очищается при сборке этажа; вещь, положенная в неё из меню, исчезла бы
так же молча.
Проверка ловит именно цепочку "поменял в меню -> начал вылазку -> вышел
с этим": сам обмен работал и раньше, пропадал он на старте.
сборы: в профиле после обмена PISTOL, на этаже PISTOL
ТВАРИ НЕ ВОСКРЕСАЮТ. Убитая в этой вылазке убита насовсем: с возвратом
любая расчищенная комната через полминуты снова была полна, и "пробиться
и уйти" не отличалось от "молотить бесконечную очередь". Исключение одно
- аура плакальщика, это его роль.
ЭВАКУАЦИЯ С ЛЮБОГО ЭТАЖА И ИЗ ЛЮБОЙ ТОЧКИ. Раньше уйти можно было только
стоя на кольце: отряд, решивший не рисковать, всё равно шёл через этаж и
умирал ровно там, где игрок уже принял решение. Чекпоинты теперь значат
только то, чем должны быть: откуда можно НАЧАТЬ следующую вылазку.
ОТЗЫВЧИВОСТЬ СТРОЯ. Отряд ходил 1.75 при курсоре 3.3 - он всегда
отставал, и управление ощущалось как "тяну за верёвку". Стало 2.6,
поводок короче (2.5 против 3.0), доворот строя вдвое резче.
Ориентация строя теперь ПОВОРАЧИВАЕТСЯ, а не щёлкает: раньше слоты клина
перепрыгивали через весь отряд на каждом повороте, и бойцы бросали дорогу
ради перестроения. В тесноте доворот почти мгновенный - там строй и так
колонна, а колонна, смотрящая в прошлую сторону, упирается в стену.
Автопилот поймал, что первая версия правок сделала хуже: при быстром
наборе натяжения (5.2) отряд уходил в вечный спринт, а спринт растягивает
строй и выключает огонь. Порог спринта поднят до 0.95, набор до 3.4 -
приёмка 11.4 держится (0.13 с при пороге 0.30).
КНОПКИ В HUD. Слева внизу четыре: SQUAD, TREE, WORK, MENU. Экраны
открывались только клавишами, о которых игра нигде не говорила, - без
кнопки мастерская и дерево не открываются никогда.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 09:24:10 +07:00
|
|
|
|
// Скорость отряда. Была 1.75 при курсоре 3.3: отряд ВСЕГДА отставал, и
|
|
|
|
|
|
// управление ощущалось как «тяну за верёвку», а не «веду людей». Поднято до
|
|
|
|
|
|
// 2.6 — это по-прежнему медленнее курсора (иначе поводок теряет смысл), но
|
|
|
|
|
|
// разрыв закрывается за доли секунды, а не за метры.
|
|
|
|
|
|
float moveSpeed = 2.60f; // юнитов/сек: хоррор, а не шутер-беготня
|
|
|
|
|
|
float moveSpeedFast = 4.4f; // догоняют курсор быстро — иначе отряд отстаёт
|
|
|
|
|
|
float steerAccel = 26.0f; // тяжелее; НИЖЕ 16 нельзя — ломается критерий 11.4
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
float bodyRadius = 0.28f;
|
|
|
|
|
|
float separationRadius = 0.7f;
|
|
|
|
|
|
float wSeek = 1.0f;
|
Пистолет не пропадает, эвакуация откуда угодно, кнопки в HUD (M36)
БАГ С ПИСТОЛЕТОМ - на самом деле ДВА бага.
Первый: экран отряда правит ЖИВОЙ МИР, а вылазка собирает бойцов заново
из профиля (NewRun пересоздаёт ecs::World и раздаёт снаряжение через
ApplyProfileLoadout). Всё, переложенное в меню, стиралось на старте, а
вещь, вынутая из схрона, исчезала вместе с миром - схрон её уже отдал.
Теперь всякая правка снаряжения пишется в профиль сразу.
Второй, рядом: перетаскивание мышью НЕ РАБОТАЛО ВООБЩЕ. Флаг dropOk
нигде не выставлялся в true - только сбрасывался, - и сброс молча
отменялся. Единственным рабочим способом переодеться оставался ENTER.
Теперь флаг считается каждый кадр, и ячейка честно красится в "нельзя".
Заодно: вне забега сумка не принимает вещей. Она принадлежит вылазке и
очищается при сборке этажа; вещь, положенная в неё из меню, исчезла бы
так же молча.
Проверка ловит именно цепочку "поменял в меню -> начал вылазку -> вышел
с этим": сам обмен работал и раньше, пропадал он на старте.
сборы: в профиле после обмена PISTOL, на этаже PISTOL
ТВАРИ НЕ ВОСКРЕСАЮТ. Убитая в этой вылазке убита насовсем: с возвратом
любая расчищенная комната через полминуты снова была полна, и "пробиться
и уйти" не отличалось от "молотить бесконечную очередь". Исключение одно
- аура плакальщика, это его роль.
ЭВАКУАЦИЯ С ЛЮБОГО ЭТАЖА И ИЗ ЛЮБОЙ ТОЧКИ. Раньше уйти можно было только
стоя на кольце: отряд, решивший не рисковать, всё равно шёл через этаж и
умирал ровно там, где игрок уже принял решение. Чекпоинты теперь значат
только то, чем должны быть: откуда можно НАЧАТЬ следующую вылазку.
ОТЗЫВЧИВОСТЬ СТРОЯ. Отряд ходил 1.75 при курсоре 3.3 - он всегда
отставал, и управление ощущалось как "тяну за верёвку". Стало 2.6,
поводок короче (2.5 против 3.0), доворот строя вдвое резче.
Ориентация строя теперь ПОВОРАЧИВАЕТСЯ, а не щёлкает: раньше слоты клина
перепрыгивали через весь отряд на каждом повороте, и бойцы бросали дорогу
ради перестроения. В тесноте доворот почти мгновенный - там строй и так
колонна, а колонна, смотрящая в прошлую сторону, упирается в стену.
Автопилот поймал, что первая версия правок сделала хуже: при быстром
наборе натяжения (5.2) отряд уходил в вечный спринт, а спринт растягивает
строй и выключает огонь. Порог спринта поднят до 0.95, набор до 3.4 -
приёмка 11.4 держится (0.13 с при пороге 0.30).
КНОПКИ В HUD. Слева внизу четыре: SQUAD, TREE, WORK, MENU. Экраны
открывались только клавишами, о которых игра нигде не говорила, - без
кнопки мастерская и дерево не открываются никогда.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 09:24:10 +07:00
|
|
|
|
// Как быстро строй доворачивается к направлению хода. Мгновенный доворот
|
|
|
|
|
|
// заставлял бойцов бегать вбок на каждом повороте (sim/squad.cpp).
|
|
|
|
|
|
float formTurnRate = 7.0f;
|
Поле путей: отставший возвращается в строй (M37)
Отряд из пяти не проходил первый этаж ни на одном сиде, а одиночка проходил.
Дело было не в ширине коридоров: у бойцов не было поиска пути вообще. Боец,
оказавшийся за стеной от своего слота, seek'ом тянулся В СТЕНУ, обход стены
толкал его обратно, и он замирал в углу навсегда.
Теперь у каждого бойца своё поле путей (BFS от клетки его цели) с натягиванием
верёвки: он берёт самую дальнюю точку пути, которую видит. Включается обход по
двум условиям сразу — боец упёрся (хочет идти и не едет) И потерял строй. Счётчик
упора сбрасывается по факту «цель видно», а не «поехал»: сброс по скорости давал
дребезг, и боец полз шаг за две секунды.
По дороге вскрылись две неверные МЕРКИ:
* критерий 11.6 считал колонной «разброс вдоль больше, чем поперёк» — и потому
награждал отряд, у которого двое навсегда остались по ту сторону стены. Теперь
колонна меряется самим проёмом: разом в горловине не больше двух, и прошли ВСЕ.
* зонд --wall читал «стены не нашлось» (ноль) как «стена на нулевом этаже» и
объявлял пятёрку, не сломавшуюся нигде, худшей из трёх.
Обе переписаны и проверены на сломанной сборке.
Замеры: --play 6 5 на восьми сидах 47/48 (было 0/48), --accept PASS, шаг 6.1 мкс,
--wall 30 одиночка D20 / трое D20 / пятеро D31 (не найдена), --descend 8/30,
hollow --descend 6, --ui-check, оба проекта --check — без ошибок.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 22:31:34 +07:00
|
|
|
|
// Насколько боец может отстать от своего слота, прежде чем бросит всё и
|
|
|
|
|
|
// вернётся в строй. Ниже 2 отряд перестаёт разбредаться по огневым
|
|
|
|
|
|
// позициям вообще, выше 4 — снова застревает цепочкой (sim/squad.cpp).
|
|
|
|
|
|
float straggleDist = 2.8f;
|
|
|
|
|
|
// Когда включается поле путей. Не по расстоянию до цели, а по факту упора:
|
|
|
|
|
|
// боец хочет идти и не едет. Расстоянием этот порог не задать — на подходе
|
|
|
|
|
|
// к проходу цель не видна никому, и по «не видно» все пятеро получили бы
|
|
|
|
|
|
// одну путевую точку и вошли бы в горловину шеренгой (критерий 11.6).
|
|
|
|
|
|
float navStuckTime = 0.35f; // секунд упора до обхода
|
|
|
|
|
|
float navStuckSpeed = 0.25f; // «не едет» — это доля от скорости хода
|
|
|
|
|
|
float navGoalReached = 0.35f; // ближе этого цель считается достигнутой
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
float wSeparation = 1.0f;
|
|
|
|
|
|
float wLaneAvoid = 2.4f; // строго выше wSeparation (спек 6.7)
|
|
|
|
|
|
float wWallAvoid = 1.2f;
|
|
|
|
|
|
float wallLookahead = 0.8f;
|
|
|
|
|
|
float wallSlide = 1.4f; // касательная составляющая обхода стен
|
|
|
|
|
|
float arriveRadius = 0.18f;
|
|
|
|
|
|
float turnRateDeg = 300.0f; // ствол тяжёлый
|
|
|
|
|
|
float wTargetPush = 1.0f; // мишень — тело, а не пустое место
|
|
|
|
|
|
float targetPushGap = 0.10f; // зазор поверх суммы радиусов
|
|
|
|
|
|
|
|
|
|
|
|
// оружие (это ПАРАМЕТРЫ КАРАБИНА — оружия по умолчанию, на котором меряются
|
|
|
|
|
|
// критерии приёмки. Остальные стволы — записи каталога sim/weapon_defs.h)
|
|
|
|
|
|
float weaponRange = 7.2f; // -20% от исходных 9.0
|
|
|
|
|
|
float fireInterval = 0.35f;
|
|
|
|
|
|
float laneHalfWidth = 0.10f;
|
|
|
|
|
|
float damage = 12.0f;
|
|
|
|
|
|
float safetyMargin = 0.15f;
|
|
|
|
|
|
float muzzleOffset = 0.3f;
|
|
|
|
|
|
float bulletSpeed = 22.0f;
|
|
|
|
|
|
float bulletRadius = 0.04f;
|
|
|
|
|
|
float bulletKnock = 0.35f; // импульс мишени от попадания пули
|
|
|
|
|
|
float aimToleranceDeg = 5.0f; // ствол должен быть доведён до цели
|
|
|
|
|
|
|
|
|
|
|
|
// --- разброс -------------------------------------------------------------
|
|
|
|
|
|
// Стоя ствол почти не гуляет, на полном ходу гуляет сильно, очередь его
|
|
|
|
|
|
// раскрывает. Тот же конус проверяется на своих перед спуском: правило 6.3
|
|
|
|
|
|
// не ослаблено, оно просто считает по конусу вместо отрезка.
|
|
|
|
|
|
float spreadStandDeg = 0.9f;
|
|
|
|
|
|
float spreadMoveDeg = 4.2f;
|
|
|
|
|
|
float spreadRecoilDeg = 0.55f; // прибавка за выстрел
|
|
|
|
|
|
float spreadRecoilMax = 3.0f;
|
|
|
|
|
|
float spreadRecoilDecay = 3.2f; // градусов/сек оседания
|
|
|
|
|
|
float spreadScale = 1.0f; // общий множитель, ручка для замеров
|
|
|
|
|
|
|
|
|
|
|
|
// --- перезарядка ---------------------------------------------------------
|
|
|
|
|
|
float magazineRifle = 24.0f; // патронов в магазине карабина
|
|
|
|
|
|
float reloadRifle = 1.9f; // сек
|
|
|
|
|
|
// Запас на манёвр союзника за время полёта пули (доля от moveSpeed).
|
|
|
|
|
|
// 0 = верить экстраполяции скорости буквально.
|
|
|
|
|
|
float shotPredictSlack = 0.3f;
|
|
|
|
|
|
|
|
|
|
|
|
// --- ближний бой ---------------------------------------------------------
|
|
|
|
|
|
// Три фазы: замах видно, урон живёт в коротком окне, после удара боец открыт.
|
|
|
|
|
|
float meleeReach = 1.40f; // от центра тела до конца замаха
|
|
|
|
|
|
float meleeArcDeg = 100.0f; // ширина сектора удара
|
|
|
|
|
|
float meleeWindup = 0.17f; // замах — телеграф
|
|
|
|
|
|
float meleeStrike = 0.13f; // активное окно урона
|
|
|
|
|
|
float meleeRecover = 0.24f; // после удара
|
|
|
|
|
|
float meleeInterval = 0.60f; // между началами замахов
|
|
|
|
|
|
float meleeDamage = 130.0f; // 4 удара на мишень против 42 пуль
|
|
|
|
|
|
float meleeBladeDeg = 34.0f; // ширина полосы самого лезвия
|
|
|
|
|
|
float meleeAimToleranceDeg = 30.0f; // допуск разворота на старте замаха
|
|
|
|
|
|
float meleeAllyMargin = 0.14f; // запас к телу союзника в секторе
|
|
|
|
|
|
// На сколько взмахов вперёд смотреть на союзника при старте замаха
|
|
|
|
|
|
// (в долях windup+strike). 0 = верить только текущему положению.
|
|
|
|
|
|
float meleeStartPredict = 1.0f;
|
|
|
|
|
|
// Доля бокового размаха сектора, которую клинок резервирует в реестре линий.
|
|
|
|
|
|
// 1.0 = вся дуга; тогда своим нечем дышать. 0 = резерв шириной с отрезок.
|
|
|
|
|
|
float meleeReserveFrac = 0.55f;
|
|
|
|
|
|
float meleeGap = 0.12f; // зазор при подходе вплотную
|
|
|
|
|
|
float meleeLungeSpeed = 3.4f; // рывок в момент удара, юнитов/сек
|
|
|
|
|
|
float meleeLungeAccel = 120.0f; // рывок обязан успеть за 0.13 с
|
|
|
|
|
|
float meleeWindupScale = 0.20f; // множитель скорости на замахе
|
|
|
|
|
|
float meleeRecoverScale= 0.55f;
|
|
|
|
|
|
float meleeKnockback = 2.4f; // импульс мишени, юнитов/сек
|
|
|
|
|
|
// СВОЙ круг бойца: его отпускают от диска дальше, чем стрелка.
|
|
|
|
|
|
float meleeCohesionRadius = 5.6f;
|
|
|
|
|
|
float meleeSeekRadius = 7.0f; // дальше этого боец за целью не идёт вообще
|
|
|
|
|
|
// Штраф за уже занятую цель, в юнитах дистанции. Двое с клинками, вцепившиеся
|
|
|
|
|
|
// в одного, половину взмахов обрывают друг об друга — пусть расходятся.
|
|
|
|
|
|
float meleeClaimPenalty = 3.0f;
|
|
|
|
|
|
// Сколько держится связка после удара. Не успел продолжить — начинаешь
|
|
|
|
|
|
// серию заново с первого удара.
|
|
|
|
|
|
float meleeComboWindow = 1.10f;
|
|
|
|
|
|
|
Tile2D: движок отдельным продуктом, игра — проектом в нём (M11-M20)
Десять вех одним состоянием: разделять их пофайлово нечем, потому что
src/game.cpp менялся почти в каждой. Собирается начисто без предупреждений,
все проверки проходят.
ДВИЖОК (engine/, цель tile2d)
Каталог свой, и решение «это движок» принимается тем, куда положен файл.
Компилятор нашёл три зависимости движка от игры, и все сняты: размеры кадра
и карты уехали в engine/config.h, потолок населения этажа стал своим
(MAX_LEVEL_SPAWNS против игровой MAX_TARGETS), запасная точка старта стала
центром карты вместо позиции отряда.
engine/projection.* единственное место, где движок знает про изометрию;
видов сразу два — абстракция с одной реализацией ничем
не проверена и обычно оказывается неправильной
engine/project.* манифест проекта: пути, вид, кривая глубины
engine/app.* окно, кадр, fixed timestep — вместо двух копий цикла
engine/tilemap_draw движок научился показывать свою карту
РЕДАКТОР (editor/, цель tile2d_editor)
Открывает любой проект и ни одной игры по имени не знает.
--tool одиннадцать безоконных команд: комнаты, каталог, кривая, сборка
этажа, ASCII-карта, проверка набора. Правку делают файлами,
последствия читают командой (docs/19-agent.md)
терминал снизу: те же инструменты, git, сборка и запросы к claude/codex,
открытый проект уходит потомкам через TILE2D_PROJECT
ПРИМЕР (samples/walk, цель tile2d_walk)
Второй потребитель движка. Игра и редактор самостоятельность движка
проверить не могут: редактор мира не рисует, а игра — то место, куда общий
код и уезжает незаметно.
ИГРА (src/)
Экстракшен: вес и два порога переноски, конечный боезапас, расходники
применяются сами, общая сумка отряда, трата опыта на улучшения.
Каждое из правил проверяется прогоном, а не глазами, и проверки поймали:
вес дважды не связывал ничего (мерил по бинтам прогона, а не по добыче);
боец с клинком не тратил найденные патроны при запасном стволе в сумке;
массив виджетов экрана переполнился и молча съел хвост схрона; проверка
улучшений сама ломала забег, вызывая NewRun посреди спуска.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 01:07:19 +07:00
|
|
|
|
// --- живучесть отряда и тварей -------------------------------------------
|
|
|
|
|
|
// У мишеней приёмки эти числа не работают: DUMMY не бьёт и не ходит,
|
|
|
|
|
|
// поэтому --accept от них не зависит вообще.
|
|
|
|
|
|
float agentHP = 100.0f; // запас бойца до брони и оберегов
|
|
|
|
|
|
float enemyDamageScale = 1.0f; // общая сложность: во сколько раз больно
|
|
|
|
|
|
float enemyRespawn = 16.0f; // через сколько тварь возвращается
|
|
|
|
|
|
float enemyLightPower = 0.14f; // как ярко тлеют глаза в темноте
|
|
|
|
|
|
// Сколько цель «светится» после попадания. Единственный признак, по которому
|
|
|
|
|
|
// в темноте видно, что отряд по кому-то реально работает, а не мажет.
|
|
|
|
|
|
float targetHurtTime = 0.22f;
|
|
|
|
|
|
|
|
|
|
|
|
// --- расход ------------------------------------------------------------------
|
|
|
|
|
|
// Патроны на вылазку. Не «на этаж»: запас едет вниз вместе с отрядом, и
|
|
|
|
|
|
// решение «ещё глубже или наверх» упирается именно в него.
|
|
|
|
|
|
float startAmmo = 120.0f;
|
|
|
|
|
|
// При какой доле здоровья боец сам возьмётся за бинт и на каком удалении
|
|
|
|
|
|
// от ближайшей твари это ему позволено. Лечиться в бою нельзя: перевязка
|
|
|
|
|
|
// посреди схватки обесценила бы урон.
|
|
|
|
|
|
float healAtFrac = 0.55f;
|
|
|
|
|
|
float healSafeDist = 9.0f;
|
|
|
|
|
|
|
|
|
|
|
|
// --- спуск, находки, опыт ---------------------------------------------------
|
|
|
|
|
|
// Ничего из этого не работает в песочнице приёмки: там нет ни лута, ни
|
|
|
|
|
|
// спуска, и `--accept` этих чисел не касается.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Радиус подбора заметно больше тела бойца: подбор пассивный, и промазать
|
|
|
|
|
|
// мимо лежащего предмета, пройдя вплотную, игрок не должен — он ведь по
|
|
|
|
|
|
// нему не кликал.
|
|
|
|
|
|
float lootPickupRadius = 0.65f;
|
|
|
|
|
|
// На каком расстоянии от точки спуска отряд считается дошедшим. Меряется
|
|
|
|
|
|
// по ЯКОРЮ, а не по бойцам: спуск — это решение игрока, а не то, что
|
|
|
|
|
|
// случается от того, что один боец забрёл не туда.
|
|
|
|
|
|
float descendRadius = 1.10f;
|
|
|
|
|
|
// Сколько опыта даёт единица опасности убитого. Опыт этажа сверх этого
|
|
|
|
|
|
// множится на xpScale из кривой глубины (engine/progression.h).
|
|
|
|
|
|
float xpPerDanger = 10.0f;
|
|
|
|
|
|
|
|
|
|
|
|
// --- разрушаемая обстановка ------------------------------------------------
|
|
|
|
|
|
// Радиус, в котором ОДНО ПРИСУТСТВИЕ живой твари запрещает бойцу работать
|
|
|
|
|
|
// по хламу. Намеренно шире дальности любого ствола: привязать его к дальности
|
|
|
|
|
|
// значило бы «пока не достаю — стреляю по ящику», и боец встречал бы
|
|
|
|
|
|
// подошедшую тварь с пустым магазином. Ящик ломают, когда вокруг ЧИСТО.
|
|
|
|
|
|
float propYieldRadius = 15.0f;
|
|
|
|
|
|
|
|
|
|
|
|
// --- атаки тварей ---------------------------------------------------------
|
|
|
|
|
|
// Форма каждой атаки (сектор, радиус круга, длина рывка) — свойство ВИДА и
|
|
|
|
|
|
// живёт в EnemyDef (sim/target.cpp). Здесь только то, что общее для всех:
|
|
|
|
|
|
// именно этими двумя ручками правится «успеваю ли я среагировать».
|
|
|
|
|
|
//
|
|
|
|
|
|
// enemyWindupScale — множитель ВСЕХ телеграфов сразу. Опускать его ниже
|
|
|
|
|
|
// ~0.5 нечестно: замах становится короче времени реакции, и смерть бойца
|
|
|
|
|
|
// снова выглядит случайной.
|
|
|
|
|
|
float enemyWindupScale = 1.0f;
|
|
|
|
|
|
// Сколько захват держится без подтверждения от твари. Тварь каждый шаг
|
|
|
|
|
|
// продлевает его на эту величину; убили её — боец освобождается сам, и
|
|
|
|
|
|
// ни bullet.cpp, ни клинку не приходится знать про захват вообще.
|
|
|
|
|
|
float enemyGrabHold = 0.15f;
|
|
|
|
|
|
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
// мишени
|
|
|
|
|
|
float targetHP = 500.0f;
|
|
|
|
|
|
float targetRespawn = 3.0f;
|
|
|
|
|
|
float targetRadius = 0.34f;
|
|
|
|
|
|
float targetKnockDamp = 9.0f; // гашение импульса от удара
|
|
|
|
|
|
float targetReturnRate= 5.0f; // возврат на своё место
|
|
|
|
|
|
// Общий множитель сопротивления отбросу. Само сопротивление у каждой мишени
|
|
|
|
|
|
// своё (sim/target.cpp): тяжёлую клинком не сдвинуть, лёгкая улетает.
|
|
|
|
|
|
float knockResistScale = 1.0f;
|
|
|
|
|
|
|
|
|
|
|
|
// отряд
|
|
|
|
|
|
float holdDelay = 0.22f; // сек без ввода -> HOLDING
|
|
|
|
|
|
float cohesionRadius = 2.1f; // стрелки держатся кучнее
|
|
|
|
|
|
float minEngageDist = 1.6f; // отмасштабировано под дальность 7.2
|
|
|
|
|
|
float formationSpacing = 1.0f; // строй плотнее
|
|
|
|
|
|
float cameraSmooth = 9.0f;
|
|
|
|
|
|
float anchorRadius = 0.3f; // якорь тоже скользит вдоль стен
|
|
|
|
|
|
// Диск отзывчивее бойцов: команда читается мгновенно, а догоняют её ногами.
|
|
|
|
|
|
float anchorSpeed = 3.3f;
|
|
|
|
|
|
float anchorSpeedFast = 4.8f;
|
|
|
|
|
|
float anchorAccel = 20.0f; // разгон/торможение курсора, юнитов/сек^2
|
|
|
|
|
|
// Поводок УПРУГИЙ, а не жёсткий: без него быстрый диск просто отрывается от
|
|
|
|
|
|
// отряда и теряет смысл, но жёсткий упор намертво фиксирует дистанцию, и по
|
|
|
|
|
|
// ней уже не отличить «подправил направление» от «тяну изо всех сил».
|
|
|
|
|
|
// anchorLeash — мягкая граница: до неё курсор идёт свободно;
|
|
|
|
|
|
// anchorLeashMax — жёсткий предел: страховка, до неё обычно не доходит;
|
|
|
|
|
|
// leashDrag — насколько тяжелеет курсор между границами (0..1).
|
Пистолет не пропадает, эвакуация откуда угодно, кнопки в HUD (M36)
БАГ С ПИСТОЛЕТОМ - на самом деле ДВА бага.
Первый: экран отряда правит ЖИВОЙ МИР, а вылазка собирает бойцов заново
из профиля (NewRun пересоздаёт ecs::World и раздаёт снаряжение через
ApplyProfileLoadout). Всё, переложенное в меню, стиралось на старте, а
вещь, вынутая из схрона, исчезала вместе с миром - схрон её уже отдал.
Теперь всякая правка снаряжения пишется в профиль сразу.
Второй, рядом: перетаскивание мышью НЕ РАБОТАЛО ВООБЩЕ. Флаг dropOk
нигде не выставлялся в true - только сбрасывался, - и сброс молча
отменялся. Единственным рабочим способом переодеться оставался ENTER.
Теперь флаг считается каждый кадр, и ячейка честно красится в "нельзя".
Заодно: вне забега сумка не принимает вещей. Она принадлежит вылазке и
очищается при сборке этажа; вещь, положенная в неё из меню, исчезла бы
так же молча.
Проверка ловит именно цепочку "поменял в меню -> начал вылазку -> вышел
с этим": сам обмен работал и раньше, пропадал он на старте.
сборы: в профиле после обмена PISTOL, на этаже PISTOL
ТВАРИ НЕ ВОСКРЕСАЮТ. Убитая в этой вылазке убита насовсем: с возвратом
любая расчищенная комната через полминуты снова была полна, и "пробиться
и уйти" не отличалось от "молотить бесконечную очередь". Исключение одно
- аура плакальщика, это его роль.
ЭВАКУАЦИЯ С ЛЮБОГО ЭТАЖА И ИЗ ЛЮБОЙ ТОЧКИ. Раньше уйти можно было только
стоя на кольце: отряд, решивший не рисковать, всё равно шёл через этаж и
умирал ровно там, где игрок уже принял решение. Чекпоинты теперь значат
только то, чем должны быть: откуда можно НАЧАТЬ следующую вылазку.
ОТЗЫВЧИВОСТЬ СТРОЯ. Отряд ходил 1.75 при курсоре 3.3 - он всегда
отставал, и управление ощущалось как "тяну за верёвку". Стало 2.6,
поводок короче (2.5 против 3.0), доворот строя вдвое резче.
Ориентация строя теперь ПОВОРАЧИВАЕТСЯ, а не щёлкает: раньше слоты клина
перепрыгивали через весь отряд на каждом повороте, и бойцы бросали дорогу
ради перестроения. В тесноте доворот почти мгновенный - там строй и так
колонна, а колонна, смотрящая в прошлую сторону, упирается в стену.
Автопилот поймал, что первая версия правок сделала хуже: при быстром
наборе натяжения (5.2) отряд уходил в вечный спринт, а спринт растягивает
строй и выключает огонь. Порог спринта поднят до 0.95, набор до 3.4 -
приёмка 11.4 держится (0.13 с при пороге 0.30).
КНОПКИ В HUD. Слева внизу четыре: SQUAD, TREE, WORK, MENU. Экраны
открывались только клавишами, о которых игра нигде не говорила, - без
кнопки мастерская и дерево не открываются никогда.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 09:24:10 +07:00
|
|
|
|
float anchorLeash = 2.5f;
|
|
|
|
|
|
float anchorLeashMax = 4.2f;
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
float leashDrag = 0.75f; // на жёсткой границе остаётся 25% скорости
|
|
|
|
|
|
|
|
|
|
|
|
// --- натяжение поводка (см. docs/05-squad-ai.md) --------------------------
|
|
|
|
|
|
// Курсор упёрся в поводок, а игрок продолжает давить -> он хочет идти
|
|
|
|
|
|
// быстрее, чем отряд шагает. Это и есть команда «бегом»: отряд ускоряется,
|
|
|
|
|
|
// строй растягивается, а на верхней трети шкалы огонь прекращается вовсе.
|
|
|
|
|
|
float urgeStartFrac = 0.50f; // с какой доли поводка начинается натяжение
|
Пистолет не пропадает, эвакуация откуда угодно, кнопки в HUD (M36)
БАГ С ПИСТОЛЕТОМ - на самом деле ДВА бага.
Первый: экран отряда правит ЖИВОЙ МИР, а вылазка собирает бойцов заново
из профиля (NewRun пересоздаёт ecs::World и раздаёт снаряжение через
ApplyProfileLoadout). Всё, переложенное в меню, стиралось на старте, а
вещь, вынутая из схрона, исчезала вместе с миром - схрон её уже отдал.
Теперь всякая правка снаряжения пишется в профиль сразу.
Второй, рядом: перетаскивание мышью НЕ РАБОТАЛО ВООБЩЕ. Флаг dropOk
нигде не выставлялся в true - только сбрасывался, - и сброс молча
отменялся. Единственным рабочим способом переодеться оставался ENTER.
Теперь флаг считается каждый кадр, и ячейка честно красится в "нельзя".
Заодно: вне забега сумка не принимает вещей. Она принадлежит вылазке и
очищается при сборке этажа; вещь, положенная в неё из меню, исчезла бы
так же молча.
Проверка ловит именно цепочку "поменял в меню -> начал вылазку -> вышел
с этим": сам обмен работал и раньше, пропадал он на старте.
сборы: в профиле после обмена PISTOL, на этаже PISTOL
ТВАРИ НЕ ВОСКРЕСАЮТ. Убитая в этой вылазке убита насовсем: с возвратом
любая расчищенная комната через полминуты снова была полна, и "пробиться
и уйти" не отличалось от "молотить бесконечную очередь". Исключение одно
- аура плакальщика, это его роль.
ЭВАКУАЦИЯ С ЛЮБОГО ЭТАЖА И ИЗ ЛЮБОЙ ТОЧКИ. Раньше уйти можно было только
стоя на кольце: отряд, решивший не рисковать, всё равно шёл через этаж и
умирал ровно там, где игрок уже принял решение. Чекпоинты теперь значат
только то, чем должны быть: откуда можно НАЧАТЬ следующую вылазку.
ОТЗЫВЧИВОСТЬ СТРОЯ. Отряд ходил 1.75 при курсоре 3.3 - он всегда
отставал, и управление ощущалось как "тяну за верёвку". Стало 2.6,
поводок короче (2.5 против 3.0), доворот строя вдвое резче.
Ориентация строя теперь ПОВОРАЧИВАЕТСЯ, а не щёлкает: раньше слоты клина
перепрыгивали через весь отряд на каждом повороте, и бойцы бросали дорогу
ради перестроения. В тесноте доворот почти мгновенный - там строй и так
колонна, а колонна, смотрящая в прошлую сторону, упирается в стену.
Автопилот поймал, что первая версия правок сделала хуже: при быстром
наборе натяжения (5.2) отряд уходил в вечный спринт, а спринт растягивает
строй и выключает огонь. Порог спринта поднят до 0.95, набор до 3.4 -
приёмка 11.4 держится (0.13 с при пороге 0.30).
КНОПКИ В HUD. Слева внизу четыре: SQUAD, TREE, WORK, MENU. Экраны
открывались только клавишами, о которых игра нигде не говорила, - без
кнопки мастерская и дерево не открываются никогда.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 09:24:10 +07:00
|
|
|
|
// Натяжение набирается быстро, но НЕ мгновенно, и порог спринта поднят.
|
|
|
|
|
|
// При urgeAttack 5.2 отряд уходил в спринт от любого движения курсора, а
|
|
|
|
|
|
// спринт растягивает строй и выключает огонь: в коридорах это выглядело как
|
|
|
|
|
|
// «отряд застрял». Поймано автопилотом (--play): первый этаж переставал
|
|
|
|
|
|
// проходиться вообще.
|
|
|
|
|
|
float urgeAttack = 3.4f; // как быстро натяжение набирается
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
float urgeRelease = 3.4f; // и как быстро отпускает
|
Пистолет не пропадает, эвакуация откуда угодно, кнопки в HUD (M36)
БАГ С ПИСТОЛЕТОМ - на самом деле ДВА бага.
Первый: экран отряда правит ЖИВОЙ МИР, а вылазка собирает бойцов заново
из профиля (NewRun пересоздаёт ecs::World и раздаёт снаряжение через
ApplyProfileLoadout). Всё, переложенное в меню, стиралось на старте, а
вещь, вынутая из схрона, исчезала вместе с миром - схрон её уже отдал.
Теперь всякая правка снаряжения пишется в профиль сразу.
Второй, рядом: перетаскивание мышью НЕ РАБОТАЛО ВООБЩЕ. Флаг dropOk
нигде не выставлялся в true - только сбрасывался, - и сброс молча
отменялся. Единственным рабочим способом переодеться оставался ENTER.
Теперь флаг считается каждый кадр, и ячейка честно красится в "нельзя".
Заодно: вне забега сумка не принимает вещей. Она принадлежит вылазке и
очищается при сборке этажа; вещь, положенная в неё из меню, исчезла бы
так же молча.
Проверка ловит именно цепочку "поменял в меню -> начал вылазку -> вышел
с этим": сам обмен работал и раньше, пропадал он на старте.
сборы: в профиле после обмена PISTOL, на этаже PISTOL
ТВАРИ НЕ ВОСКРЕСАЮТ. Убитая в этой вылазке убита насовсем: с возвратом
любая расчищенная комната через полминуты снова была полна, и "пробиться
и уйти" не отличалось от "молотить бесконечную очередь". Исключение одно
- аура плакальщика, это его роль.
ЭВАКУАЦИЯ С ЛЮБОГО ЭТАЖА И ИЗ ЛЮБОЙ ТОЧКИ. Раньше уйти можно было только
стоя на кольце: отряд, решивший не рисковать, всё равно шёл через этаж и
умирал ровно там, где игрок уже принял решение. Чекпоинты теперь значат
только то, чем должны быть: откуда можно НАЧАТЬ следующую вылазку.
ОТЗЫВЧИВОСТЬ СТРОЯ. Отряд ходил 1.75 при курсоре 3.3 - он всегда
отставал, и управление ощущалось как "тяну за верёвку". Стало 2.6,
поводок короче (2.5 против 3.0), доворот строя вдвое резче.
Ориентация строя теперь ПОВОРАЧИВАЕТСЯ, а не щёлкает: раньше слоты клина
перепрыгивали через весь отряд на каждом повороте, и бойцы бросали дорогу
ради перестроения. В тесноте доворот почти мгновенный - там строй и так
колонна, а колонна, смотрящая в прошлую сторону, упирается в стену.
Автопилот поймал, что первая версия правок сделала хуже: при быстром
наборе натяжения (5.2) отряд уходил в вечный спринт, а спринт растягивает
строй и выключает огонь. Порог спринта поднят до 0.95, набор до 3.4 -
приёмка 11.4 держится (0.13 с при пороге 0.30).
КНОПКИ В HUD. Слева внизу четыре: SQUAD, TREE, WORK, MENU. Экраны
открывались только клавишами, о которых игра нигде не говорила, - без
кнопки мастерская и дерево не открываются никогда.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 09:24:10 +07:00
|
|
|
|
float sprintAt = 0.95f; // выше — спринт: бежим, не атакуем.
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
// Порог высокий намеренно: при удержании направления натяжение садится
|
|
|
|
|
|
// примерно на 0.54, то есть отряд идёт быстрым шагом И ПРОДОЛЖАЕТ работать
|
|
|
|
|
|
// по целям. Спринт остаётся тем, чем должен быть, — отдельным решением
|
|
|
|
|
|
// (Shift) или следствием того, что курсор реально уехал.
|
|
|
|
|
|
|
|
|
|
|
|
// --- дыхание строя --------------------------------------------------------
|
|
|
|
|
|
float holdSpacing = 0.90f; // на стоянке отряд собирается
|
Запуск грузит проект; автопилот --play; строй сжимается в тесноте (M31)
ГЛАВНОЕ. "Дольше пяти секунд не продержаться на первом уровне" - это был
не баланс, а запуск. По умолчанию useRooms был false: обычный запуск exe
собирал ЗАХАРДКОЖЕННУЮ карту прототипа со старой расстановкой тварей и
высаживал туда ОДНОГО оперативника с ножом (размер отряда берётся из
профиля). Игрок попадал не на первый этаж игры, а вообще не в игру.
Теперь проект подключается по умолчанию, а старая карта осталась под
флагом --no-rooms.
--PLAY. Новый прогон: автопилот ведёт отряд к спуску и дерётся по дороге.
Ни --descend, ни --wall не меряли ПРОХОЖДЕНИЕ: первый собирает этажи и ни
с кем не воюет, второй ставит отряд в толпу и запрещает двигаться. Жалоба
же была именно про проход этажа.
Автопилот ходит по полю расстояний до спуска (заливка по проходимым
клеткам), целится на несколько клеток вперёд по спуску поля и правит
курсором - тем же вводом, каким играет человек. Три версии до рабочей:
по прямой упирался в первую стену, по восьми соседям лез в диагональную
щель между углами (тело круглое, не пролезает), по соседней клетке дёргался
на каждой границе.
Одиночка проходит три этажа и гибнет на четвёртом:
1 тварей 3, убил 0, 100% здоровья, света 244 дошёл (78 с)
2 тварей 6, убил 1, 100%, света 155 дошёл (44 с)
3 тварей 6, убил 0, 65%, света 8 дошёл (74 с)
4 тварей 9, убил 5, ВАЙП (37 с)
Свет кончается к третьему этажу: расчёт "заряда на четыре пути до спуска"
считал прямые пути, а живой отряд ходит петлями.
СТРОЙ В ТЕСНОТЕ. Клин шириной в пять тел не проходит в дверь шириной в
клетку: бойцы упираются друг в друга, расталкивание держит их на месте,
отряд встаёт насмерть. Теперь строй сам сжимается в колонну, когда вокруг
тел мало свободных клеток.
ЧЕСТНО: это НЕ вылечило проход пятёрки до конца. Автопилот с полным
отрядом всё ещё застревает на первом этаже там, где одиночка проходит, и
причина не в ширине строя (проверено сжатием в точку и отрядом без
клинков). Похоже, нужна нормальная проводка строя по проходам - это
отдельная веха, а не патч в конце. Оставляю с числами, а не с обещанием.
--accept PASS, все семь проверок зелёные.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 17:20:21 +07:00
|
|
|
|
// Строй в тесноте: во сколько раз сжимается вбок и вытягивается вдоль хода.
|
|
|
|
|
|
// 0.28 — это колонна: пять тел проходят в дверь шириной в одну клетку.
|
|
|
|
|
|
float tightSpacing = 0.28f;
|
|
|
|
|
|
float tightStretch = 1.60f;
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
float sprintSpacing = 1.30f; // в беге расходится
|
|
|
|
|
|
float sprintStretch = 1.55f; // и вытягивается вдоль движения
|
|
|
|
|
|
float formMorphRate = 4.0f; // скорость перестроения, 1/сек
|
|
|
|
|
|
|
|
|
|
|
|
// решатель огневых позиций (6.5)
|
|
|
|
|
|
float solveInterval = 0.25f;
|
|
|
|
|
|
float commitTime = 0.6f;
|
|
|
|
|
|
float hysteresis = 15.0f;
|
|
|
|
|
|
float ring1 = 0.7f; // кольца решателя сжаты вслед за строем
|
|
|
|
|
|
float ring2 = 1.4f;
|
|
|
|
|
|
float ring3 = 2.1f;
|
|
|
|
|
|
float candMinAlly = 0.60f; // минимум до другого агента (было МЕНЬШЕ диаметра тела)
|
|
|
|
|
|
float sLaneClear = 100.0f;
|
|
|
|
|
|
float sCoverage = 25.0f;
|
|
|
|
|
|
float sBlockAlly = 35.0f;
|
|
|
|
|
|
float sDistCost = 9.0f;
|
|
|
|
|
|
float sCohesionCost = 24.0f; // отбегать от отряда дорого
|
|
|
|
|
|
float sTooClose = 60.0f;
|
|
|
|
|
|
// Жёсткая граница огневой позиции: дальше cohesionRadius + postLeash от диска
|
|
|
|
|
|
// кандидат просто отбраковывается. Одной цены мало — линия огня стоит 100
|
|
|
|
|
|
// очков, и за неё решатель готов уйти сколько угодно далеко. А отряд, из
|
|
|
|
|
|
// которого стрелок ушёл на пять метров, отрядом быть перестаёт.
|
|
|
|
|
|
float postLeash = 1.0f;
|
|
|
|
|
|
|
|
|
|
|
|
// расступание из чужих линий (6.6)
|
|
|
|
|
|
float laneAvoidStrength = 7.0f;
|
|
|
|
|
|
// Ширина РЕЗЕРВА линии относительно порога «тело в линии» из 6.3.
|
|
|
|
|
|
// >1 означает: агента отжимает ДО того, как он влез в чужой сектор.
|
|
|
|
|
|
// Правило стрельбы (6.3) от этого не меняется — см. docs/08-decisions.md.
|
|
|
|
|
|
float laneReserveScale = 2.0f;
|
|
|
|
|
|
|
|
|
|
|
|
// ручное прицеливание (6.8)
|
|
|
|
|
|
float aimConeDeg = 20.0f;
|
|
|
|
|
|
float aimTurnRateDeg = 110.0f; // прицел ведётся тяжелее
|
|
|
|
|
|
|
|
|
|
|
|
// метрики
|
|
|
|
|
|
float uptimeWindow = 10.0f; // окно FIRE UPTIME, сек
|
|
|
|
|
|
float settleQuorum = 4.0f; // сколько агентов нужно для SETTLE TIME
|
|
|
|
|
|
|
|
|
|
|
|
// --- свет и туман войны (render-only, на симуляцию не влияет) -----------
|
|
|
|
|
|
float sightRadius = 7.6f; // где обзор гаснет полностью
|
|
|
|
|
|
float sightFullRadius = 2.2f; // дальше сразу начинается спад: свет должен таять
|
|
|
|
|
|
float sightConeDeg = 100.0f; // конус взгляда агента
|
|
|
|
|
|
float sightConeSoftDeg = 22.0f; // мягкий край конуса
|
|
|
|
|
|
float sightPersonalRadius= 1.9f; // круговой «личный» ореол вокруг агента
|
|
|
|
|
|
float sightBrightness = 0.86f; // яркость видимого; остаток отдан вспышкам
|
|
|
|
|
|
float fogMemoryLevel = 0.13f; // как тускло помнится уже увиденное
|
|
|
|
|
|
float fogWallBleed = 0.92f; // затухание света вглубь блока стен
|
|
|
|
|
|
float fogFadeRate = 9.0f; // сглаживание во времени, чтобы не щёлкало
|
|
|
|
|
|
float fogEntityReveal = 0.30f; // ниже этого света сущность не показывается
|
|
|
|
|
|
float eyeRevealLight = 0.05f; // глаз мишени тлеет почти в полной темноте
|
|
|
|
|
|
float eyeRadiusPx = 3.0f;
|
|
|
|
|
|
float eyeLiftPx = 11.0f; // на уровне «головы» капсулы
|
|
|
|
|
|
float agentMinLight = 0.34f; // свои читаемы всегда: игрок обязан их различать
|
|
|
|
|
|
|
|
|
|
|
|
// вспышки выстрелов как источники света
|
|
|
|
|
|
float flashPower = 0.55f;
|
|
|
|
|
|
float flashRadius = 4.0f;
|
|
|
|
|
|
float flashTime = 0.11f;
|
|
|
|
|
|
|
|
|
|
|
|
// рампа освещения (меняет саму таблицу — требует перепечь)
|
|
|
|
|
|
float lightGamma = 1.45f;
|
|
|
|
|
|
float lightMaxGain = 1.25f;
|
|
|
|
|
|
|
|
|
|
|
|
// --- эффекты стрельбы ---------------------------------------------------
|
|
|
|
|
|
float fxIntensity = 1.0f;
|
|
|
|
|
|
float tracerLength = 1.30f; // длина светящегося штриха, юнитов мира
|
|
|
|
|
|
float bulletLiftPx = 8.0f; // пуля летит на уровне груди, не по земле
|
|
|
|
|
|
float muzzleTime = 0.07f;
|
|
|
|
|
|
float muzzleRadiusPx = 5.0f;
|
|
|
|
|
|
float sparkTime = 0.16f;
|
|
|
|
|
|
float sparkRadiusPx = 4.0f;
|
|
|
|
|
|
float bladeLiftPx = 9.0f; // дуга метёт на уровне груди, не по земле
|
|
|
|
|
|
float bladeTrailFrac = 0.80f; // какая доля пройденной дуги ещё светится
|
|
|
|
|
|
float meleeShake = 1.4f; // отдача камеры на попадание клинком
|
|
|
|
|
|
|
|
|
|
|
|
// --- напряжение и постобработка -----------------------------------------
|
|
|
|
|
|
float dreadRadius = 7.0f; // с какой дистанции мишень начинает пугать
|
|
|
|
|
|
float dreadAttack = 6.0f; // страх набегает быстро
|
|
|
|
|
|
float dreadRelease = 0.5f; // отпускает медленно
|
|
|
|
|
|
float vigStartCalm = 0.42f; // где начинается виньетка
|
|
|
|
|
|
float vigStartTense = 0.20f;
|
|
|
|
|
|
float vigDepthCalm = 0.55f; // насколько темнеет край
|
|
|
|
|
|
float vigDepthTense = 0.85f;
|
|
|
|
|
|
float grainCalm = 6.0f; // амплитуда зерна (макс 127)
|
|
|
|
|
|
float grainTense = 16.0f;
|
|
|
|
|
|
float heartBpmCalm = 58.0f;
|
|
|
|
|
|
float heartBpmTense = 132.0f;
|
|
|
|
|
|
float heartAmp = 0.10f; // насколько сердцебиение давит картинку
|
|
|
|
|
|
float shotFlash = 0.12f; // подсветка кадра на выстрел
|
|
|
|
|
|
float shakePerShot = 0.55f; // отдача камеры, пикселей на выстрел
|
|
|
|
|
|
float shakeMax = 3.0f;
|
|
|
|
|
|
float shakeDecay = 9.0f;
|
|
|
|
|
|
|
|
|
|
|
|
// --- кровь (render-only, декали запекаются в спрайты пола) ---------------
|
|
|
|
|
|
float bloodAmount = 1.0f; // общий множитель размера пятен, 0 = выключить
|
|
|
|
|
|
float bloodRadiusHit = 0.26f; // пуля
|
|
|
|
|
|
float bloodRadiusMelee = 0.50f; // клинок
|
|
|
|
|
|
float bloodRadiusKill = 0.78f; // смерть: лужа
|
|
|
|
|
|
float bloodDrops = 5.0f; // сколько брызг летит по направлению удара
|
|
|
|
|
|
float bloodOpacity = 0.92f; // насколько густо пятно перекрывает пол
|
|
|
|
|
|
|
|
|
|
|
|
// --- дымка и пепел в воздухе (render-only) --------------------------------
|
|
|
|
|
|
float hazeAmount = 0.34f; // сила дымки, 0 = выключить
|
|
|
|
|
|
float hazeScale = 1.0f; // крупность клубов
|
|
|
|
|
|
float hazeDrift = 0.05f; // скорость сноса, экранных ячеек/сек
|
|
|
|
|
|
float hazeGate = 24.0f; // ниже этой яркости дымки нет: тьма остаётся тьмой
|
|
|
|
|
|
float ashDensity = 0.75f; // пылинок на тайл
|
|
|
|
|
|
float ashSpeed = 0.30f; // скорость дрейфа пепла
|
|
|
|
|
|
float ashBrightness = 0.60f;
|
|
|
|
|
|
float ashLiftPx = 13.0f; // на какой высоте висит взвесь
|
|
|
|
|
|
|
|
|
|
|
|
// флаги
|
|
|
|
|
|
bool freeFireSecondary = true; // 6.8: стрелять по вторичным целям в движении
|
|
|
|
|
|
bool friendlyFire = true; // 6.9: всегда true, тумблер только для проверки
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// --- связка ударов ближнего боя ----------------------------------------------
|
|
|
|
|
|
//
|
|
|
|
|
|
// Чередование «влево-вправо» — это не связка, а метроном: два удара, между
|
|
|
|
|
|
// которыми нет разницы ни на глаз, ни по последствиям. Связка обязана иметь
|
|
|
|
|
|
// ФОРМУ: быстрый вход, злой возврат, тяжёлое завершение.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Множители, а не абсолютные числа: базовые характеристики клинка остаются
|
|
|
|
|
|
// одним набором в Tuning и продолжают крутиться на ходу, а связка их гнёт.
|
|
|
|
|
|
struct MeleeComboStep
|
|
|
|
|
|
{
|
|
|
|
|
|
const char* name;
|
|
|
|
|
|
float arcMul; // ширина сектора
|
|
|
|
|
|
float dmgMul;
|
|
|
|
|
|
float knockMul;
|
|
|
|
|
|
float windupMul; // замах — телеграф; у тяжёлого удара он длиннее
|
|
|
|
|
|
float strikeMul; // активное окно
|
|
|
|
|
|
float recoverMul; // и расплата после
|
|
|
|
|
|
float intervalMul; // пауза до следующего удара связки
|
|
|
|
|
|
bool flipSide; // менять сторону захода лезвия
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
inline const MeleeComboStep MELEE_COMBO[] = {
|
|
|
|
|
|
// 1. Косой заход. Обычный удар, с него всё начинается.
|
|
|
|
|
|
{"slash", 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, true},
|
|
|
|
|
|
// 2. Возврат с обратной стороны. Быстрее и почти без паузы — именно это
|
|
|
|
|
|
// читается как «связка», а не как два отдельных удара.
|
|
|
|
|
|
{"backhand",0.85f, 1.00f, 0.85f, 0.55f, 0.80f, 0.70f, 0.50f, true},
|
|
|
|
|
|
// 3. Раскрутка. Широкая, тяжёлая, с долгим восстановлением: в тесноте её
|
|
|
|
|
|
// просто не дадут начать — сектор не будет чист, и это правильная цена.
|
|
|
|
|
|
{"spin", 1.70f, 1.75f, 2.30f, 1.50f, 1.40f, 1.70f, 1.35f, false},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
inline constexpr int MELEE_COMBO_STEPS = int(sizeof(MELEE_COMBO) / sizeof(MELEE_COMBO[0]));
|
|
|
|
|
|
|
|
|
|
|
|
inline Tuning g_tune;
|
|
|
|
|
|
|
|
|
|
|
|
// Максимально возможная скорость агента НА ХОДУ. Проверка спуска на время полёта
|
|
|
|
|
|
// пули обязана считать по НЕЙ, иначе рывок союзника не учитывается.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Рывок бойца ближнего боя сюда намеренно не входит: он не «манёвр», а прямой
|
|
|
|
|
|
// отрезок с уже известным направлением, и AllyInterceptsShot проверяет его
|
|
|
|
|
|
// точно — отдельной экстраполяцией (см. ai/firing_solver.cpp).
|
|
|
|
|
|
inline float MaxAgentSpeed() { return std::max(g_tune.moveSpeed, g_tune.moveSpeedFast); }
|
|
|
|
|
|
|
|
|
|
|
|
// --- таблица параметров для подкрутки в оверлее ------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
struct TunableParam
|
|
|
|
|
|
{
|
|
|
|
|
|
const char* name;
|
|
|
|
|
|
float Tuning::* member;
|
|
|
|
|
|
float step;
|
|
|
|
|
|
float minV;
|
|
|
|
|
|
float maxV;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
inline const TunableParam TUNABLES[] = {
|
|
|
|
|
|
{"safetyMargin", &Tuning::safetyMargin, 0.01f, 0.0f, 1.0f},
|
|
|
|
|
|
{"laneHalfWidth", &Tuning::laneHalfWidth, 0.01f, 0.0f, 0.5f},
|
|
|
|
|
|
{"bodyRadius", &Tuning::bodyRadius, 0.01f, 0.1f, 0.8f},
|
|
|
|
|
|
{"wLaneAvoid", &Tuning::wLaneAvoid, 0.1f, 0.0f, 10.0f},
|
|
|
|
|
|
{"laneAvoidStr", &Tuning::laneAvoidStrength,0.25f, 0.0f, 30.0f},
|
|
|
|
|
|
{"laneReserve", &Tuning::laneReserveScale, 0.05f, 1.0f, 4.0f},
|
|
|
|
|
|
{"predictSlack", &Tuning::shotPredictSlack, 0.05f, 0.0f, 2.0f},
|
|
|
|
|
|
{"wSeparation", &Tuning::wSeparation, 0.1f, 0.0f, 10.0f},
|
|
|
|
|
|
{"steerAccel", &Tuning::steerAccel, 1.0f, 2.0f, 60.0f},
|
Пистолет не пропадает, эвакуация откуда угодно, кнопки в HUD (M36)
БАГ С ПИСТОЛЕТОМ - на самом деле ДВА бага.
Первый: экран отряда правит ЖИВОЙ МИР, а вылазка собирает бойцов заново
из профиля (NewRun пересоздаёт ecs::World и раздаёт снаряжение через
ApplyProfileLoadout). Всё, переложенное в меню, стиралось на старте, а
вещь, вынутая из схрона, исчезала вместе с миром - схрон её уже отдал.
Теперь всякая правка снаряжения пишется в профиль сразу.
Второй, рядом: перетаскивание мышью НЕ РАБОТАЛО ВООБЩЕ. Флаг dropOk
нигде не выставлялся в true - только сбрасывался, - и сброс молча
отменялся. Единственным рабочим способом переодеться оставался ENTER.
Теперь флаг считается каждый кадр, и ячейка честно красится в "нельзя".
Заодно: вне забега сумка не принимает вещей. Она принадлежит вылазке и
очищается при сборке этажа; вещь, положенная в неё из меню, исчезла бы
так же молча.
Проверка ловит именно цепочку "поменял в меню -> начал вылазку -> вышел
с этим": сам обмен работал и раньше, пропадал он на старте.
сборы: в профиле после обмена PISTOL, на этаже PISTOL
ТВАРИ НЕ ВОСКРЕСАЮТ. Убитая в этой вылазке убита насовсем: с возвратом
любая расчищенная комната через полминуты снова была полна, и "пробиться
и уйти" не отличалось от "молотить бесконечную очередь". Исключение одно
- аура плакальщика, это его роль.
ЭВАКУАЦИЯ С ЛЮБОГО ЭТАЖА И ИЗ ЛЮБОЙ ТОЧКИ. Раньше уйти можно было только
стоя на кольце: отряд, решивший не рисковать, всё равно шёл через этаж и
умирал ровно там, где игрок уже принял решение. Чекпоинты теперь значат
только то, чем должны быть: откуда можно НАЧАТЬ следующую вылазку.
ОТЗЫВЧИВОСТЬ СТРОЯ. Отряд ходил 1.75 при курсоре 3.3 - он всегда
отставал, и управление ощущалось как "тяну за верёвку". Стало 2.6,
поводок короче (2.5 против 3.0), доворот строя вдвое резче.
Ориентация строя теперь ПОВОРАЧИВАЕТСЯ, а не щёлкает: раньше слоты клина
перепрыгивали через весь отряд на каждом повороте, и бойцы бросали дорогу
ради перестроения. В тесноте доворот почти мгновенный - там строй и так
колонна, а колонна, смотрящая в прошлую сторону, упирается в стену.
Автопилот поймал, что первая версия правок сделала хуже: при быстром
наборе натяжения (5.2) отряд уходил в вечный спринт, а спринт растягивает
строй и выключает огонь. Порог спринта поднят до 0.95, набор до 3.4 -
приёмка 11.4 держится (0.13 с при пороге 0.30).
КНОПКИ В HUD. Слева внизу четыре: SQUAD, TREE, WORK, MENU. Экраны
открывались только клавишами, о которых игра нигде не говорила, - без
кнопки мастерская и дерево не открываются никогда.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 09:24:10 +07:00
|
|
|
|
{"formTurnRate", &Tuning::formTurnRate, 0.5f, 0.5f, 30.0f},
|
Поле путей: отставший возвращается в строй (M37)
Отряд из пяти не проходил первый этаж ни на одном сиде, а одиночка проходил.
Дело было не в ширине коридоров: у бойцов не было поиска пути вообще. Боец,
оказавшийся за стеной от своего слота, seek'ом тянулся В СТЕНУ, обход стены
толкал его обратно, и он замирал в углу навсегда.
Теперь у каждого бойца своё поле путей (BFS от клетки его цели) с натягиванием
верёвки: он берёт самую дальнюю точку пути, которую видит. Включается обход по
двум условиям сразу — боец упёрся (хочет идти и не едет) И потерял строй. Счётчик
упора сбрасывается по факту «цель видно», а не «поехал»: сброс по скорости давал
дребезг, и боец полз шаг за две секунды.
По дороге вскрылись две неверные МЕРКИ:
* критерий 11.6 считал колонной «разброс вдоль больше, чем поперёк» — и потому
награждал отряд, у которого двое навсегда остались по ту сторону стены. Теперь
колонна меряется самим проёмом: разом в горловине не больше двух, и прошли ВСЕ.
* зонд --wall читал «стены не нашлось» (ноль) как «стена на нулевом этаже» и
объявлял пятёрку, не сломавшуюся нигде, худшей из трёх.
Обе переписаны и проверены на сломанной сборке.
Замеры: --play 6 5 на восьми сидах 47/48 (было 0/48), --accept PASS, шаг 6.1 мкс,
--wall 30 одиночка D20 / трое D20 / пятеро D31 (не найдена), --descend 8/30,
hollow --descend 6, --ui-check, оба проекта --check — без ошибок.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 22:31:34 +07:00
|
|
|
|
{"straggleDist", &Tuning::straggleDist, 0.2f, 1.0f, 12.0f},
|
|
|
|
|
|
{"navStuckTime", &Tuning::navStuckTime, 0.05f, 0.05f, 3.0f},
|
|
|
|
|
|
{"navStuckSpeed", &Tuning::navStuckSpeed, 0.05f, 0.02f, 1.0f},
|
|
|
|
|
|
{"navGoalReached", &Tuning::navGoalReached, 0.05f, 0.05f, 2.0f},
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
{"moveSpeed", &Tuning::moveSpeed, 0.1f, 0.5f, 10.0f},
|
|
|
|
|
|
{"moveSpeedFast", &Tuning::moveSpeedFast, 0.1f, 0.5f, 12.0f},
|
|
|
|
|
|
{"weaponRange", &Tuning::weaponRange, 0.25f, 2.0f, 20.0f},
|
|
|
|
|
|
{"fireInterval", &Tuning::fireInterval, 0.01f, 0.05f, 2.0f},
|
|
|
|
|
|
{"bulletSpeed", &Tuning::bulletSpeed, 1.0f, 5.0f, 60.0f},
|
|
|
|
|
|
{"solveInterval", &Tuning::solveInterval, 0.05f, 0.05f, 2.0f},
|
|
|
|
|
|
{"commitTime", &Tuning::commitTime, 0.05f, 0.0f, 3.0f},
|
|
|
|
|
|
{"hysteresis", &Tuning::hysteresis, 1.0f, 0.0f, 100.0f},
|
|
|
|
|
|
{"cohesionRadius", &Tuning::cohesionRadius, 0.1f, 1.0f, 12.0f},
|
|
|
|
|
|
{"minEngageDist", &Tuning::minEngageDist, 0.1f, 0.0f, 8.0f},
|
|
|
|
|
|
{"sLaneClear", &Tuning::sLaneClear, 5.0f, 0.0f, 400.0f},
|
|
|
|
|
|
{"sCoverage", &Tuning::sCoverage, 1.0f, 0.0f, 200.0f},
|
|
|
|
|
|
{"sBlockAlly", &Tuning::sBlockAlly, 1.0f, 0.0f, 200.0f},
|
|
|
|
|
|
{"sDistCost", &Tuning::sDistCost, 0.5f, 0.0f, 100.0f},
|
|
|
|
|
|
{"sCohesionCost", &Tuning::sCohesionCost, 0.5f, 0.0f, 100.0f},
|
|
|
|
|
|
{"sTooClose", &Tuning::sTooClose, 2.0f, 0.0f, 300.0f},
|
|
|
|
|
|
{"postLeash", &Tuning::postLeash, 0.1f, 0.0f, 10.0f},
|
|
|
|
|
|
{"formSpacing", &Tuning::formationSpacing, 0.05f, 0.4f, 4.0f},
|
Запуск грузит проект; автопилот --play; строй сжимается в тесноте (M31)
ГЛАВНОЕ. "Дольше пяти секунд не продержаться на первом уровне" - это был
не баланс, а запуск. По умолчанию useRooms был false: обычный запуск exe
собирал ЗАХАРДКОЖЕННУЮ карту прототипа со старой расстановкой тварей и
высаживал туда ОДНОГО оперативника с ножом (размер отряда берётся из
профиля). Игрок попадал не на первый этаж игры, а вообще не в игру.
Теперь проект подключается по умолчанию, а старая карта осталась под
флагом --no-rooms.
--PLAY. Новый прогон: автопилот ведёт отряд к спуску и дерётся по дороге.
Ни --descend, ни --wall не меряли ПРОХОЖДЕНИЕ: первый собирает этажи и ни
с кем не воюет, второй ставит отряд в толпу и запрещает двигаться. Жалоба
же была именно про проход этажа.
Автопилот ходит по полю расстояний до спуска (заливка по проходимым
клеткам), целится на несколько клеток вперёд по спуску поля и правит
курсором - тем же вводом, каким играет человек. Три версии до рабочей:
по прямой упирался в первую стену, по восьми соседям лез в диагональную
щель между углами (тело круглое, не пролезает), по соседней клетке дёргался
на каждой границе.
Одиночка проходит три этажа и гибнет на четвёртом:
1 тварей 3, убил 0, 100% здоровья, света 244 дошёл (78 с)
2 тварей 6, убил 1, 100%, света 155 дошёл (44 с)
3 тварей 6, убил 0, 65%, света 8 дошёл (74 с)
4 тварей 9, убил 5, ВАЙП (37 с)
Свет кончается к третьему этажу: расчёт "заряда на четыре пути до спуска"
считал прямые пути, а живой отряд ходит петлями.
СТРОЙ В ТЕСНОТЕ. Клин шириной в пять тел не проходит в дверь шириной в
клетку: бойцы упираются друг в друга, расталкивание держит их на месте,
отряд встаёт насмерть. Теперь строй сам сжимается в колонну, когда вокруг
тел мало свободных клеток.
ЧЕСТНО: это НЕ вылечило проход пятёрки до конца. Автопилот с полным
отрядом всё ещё застревает на первом этаже там, где одиночка проходит, и
причина не в ширине строя (проверено сжатием в точку и отрядом без
клинков). Похоже, нужна нормальная проводка строя по проходам - это
отдельная веха, а не патч в конце. Оставляю с числами, а не с обещанием.
--accept PASS, все семь проверок зелёные.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 17:20:21 +07:00
|
|
|
|
{"tightSpacing", &Tuning::tightSpacing, 0.02f, 0.1f, 1.0f},
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
{"anchorSpeed", &Tuning::anchorSpeed, 0.1f, 0.5f, 12.0f},
|
|
|
|
|
|
{"anchorSpeedFast",&Tuning::anchorSpeedFast, 0.1f, 0.5f, 16.0f},
|
|
|
|
|
|
{"anchorLeash", &Tuning::anchorLeash, 0.1f, 0.2f, 12.0f},
|
|
|
|
|
|
{"anchorLeashMax", &Tuning::anchorLeashMax, 0.1f, 0.4f, 16.0f},
|
|
|
|
|
|
{"leashDrag", &Tuning::leashDrag, 0.05f, 0.0f, 1.0f},
|
|
|
|
|
|
{"anchorAccel", &Tuning::anchorAccel, 1.0f, 2.0f, 80.0f},
|
|
|
|
|
|
// --- натяжение и строй ---
|
|
|
|
|
|
{"urgeStart", &Tuning::urgeStartFrac, 0.05f, 0.0f, 0.95f},
|
|
|
|
|
|
{"urgeAttack", &Tuning::urgeAttack, 0.2f, 0.1f, 20.0f},
|
|
|
|
|
|
{"urgeRelease", &Tuning::urgeRelease, 0.2f, 0.1f, 20.0f},
|
|
|
|
|
|
{"sprintAt", &Tuning::sprintAt, 0.02f, 0.05f, 1.0f},
|
|
|
|
|
|
{"holdSpacing", &Tuning::holdSpacing, 0.02f, 0.4f, 2.0f},
|
|
|
|
|
|
{"sprintSpacing", &Tuning::sprintSpacing, 0.02f, 0.4f, 3.0f},
|
|
|
|
|
|
{"sprintStretch", &Tuning::sprintStretch, 0.05f, 0.5f, 4.0f},
|
|
|
|
|
|
{"formMorph", &Tuning::formMorphRate, 0.25f, 0.25f, 20.0f},
|
|
|
|
|
|
// --- разброс и перезарядка ---
|
|
|
|
|
|
{"spreadStand", &Tuning::spreadStandDeg, 0.1f, 0.0f, 20.0f},
|
|
|
|
|
|
{"spreadMove", &Tuning::spreadMoveDeg, 0.1f, 0.0f, 30.0f},
|
|
|
|
|
|
{"spreadRecoil", &Tuning::spreadRecoilDeg, 0.05f, 0.0f, 10.0f},
|
|
|
|
|
|
{"spreadRecMax", &Tuning::spreadRecoilMax, 0.1f, 0.0f, 20.0f},
|
|
|
|
|
|
{"spreadDecay", &Tuning::spreadRecoilDecay,0.2f, 0.1f, 40.0f},
|
|
|
|
|
|
{"spreadScale", &Tuning::spreadScale, 0.05f, 0.0f, 4.0f},
|
|
|
|
|
|
{"magazine", &Tuning::magazineRifle, 1.0f, 1.0f, 200.0f},
|
|
|
|
|
|
{"reloadTime", &Tuning::reloadRifle, 0.05f, 0.1f, 8.0f},
|
|
|
|
|
|
{"bulletKnock", &Tuning::bulletKnock, 0.05f, 0.0f, 6.0f},
|
|
|
|
|
|
{"knockResist", &Tuning::knockResistScale, 0.05f, 0.0f, 2.0f},
|
Tile2D: движок отдельным продуктом, игра — проектом в нём (M11-M20)
Десять вех одним состоянием: разделять их пофайлово нечем, потому что
src/game.cpp менялся почти в каждой. Собирается начисто без предупреждений,
все проверки проходят.
ДВИЖОК (engine/, цель tile2d)
Каталог свой, и решение «это движок» принимается тем, куда положен файл.
Компилятор нашёл три зависимости движка от игры, и все сняты: размеры кадра
и карты уехали в engine/config.h, потолок населения этажа стал своим
(MAX_LEVEL_SPAWNS против игровой MAX_TARGETS), запасная точка старта стала
центром карты вместо позиции отряда.
engine/projection.* единственное место, где движок знает про изометрию;
видов сразу два — абстракция с одной реализацией ничем
не проверена и обычно оказывается неправильной
engine/project.* манифест проекта: пути, вид, кривая глубины
engine/app.* окно, кадр, fixed timestep — вместо двух копий цикла
engine/tilemap_draw движок научился показывать свою карту
РЕДАКТОР (editor/, цель tile2d_editor)
Открывает любой проект и ни одной игры по имени не знает.
--tool одиннадцать безоконных команд: комнаты, каталог, кривая, сборка
этажа, ASCII-карта, проверка набора. Правку делают файлами,
последствия читают командой (docs/19-agent.md)
терминал снизу: те же инструменты, git, сборка и запросы к claude/codex,
открытый проект уходит потомкам через TILE2D_PROJECT
ПРИМЕР (samples/walk, цель tile2d_walk)
Второй потребитель движка. Игра и редактор самостоятельность движка
проверить не могут: редактор мира не рисует, а игра — то место, куда общий
код и уезжает незаметно.
ИГРА (src/)
Экстракшен: вес и два порога переноски, конечный боезапас, расходники
применяются сами, общая сумка отряда, трата опыта на улучшения.
Каждое из правил проверяется прогоном, а не глазами, и проверки поймали:
вес дважды не связывал ничего (мерил по бинтам прогона, а не по добыче);
боец с клинком не тратил найденные патроны при запасном стволе в сумке;
массив виджетов экрана переполнился и молча съел хвост схрона; проверка
улучшений сама ломала забег, вызывая NewRun посреди спуска.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 01:07:19 +07:00
|
|
|
|
// --- живучесть и сложность ---
|
|
|
|
|
|
{"agentHP", &Tuning::agentHP, 5.0f, 10.0f, 500.0f},
|
|
|
|
|
|
{"enemyDamage", &Tuning::enemyDamageScale, 0.05f, 0.0f, 4.0f},
|
|
|
|
|
|
{"enemyRespawn", &Tuning::enemyRespawn, 1.0f, 1.0f, 300.0f},
|
|
|
|
|
|
{"enemyWindup", &Tuning::enemyWindupScale, 0.05f, 0.25f, 3.0f},
|
|
|
|
|
|
{"enemyGrabHold", &Tuning::enemyGrabHold, 0.01f, 0.02f, 1.0f},
|
|
|
|
|
|
{"propYield", &Tuning::propYieldRadius, 0.5f, 0.0f, 64.0f},
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (raylib только окно, ввод и финальный блит).
Ядро (спек, раздел 6):
- LaneClear: проверка линии огня по своим, стенам и дальности;
- решатель огневых позиций с гистерезисом и коммитом;
- резервирование линий огня и расступание из чужих секторов;
- пули-снаряды, дружественный урон физически возможен.
Хоррор-слой (render-only): свет и туман войны с памятью карты,
светящиеся трассеры, кровь, виньетка, зерно, напряжение.
Ближний бой: сектор удара, три фазы, связка из трёх ударов,
своя дисциплина «не бить сквозь своего».
Проверка: squad_proto.exe --accept прогоняет критерии приёмки
раздела 11 плюс блок M7 по ближнему бою — все PASS,
FRIENDLY_HITS = 0 за 3 минуты боя.
Сборка: cmake -S . -B build && cmake --build build --config Release
raylib 6.0 подтягивается через FetchContent.
Документация — CLAUDE.md как оглавление, содержание в docs/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
|
|
|
|
// --- ближний бой ---
|
|
|
|
|
|
{"meleeReach", &Tuning::meleeReach, 0.05f, 0.4f, 4.0f},
|
|
|
|
|
|
{"meleeArc", &Tuning::meleeArcDeg, 5.0f, 20.0f, 300.0f},
|
|
|
|
|
|
{"meleeWindup", &Tuning::meleeWindup, 0.01f, 0.02f, 1.0f},
|
|
|
|
|
|
{"meleeStrike", &Tuning::meleeStrike, 0.01f, 0.02f, 1.0f},
|
|
|
|
|
|
{"meleeRecover", &Tuning::meleeRecover, 0.01f, 0.0f, 1.5f},
|
|
|
|
|
|
{"meleeInterval", &Tuning::meleeInterval, 0.02f, 0.1f, 3.0f},
|
|
|
|
|
|
{"meleeDamage", &Tuning::meleeDamage, 5.0f, 1.0f, 999.0f},
|
|
|
|
|
|
{"meleeLunge", &Tuning::meleeLungeSpeed, 0.1f, 0.0f, 12.0f},
|
|
|
|
|
|
{"meleeKnock", &Tuning::meleeKnockback, 0.1f, 0.0f, 12.0f},
|
|
|
|
|
|
{"meleeCohesion", &Tuning::meleeCohesionRadius, 0.2f, 1.0f, 20.0f},
|
|
|
|
|
|
{"meleeAllyMargin",&Tuning::meleeAllyMargin, 0.02f, 0.0f, 1.0f},
|
|
|
|
|
|
{"meleePredict", &Tuning::meleeStartPredict,0.1f, 0.0f, 3.0f},
|
|
|
|
|
|
{"meleeReserve", &Tuning::meleeReserveFrac, 0.05f, 0.0f, 1.5f},
|
|
|
|
|
|
{"meleeShake", &Tuning::meleeShake, 0.1f, 0.0f, 6.0f},
|
|
|
|
|
|
// --- хоррор-часть ---
|
|
|
|
|
|
{"sightRadius", &Tuning::sightRadius, 0.25f, 2.0f, 24.0f},
|
|
|
|
|
|
{"sightCone", &Tuning::sightConeDeg, 5.0f, 20.0f, 360.0f},
|
|
|
|
|
|
{"sightPersonal", &Tuning::sightPersonalRadius, 0.1f, 0.5f, 10.0f},
|
|
|
|
|
|
{"fogMemory", &Tuning::fogMemoryLevel, 0.02f, 0.0f, 1.0f},
|
|
|
|
|
|
{"fogReveal", &Tuning::fogEntityReveal, 0.02f, 0.0f, 1.0f},
|
|
|
|
|
|
{"flashPower", &Tuning::flashPower, 0.05f, 0.0f, 2.0f},
|
|
|
|
|
|
{"lightGamma", &Tuning::lightGamma, 0.05f, 0.3f, 4.0f},
|
|
|
|
|
|
{"lightGain", &Tuning::lightMaxGain, 0.05f, 0.2f, 3.0f},
|
|
|
|
|
|
{"fxIntensity", &Tuning::fxIntensity, 0.1f, 0.0f, 4.0f},
|
|
|
|
|
|
{"tracerLength", &Tuning::tracerLength, 0.1f, 0.1f, 6.0f},
|
|
|
|
|
|
{"grainTense", &Tuning::grainTense, 1.0f, 0.0f, 100.0f},
|
|
|
|
|
|
{"vigDepthTense", &Tuning::vigDepthTense, 0.05f, 0.0f, 1.0f},
|
|
|
|
|
|
{"dreadRadius", &Tuning::dreadRadius, 0.5f, 1.0f, 20.0f},
|
|
|
|
|
|
{"shakePerShot", &Tuning::shakePerShot, 0.05f, 0.0f, 4.0f},
|
|
|
|
|
|
// --- кровь, дымка, пепел ---
|
|
|
|
|
|
{"bloodAmount", &Tuning::bloodAmount, 0.05f, 0.0f, 3.0f},
|
|
|
|
|
|
{"bloodOpacity", &Tuning::bloodOpacity, 0.02f, 0.0f, 1.0f},
|
|
|
|
|
|
{"bloodDrops", &Tuning::bloodDrops, 1.0f, 0.0f, 16.0f},
|
|
|
|
|
|
{"hazeAmount", &Tuning::hazeAmount, 0.02f, 0.0f, 2.0f},
|
|
|
|
|
|
{"hazeScale", &Tuning::hazeScale, 0.05f, 0.2f, 4.0f},
|
|
|
|
|
|
{"hazeDrift", &Tuning::hazeDrift, 0.01f, 0.0f, 1.0f},
|
|
|
|
|
|
{"hazeGate", &Tuning::hazeGate, 1.0f, 0.0f, 128.0f},
|
|
|
|
|
|
{"ashDensity", &Tuning::ashDensity, 0.05f, 0.0f, 4.0f},
|
|
|
|
|
|
{"ashSpeed", &Tuning::ashSpeed, 0.05f, 0.0f, 3.0f},
|
|
|
|
|
|
{"ashBright", &Tuning::ashBrightness, 0.05f, 0.0f, 2.0f},
|
|
|
|
|
|
{"ashLift", &Tuning::ashLiftPx, 1.0f, 0.0f, 40.0f},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
inline constexpr int TUNABLE_COUNT = int(sizeof(TUNABLES) / sizeof(TUNABLES[0]));
|