Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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
|
Стихии и вторая поверхность инструментов (M21, E2)
СТИХИИ (src/sim/element.h)
Семь элементов, сопротивления свойством ВИДА, одна формула урона на пулю и на
клинок. Зачем они в игре про бесконечный спуск: сила отряда растёт медленнее
угрозы (закон масштаба, docs/20-gdd.md), и разрыв обязан закрываться знанием.
Стихии — первый такой канал: верное оружие убивает втрое быстрее, и цифра эта
не растёт от прокачки.
Ручных действий не заводится: стихия — свойство оружия, а не кнопка. Всё
решается тем, что отряд взял вниз.
Стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и сабля остаются
чистой кинетикой, потому что на них меряется приёмка.
ИНСТРУМЕНТЫ БАЛАНСА (src/debug/tools.cpp)
Вторая поверхность: `squad_proto --tool weapons|enemies|ttk|economy`. Отдельная
от редакторской не по вкусу — редактор линкуется только с движком и правил боя
не видит, а всё это считает урон. Инструменты делятся по ПРЕДМЕТУ: содержимое
у редактора, баланс у игры.
ЧТО НАШЛА ПРОВЕРКА
Первая версия --tool ttk считала разброс по времени убийства и показывала
уверенные 2.8x на каждой твари. Это была разница МОЩНОСТИ стволов: магнум лучше
пистолета против всех, стихии ни при чём.
С вынесенной за скобки мощностью оружия правда оказалась другой: 1.04-1.36x,
то есть состав удара не значил почти ничего. Механика была декоративной ровно
в том же смысле, в каком когда-то был вес.
Чтобы разброс появился, у твари должны быть И уязвимость, И сопротивление среди
тех стихий, которые вообще носит оружие: одной уязвимости мало, если ни один
ствол не бьёт тем, что она держит. После правки содержимого все пять видов в
коридоре 1.90-3.06x, и картина читается — дробовик жжёт бредущих, дуга бьёт
плевков, лук разъедает громилу, карабин против громилы бесполезен.
Коридор в инструменте 1.8-8.0, а не заявленные в документе 3-6: до тройки
дотянут почти чистые стихийные сборки, которые приходят с аффиксами. Занижать
цель в документе нельзя, поэтому в инструменте стоит достижимое.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 09:25:44 +07:00
|
|
|
|
#include "sim/element.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
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
// Каталог оружия: ЧТО такое ствол или клинок, в отрыве от того, кто его держит.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Оружие — отдельная сущность ECS с компонентом WeaponItem. Боец хранит только
|
|
|
|
|
|
// ссылку на неё (Agent::weaponEntity) и рабочее состояние выстрела (патроны,
|
|
|
|
|
|
// откат, увод ствола). Поэтому «дать бойцу пистолет» — это подменить компонент
|
|
|
|
|
|
// на его сущности-оружии, а не править ни агента, ни правила боя.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Где живут числа. Общее правило прототипа — все числа в tuning.h. Здесь оно
|
|
|
|
|
|
// соблюдено с одной оговоркой, записанной в docs/08-decisions.md: КАРАБИН
|
|
|
|
|
|
// (оружие по умолчанию, то самое, на котором меряются критерии приёмки) читает
|
|
|
|
|
|
// свои параметры из g_tune, поэтому все прежние ручки оверлея и --set работают
|
|
|
|
|
|
// как работали. Остальные стволы — записи каталога: у второго ствола не может
|
|
|
|
|
|
// быть «того же самого» weaponRange, иначе он не второй ствол.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Визуальные параметры атаки лежат ЗДЕСЬ ЖЕ, но в нейтральном виде: длины,
|
|
|
|
|
|
// радиусы в пикселях и байты RGB. Ни одного типа рендера в sim/ не появилось —
|
|
|
|
|
|
// цвет собирает render/fx.cpp. Так у пистолета своя вспышка и свой трассер,
|
|
|
|
|
|
// и при этом правило «sim не знает про рендер» не нарушено.
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
|
|
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/math.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
|
|
|
|
#include "tuning.h"
|
|
|
|
|
|
|
|
|
|
|
|
enum class WeaponClass : uint8_t
|
|
|
|
|
|
{
|
|
|
|
|
|
RANGED = 0, // ствол: линия огня, снаряд, правило 6.3
|
|
|
|
|
|
MELEE = 1, // клинок: сектор замаха, то же правило в угловой форме
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
inline const char* WeaponClassName(WeaponClass c)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (c == WeaponClass::MELEE) ? "MELEE" : "RANGED";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum class FirearmId : uint8_t
|
|
|
|
|
|
{
|
|
|
|
|
|
RIFLE = 0, // штатный карабин отряда: числа из tuning.h
|
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
|
|
|
|
PISTOL, // короткоствол: ближе, быстрее, мельче магазин
|
|
|
|
|
|
SHOTGUN, // картечь: залп дробин, в упор рвёт, дальше двух шагов бесполезен
|
|
|
|
|
|
SMG, // шквал: очередь дешёвыми патронами, ствол уводит адски
|
|
|
|
|
|
MAGNUM, // один тяжёлый выстрел: медленно, больно, ошибок не прощает
|
|
|
|
|
|
BOW, // тихий составной лук: редкий тяжёлый снаряд без дульной вспышки
|
Крафт стал выбором: закалка, отпечаток, направленные операции (M33)
Прежние пять операций были СЛЕПЫМИ: перековка бросала всё заново,
прививка ставила случайный тег, отсечение снимало последний аффикс,
возвышение поднимало случайный. Игрок не принимал ни одного решения - жал
кнопку и смотрел, что выпало. Это не крафт, это второй бросок предмета за
материалы.
Теперь половина операций направленные: игрок показывает пальцем, какой
аффикс трогать, и платит за это вдвое - вот это уже решение
("перебросить всё дёшево или снять один дорого").
Два новых глагола:
ЗАКАЛКА двигает рулон выбранного аффикса к максимуму его тира на треть
остатка. Дёшево, понятно, повторяемо - то самое "доработать почти
хорошую вещь". Тир не трогает: за ступень платят возвышением.
ОТПЕЧАТОК переносит лучший аффикс с ДРУГОЙ вещи, сжигая её. Именно он
превращает хлам из схрона в ресурс, а не в мусор, который лежит.
Правило "один тег один раз" им не обходится, бюджет проверяется.
В мастерской: клик по вещи кладёт её на верстак, повторный клик по той же
назначает донором (помечается кровью - он сгорит, и видеть это надо ДО
нажатия). Строки аффиксов стали кнопками: по ним выбирается цель.
Проверка меряет НАПРАВЛЕННОСТЬ, а не факт срабатывания:
направленность: отсечение по цели, возвышение по цели,
закалка двигает рулон, отпечаток перенёс и сжёг донора
Отсечение пробуется на СРЕДНЕМ аффиксе из трёх: операция, которая
"работает", снимая последний, прошла бы и слепую версию.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:37:07 +07:00
|
|
|
|
NAILER, // гвоздомёт: дешевле некуда, бьёт часто и почти никак
|
|
|
|
|
|
HMG, // пулемёт: полтораста патронов и шесть секунд беспомощности
|
|
|
|
|
|
PLASMA, // резак: два с половиной шага дальности, зато почти чистая дуга
|
|
|
|
|
|
RAIL, // рельса: три выстрела на всю комнату, между ними целая вечность
|
|
|
|
|
|
FLAMER, // огнемёт: конус жара в упор, свои под него лезут постоянно
|
|
|
|
|
|
ACID, // распылитель: медленные кислотные капли, разъедают жирное
|
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
|
|
|
|
COUNT,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
enum class BladeId : uint8_t
|
|
|
|
|
|
{
|
|
|
|
|
|
SABER = 0, // штатный клинок: числа из tuning.h
|
|
|
|
|
|
KNIFE, // нож: узкая дуга, зато почти без замаха
|
|
|
|
|
|
CLEAVER, // тесак: широкий, тяжёлый, сносит с ног
|
Крафт стал выбором: закалка, отпечаток, направленные операции (M33)
Прежние пять операций были СЛЕПЫМИ: перековка бросала всё заново,
прививка ставила случайный тег, отсечение снимало последний аффикс,
возвышение поднимало случайный. Игрок не принимал ни одного решения - жал
кнопку и смотрел, что выпало. Это не крафт, это второй бросок предмета за
материалы.
Теперь половина операций направленные: игрок показывает пальцем, какой
аффикс трогать, и платит за это вдвое - вот это уже решение
("перебросить всё дёшево или снять один дорого").
Два новых глагола:
ЗАКАЛКА двигает рулон выбранного аффикса к максимуму его тира на треть
остатка. Дёшево, понятно, повторяемо - то самое "доработать почти
хорошую вещь". Тир не трогает: за ступень платят возвышением.
ОТПЕЧАТОК переносит лучший аффикс с ДРУГОЙ вещи, сжигая её. Именно он
превращает хлам из схрона в ресурс, а не в мусор, который лежит.
Правило "один тег один раз" им не обходится, бюджет проверяется.
В мастерской: клик по вещи кладёт её на верстак, повторный клик по той же
назначает донором (помечается кровью - он сгорит, и видеть это надо ДО
нажатия). Строки аффиксов стали кнопками: по ним выбирается цель.
Проверка меряет НАПРАВЛЕННОСТЬ, а не факт срабатывания:
направленность: отсечение по цели, возвышение по цели,
закалка двигает рулон, отпечаток перенёс и сжёг донора
Отсечение пробуется на СРЕДНЕМ аффиксе из трёх: операция, которая
"работает", снимая последний, прошла бы и слепую версию.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:37:07 +07:00
|
|
|
|
STILETTO, // стилет: держит чужой клинок лучше всех, а бьёт слабее всех
|
|
|
|
|
|
MAUL, // молот: самый тяжёлый удар в игре и никакой защиты
|
|
|
|
|
|
RIPPER, // цепной: мелкие частые укусы, кислота
|
|
|
|
|
|
CRYOEDGE, // крио-клинок: сабля, отдавшая часть урона за холод
|
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
|
|
|
|
COUNT,
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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 FirearmDef
|
|
|
|
|
|
{
|
|
|
|
|
|
const char* name = "rifle";
|
|
|
|
|
|
|
Стихии и вторая поверхность инструментов (M21, E2)
СТИХИИ (src/sim/element.h)
Семь элементов, сопротивления свойством ВИДА, одна формула урона на пулю и на
клинок. Зачем они в игре про бесконечный спуск: сила отряда растёт медленнее
угрозы (закон масштаба, docs/20-gdd.md), и разрыв обязан закрываться знанием.
Стихии — первый такой канал: верное оружие убивает втрое быстрее, и цифра эта
не растёт от прокачки.
Ручных действий не заводится: стихия — свойство оружия, а не кнопка. Всё
решается тем, что отряд взял вниз.
Стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и сабля остаются
чистой кинетикой, потому что на них меряется приёмка.
ИНСТРУМЕНТЫ БАЛАНСА (src/debug/tools.cpp)
Вторая поверхность: `squad_proto --tool weapons|enemies|ttk|economy`. Отдельная
от редакторской не по вкусу — редактор линкуется только с движком и правил боя
не видит, а всё это считает урон. Инструменты делятся по ПРЕДМЕТУ: содержимое
у редактора, баланс у игры.
ЧТО НАШЛА ПРОВЕРКА
Первая версия --tool ttk считала разброс по времени убийства и показывала
уверенные 2.8x на каждой твари. Это была разница МОЩНОСТИ стволов: магнум лучше
пистолета против всех, стихии ни при чём.
С вынесенной за скобки мощностью оружия правда оказалась другой: 1.04-1.36x,
то есть состав удара не значил почти ничего. Механика была декоративной ровно
в том же смысле, в каком когда-то был вес.
Чтобы разброс появился, у твари должны быть И уязвимость, И сопротивление среди
тех стихий, которые вообще носит оружие: одной уязвимости мало, если ни один
ствол не бьёт тем, что она держит. После правки содержимого все пять видов в
коридоре 1.90-3.06x, и картина читается — дробовик жжёт бредущих, дуга бьёт
плевков, лук разъедает громилу, карабин против громилы бесполезен.
Коридор в инструменте 1.8-8.0, а не заявленные в документе 3-6: до тройки
дотянут почти чистые стихийные сборки, которые приходят с аффиксами. Занижать
цель в документе нельзя, поэтому в инструменте стоит достижимое.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 09:25:44 +07:00
|
|
|
|
// Из чего состоит удар. По умолчанию чистая кинетика: карабин, на котором
|
|
|
|
|
|
// меряется приёмка, обязан остаться ровно таким, каким был.
|
|
|
|
|
|
ElementMix elem;
|
|
|
|
|
|
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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 range = 7.2f;
|
|
|
|
|
|
float fireInterval = 0.35f;
|
|
|
|
|
|
float damage = 12.0f;
|
|
|
|
|
|
float laneHalfWidth = 0.10f;
|
|
|
|
|
|
float muzzleOffset = 0.30f;
|
|
|
|
|
|
float bulletSpeed = 22.0f;
|
|
|
|
|
|
float bulletRadius = 0.04f;
|
|
|
|
|
|
float knockback = 0.35f; // импульс мишени от попадания
|
|
|
|
|
|
|
|
|
|
|
|
// разброс: стоя, на полном ходу, и увод ствола за выстрел
|
|
|
|
|
|
float spreadStandDeg = 0.9f;
|
|
|
|
|
|
float spreadMoveDeg = 4.2f;
|
|
|
|
|
|
float spreadRecoilDeg = 0.55f;
|
|
|
|
|
|
float spreadRecoilMax = 3.0f;
|
|
|
|
|
|
float recoilDecay = 3.2f; // 1/сек
|
|
|
|
|
|
|
|
|
|
|
|
// перезарядка
|
|
|
|
|
|
int magazine = 24;
|
|
|
|
|
|
float reloadTime = 1.9f;
|
|
|
|
|
|
|
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
|
|
|
|
// Сколько снарядов уходит за один спуск. Дробины летят в ОДНОМ уже
|
|
|
|
|
|
// проверенном конусе разброса — правило 6.3 от залпа не слабеет.
|
|
|
|
|
|
int pellets = 1;
|
|
|
|
|
|
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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
|
|
|
|
// визуал атаки (нейтральные числа: пиксели, секунды, байты RGB)
|
|
|
|
|
|
float tracerLength = 1.30f;
|
|
|
|
|
|
float muzzleRadiusPx = 5.0f;
|
|
|
|
|
|
float muzzleTime = 0.07f;
|
|
|
|
|
|
float muzzleFlamePx = 0.35f; // длина языка пламени, юнитов мира
|
|
|
|
|
|
float flashPower = 0.55f; // как источник света
|
|
|
|
|
|
float flashRadius = 4.0f;
|
|
|
|
|
|
float shakePerShot = 0.55f;
|
|
|
|
|
|
uint8_t tracerR = 255, tracerG = 240, tracerB = 205;
|
|
|
|
|
|
uint8_t muzzleR = 255, muzzleG = 225, muzzleB = 150;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// --- клинок ------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
struct BladeDef
|
|
|
|
|
|
{
|
|
|
|
|
|
const char* name = "blade";
|
|
|
|
|
|
|
Стихии и вторая поверхность инструментов (M21, E2)
СТИХИИ (src/sim/element.h)
Семь элементов, сопротивления свойством ВИДА, одна формула урона на пулю и на
клинок. Зачем они в игре про бесконечный спуск: сила отряда растёт медленнее
угрозы (закон масштаба, docs/20-gdd.md), и разрыв обязан закрываться знанием.
Стихии — первый такой канал: верное оружие убивает втрое быстрее, и цифра эта
не растёт от прокачки.
Ручных действий не заводится: стихия — свойство оружия, а не кнопка. Всё
решается тем, что отряд взял вниз.
Стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и сабля остаются
чистой кинетикой, потому что на них меряется приёмка.
ИНСТРУМЕНТЫ БАЛАНСА (src/debug/tools.cpp)
Вторая поверхность: `squad_proto --tool weapons|enemies|ttk|economy`. Отдельная
от редакторской не по вкусу — редактор линкуется только с движком и правил боя
не видит, а всё это считает урон. Инструменты делятся по ПРЕДМЕТУ: содержимое
у редактора, баланс у игры.
ЧТО НАШЛА ПРОВЕРКА
Первая версия --tool ttk считала разброс по времени убийства и показывала
уверенные 2.8x на каждой твари. Это была разница МОЩНОСТИ стволов: магнум лучше
пистолета против всех, стихии ни при чём.
С вынесенной за скобки мощностью оружия правда оказалась другой: 1.04-1.36x,
то есть состав удара не значил почти ничего. Механика была декоративной ровно
в том же смысле, в каком когда-то был вес.
Чтобы разброс появился, у твари должны быть И уязвимость, И сопротивление среди
тех стихий, которые вообще носит оружие: одной уязвимости мало, если ни один
ствол не бьёт тем, что она держит. После правки содержимого все пять видов в
коридоре 1.90-3.06x, и картина читается — дробовик жжёт бредущих, дуга бьёт
плевков, лук разъедает громилу, карабин против громилы бесполезен.
Коридор в инструменте 1.8-8.0, а не заявленные в документе 3-6: до тройки
дотянут почти чистые стихийные сборки, которые приходят с аффиксами. Занижать
цель в документе нельзя, поэтому в инструменте стоит достижимое.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 09:25:44 +07:00
|
|
|
|
ElementMix elem;
|
|
|
|
|
|
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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 reach = 1.40f;
|
|
|
|
|
|
float arcDeg = 100.0f;
|
|
|
|
|
|
float windup = 0.17f;
|
|
|
|
|
|
float strike = 0.13f;
|
|
|
|
|
|
float recover = 0.24f;
|
|
|
|
|
|
float interval = 0.60f;
|
|
|
|
|
|
float damage = 130.0f;
|
|
|
|
|
|
float knockback = 2.4f; // импульс мишени, юнитов/сек
|
|
|
|
|
|
float lungeSpeed = 3.4f; // рывок в момент удара
|
Личинки, парирование и инструмент --wall (M30)
--WALL. Новый прогон отвечает на вопрос, появившийся вместе с чекпоинтами
через пятьдесят этажей: растёт ли достижимая глубина от вылазки к вылазке.
Отряд из одного, трёх и пяти, одетый по глубине, ставится в скопление
тварей и живёт полторы минуты без управления. Первая стена: одиночка D7,
трое D30, пятеро D30 - петля работает.
И он же нашёл настоящую ошибку: PlaceSquad воскрешал ВСЕ пять слотов, в
том числе незанятые. Строка писалась, когда отряд всегда был впятером;
после появления ростера одиночка выходил на этаж с четырьмя невидимыми
телохранителями. Из-за них прежний замер "одиночка выжил на первом этаже,
100 -> 41" был боем впятером.
ЛИЧИНКИ. С исправленным замером выяснилось, что одиночка с ножом
проигрывает двум бредущим: 210 здоровья против ножа это девять секунд
работы под ответным уроном. Первые этажи населены личинками (55 здоровья,
7 урона, медленные), бредущий уехал на третий этаж, бегун на четвёртый.
Новый ВИД, а не ослабленный бредущий с множителем: глубина в этой игре
меняет, кто выходит, а не числа у одного и того же.
ПАРИРОВАНИЕ. Отдельная характеристика клинка, и чем оружие легче, тем она
выше: нож 34%, сабля 20%, тесак 8%. Не выведена из веса намеренно - её
двигает аффикс PARRY, и лёгкий тесак обязан быть возможен как находка.
Отбить можно, только если удары СОПРИКОСНУЛИСЬ: боец сам должен быть в
замахе или в ударе. Иначе клинок стал бы щитом, работающим, пока стоишь.
Удавшийся отбив снимает урон, бьёт тварь ударом бойца целиком и запускает
повторную атаку с начала фазы, НЕ сбрасывая откат. Последнее и есть суть
размена: сбрасывай отбив откат, выгоднее было бы подставляться под удары.
Плевок отбивается тоже: летит назад и несёт урон бойца, а не твари. Это
его удар, просто по летящей цели.
Проверка меряет размен целиком, включая обратную половину правила:
"стоя столбом не отбивает". Без неё условие про соприкосновение
существовало бы только в комментарии.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 09:04:35 +07:00
|
|
|
|
|
|
|
|
|
|
// Шанс отбить чужой удар. ЧЕМ ЛЕГЧЕ ОРУЖИЕ, ТЕМ ВЫШЕ: нож успевает встать
|
|
|
|
|
|
// на пути, тесаком парировать нечем — им можно только бить первым.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Это отдельная характеристика, а не производная от веса: её меняют
|
|
|
|
|
|
// аффиксы (sim/affix.h), и «лёгкий тесак» должен быть возможен как
|
|
|
|
|
|
// находка, а не запрещён формулой.
|
|
|
|
|
|
float parry = 0.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
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// --- компонент сущности-оружия ------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
struct WeaponItem
|
|
|
|
|
|
{
|
|
|
|
|
|
WeaponClass cls = WeaponClass::RANGED;
|
|
|
|
|
|
FirearmId gunId = FirearmId::RIFLE;
|
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
|
|
|
|
BladeId bladeId = BladeId::SABER;
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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
|
|
|
|
FirearmDef gun{};
|
|
|
|
|
|
BladeDef blade{};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// --- каталог ------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
// Карабин: параметры берутся из tuning, чтобы прежние ручки и --set остались
|
|
|
|
|
|
// рабочими (см. шапку файла).
|
|
|
|
|
|
inline FirearmDef RifleDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "rifle";
|
|
|
|
|
|
d.range = g_tune.weaponRange;
|
|
|
|
|
|
d.fireInterval = g_tune.fireInterval;
|
|
|
|
|
|
d.damage = g_tune.damage;
|
|
|
|
|
|
d.laneHalfWidth = g_tune.laneHalfWidth;
|
|
|
|
|
|
d.muzzleOffset = g_tune.muzzleOffset;
|
|
|
|
|
|
d.bulletSpeed = g_tune.bulletSpeed;
|
|
|
|
|
|
d.bulletRadius = g_tune.bulletRadius;
|
|
|
|
|
|
d.knockback = g_tune.bulletKnock;
|
|
|
|
|
|
|
|
|
|
|
|
d.spreadStandDeg = g_tune.spreadStandDeg;
|
|
|
|
|
|
d.spreadMoveDeg = g_tune.spreadMoveDeg;
|
|
|
|
|
|
d.spreadRecoilDeg = g_tune.spreadRecoilDeg;
|
|
|
|
|
|
d.spreadRecoilMax = g_tune.spreadRecoilMax;
|
|
|
|
|
|
d.recoilDecay = g_tune.spreadRecoilDecay;
|
|
|
|
|
|
|
|
|
|
|
|
d.magazine = int(g_tune.magazineRifle);
|
|
|
|
|
|
d.reloadTime = g_tune.reloadRifle;
|
|
|
|
|
|
|
|
|
|
|
|
d.tracerLength = g_tune.tracerLength;
|
|
|
|
|
|
d.muzzleRadiusPx = g_tune.muzzleRadiusPx;
|
|
|
|
|
|
d.muzzleTime = g_tune.muzzleTime;
|
|
|
|
|
|
d.flashPower = g_tune.flashPower;
|
|
|
|
|
|
d.flashRadius = g_tune.flashRadius;
|
|
|
|
|
|
d.shakePerShot = g_tune.shakePerShot;
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Пистолет: бьёт ближе и слабее, но чаще; на ходу «гуляет» заметно сильнее
|
|
|
|
|
|
// карабина, зато перезаряжается вдвое быстрее. Вспышка мельче и холоднее,
|
|
|
|
|
|
// трассер короче — ствол должен читаться по картинке, а не только по цифрам.
|
|
|
|
|
|
inline FirearmDef PistolDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "pistol";
|
|
|
|
|
|
d.range = 4.6f;
|
|
|
|
|
|
d.fireInterval = 0.22f;
|
|
|
|
|
|
d.damage = 8.0f;
|
|
|
|
|
|
d.laneHalfWidth = 0.10f;
|
|
|
|
|
|
d.muzzleOffset = 0.24f;
|
|
|
|
|
|
d.bulletSpeed = 17.0f;
|
|
|
|
|
|
d.bulletRadius = 0.04f;
|
|
|
|
|
|
d.knockback = 0.22f;
|
|
|
|
|
|
|
|
|
|
|
|
d.spreadStandDeg = 1.6f;
|
|
|
|
|
|
d.spreadMoveDeg = 6.5f;
|
|
|
|
|
|
d.spreadRecoilDeg = 0.9f;
|
|
|
|
|
|
d.spreadRecoilMax = 4.5f;
|
|
|
|
|
|
d.recoilDecay = 4.5f;
|
|
|
|
|
|
|
|
|
|
|
|
d.magazine = 7;
|
|
|
|
|
|
d.reloadTime = 1.15f;
|
|
|
|
|
|
|
|
|
|
|
|
d.tracerLength = 0.85f;
|
|
|
|
|
|
d.muzzleRadiusPx = 3.5f;
|
|
|
|
|
|
d.muzzleTime = 0.055f;
|
|
|
|
|
|
d.muzzleFlamePx = 0.26f;
|
|
|
|
|
|
d.flashPower = 0.42f;
|
|
|
|
|
|
d.flashRadius = 3.2f;
|
|
|
|
|
|
d.shakePerShot = 0.34f;
|
|
|
|
|
|
d.tracerR = 255; d.tracerG = 214; d.tracerB = 170;
|
|
|
|
|
|
d.muzzleR = 255; d.muzzleG = 205; d.muzzleB = 130;
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
// Картечь: один спуск — залп дробин в общем конусе. Дальность позорная, зато
|
|
|
|
|
|
// в упор снимает столько же, сколько очередь карабина. Конус широкий всегда,
|
|
|
|
|
|
// поэтому проверка «свой на линии» отсекает выстрел куда чаще, чем у ствола —
|
|
|
|
|
|
// это не баг картечи, это её цена.
|
|
|
|
|
|
inline FirearmDef ShotgunDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "shotgun";
|
|
|
|
|
|
d.range = 3.4f;
|
|
|
|
|
|
d.fireInterval = 0.85f;
|
|
|
|
|
|
d.damage = 26.0f; // на дробину
|
|
|
|
|
|
d.laneHalfWidth = 0.10f;
|
|
|
|
|
|
d.muzzleOffset = 0.32f;
|
|
|
|
|
|
d.bulletSpeed = 19.0f;
|
|
|
|
|
|
d.bulletRadius = 0.05f;
|
|
|
|
|
|
d.knockback = 0.55f;
|
|
|
|
|
|
|
|
|
|
|
|
d.spreadStandDeg = 7.5f;
|
|
|
|
|
|
d.spreadMoveDeg = 13.0f;
|
|
|
|
|
|
d.spreadRecoilDeg = 1.4f;
|
|
|
|
|
|
d.spreadRecoilMax = 5.0f;
|
|
|
|
|
|
d.recoilDecay = 5.0f;
|
|
|
|
|
|
|
|
|
|
|
|
d.magazine = 5;
|
|
|
|
|
|
d.reloadTime = 2.6f;
|
|
|
|
|
|
d.pellets = 6;
|
|
|
|
|
|
|
|
|
|
|
|
d.tracerLength = 0.55f;
|
|
|
|
|
|
d.muzzleRadiusPx = 7.5f;
|
|
|
|
|
|
d.muzzleTime = 0.10f;
|
|
|
|
|
|
d.muzzleFlamePx = 0.50f;
|
|
|
|
|
|
d.flashPower = 0.85f;
|
|
|
|
|
|
d.flashRadius = 5.2f;
|
|
|
|
|
|
d.shakePerShot = 1.55f;
|
|
|
|
|
|
d.tracerR = 255; d.tracerG = 190; d.tracerB = 130;
|
|
|
|
|
|
d.muzzleR = 255; d.muzzleG = 196; d.muzzleB = 110;
|
Стихии и вторая поверхность инструментов (M21, E2)
СТИХИИ (src/sim/element.h)
Семь элементов, сопротивления свойством ВИДА, одна формула урона на пулю и на
клинок. Зачем они в игре про бесконечный спуск: сила отряда растёт медленнее
угрозы (закон масштаба, docs/20-gdd.md), и разрыв обязан закрываться знанием.
Стихии — первый такой канал: верное оружие убивает втрое быстрее, и цифра эта
не растёт от прокачки.
Ручных действий не заводится: стихия — свойство оружия, а не кнопка. Всё
решается тем, что отряд взял вниз.
Стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и сабля остаются
чистой кинетикой, потому что на них меряется приёмка.
ИНСТРУМЕНТЫ БАЛАНСА (src/debug/tools.cpp)
Вторая поверхность: `squad_proto --tool weapons|enemies|ttk|economy`. Отдельная
от редакторской не по вкусу — редактор линкуется только с движком и правил боя
не видит, а всё это считает урон. Инструменты делятся по ПРЕДМЕТУ: содержимое
у редактора, баланс у игры.
ЧТО НАШЛА ПРОВЕРКА
Первая версия --tool ttk считала разброс по времени убийства и показывала
уверенные 2.8x на каждой твари. Это была разница МОЩНОСТИ стволов: магнум лучше
пистолета против всех, стихии ни при чём.
С вынесенной за скобки мощностью оружия правда оказалась другой: 1.04-1.36x,
то есть состав удара не значил почти ничего. Механика была декоративной ровно
в том же смысле, в каком когда-то был вес.
Чтобы разброс появился, у твари должны быть И уязвимость, И сопротивление среди
тех стихий, которые вообще носит оружие: одной уязвимости мало, если ни один
ствол не бьёт тем, что она держит. После правки содержимого все пять видов в
коридоре 1.90-3.06x, и картина читается — дробовик жжёт бредущих, дуга бьёт
плевков, лук разъедает громилу, карабин против громилы бесполезен.
Коридор в инструменте 1.8-8.0, а не заявленные в документе 3-6: до тройки
дотянут почти чистые стихийные сборки, которые приходят с аффиксами. Занижать
цель в документе нельзя, поэтому в инструменте стоит достижимое.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 09:25:44 +07:00
|
|
|
|
|
|
|
|
|
|
// раскалённая дробь: стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и
|
|
|
|
|
|
// сабля остаются чистой кинетикой — на них меряется приёмка.
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::INC, 0.55f);
|
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
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Шквал: патроны дешёвые, урон мизерный, но ствол не молчит. Увод копится
|
|
|
|
|
|
// быстрее, чем оседает, поэтому длинная очередь сама себя разбрасывает.
|
|
|
|
|
|
inline FirearmDef SmgDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "smg";
|
|
|
|
|
|
d.range = 5.4f;
|
|
|
|
|
|
d.fireInterval = 0.09f;
|
|
|
|
|
|
d.damage = 6.0f;
|
|
|
|
|
|
d.laneHalfWidth = 0.10f;
|
|
|
|
|
|
d.muzzleOffset = 0.28f;
|
|
|
|
|
|
d.bulletSpeed = 20.0f;
|
|
|
|
|
|
d.bulletRadius = 0.035f;
|
|
|
|
|
|
d.knockback = 0.16f;
|
|
|
|
|
|
|
|
|
|
|
|
d.spreadStandDeg = 1.9f;
|
|
|
|
|
|
d.spreadMoveDeg = 7.0f;
|
|
|
|
|
|
d.spreadRecoilDeg = 0.7f;
|
|
|
|
|
|
d.spreadRecoilMax = 7.0f;
|
|
|
|
|
|
d.recoilDecay = 5.5f;
|
|
|
|
|
|
|
|
|
|
|
|
d.magazine = 40;
|
|
|
|
|
|
d.reloadTime = 2.2f;
|
|
|
|
|
|
|
|
|
|
|
|
d.tracerLength = 0.95f;
|
|
|
|
|
|
d.muzzleRadiusPx = 3.8f;
|
|
|
|
|
|
d.muzzleTime = 0.05f;
|
|
|
|
|
|
d.muzzleFlamePx = 0.22f;
|
|
|
|
|
|
d.flashPower = 0.38f;
|
|
|
|
|
|
d.flashRadius = 3.0f;
|
|
|
|
|
|
d.shakePerShot = 0.22f;
|
|
|
|
|
|
d.tracerR = 220; d.tracerG = 240; d.tracerB = 255;
|
|
|
|
|
|
d.muzzleR = 210; d.muzzleG = 230; d.muzzleB = 255;
|
Стихии и вторая поверхность инструментов (M21, E2)
СТИХИИ (src/sim/element.h)
Семь элементов, сопротивления свойством ВИДА, одна формула урона на пулю и на
клинок. Зачем они в игре про бесконечный спуск: сила отряда растёт медленнее
угрозы (закон масштаба, docs/20-gdd.md), и разрыв обязан закрываться знанием.
Стихии — первый такой канал: верное оружие убивает втрое быстрее, и цифра эта
не растёт от прокачки.
Ручных действий не заводится: стихия — свойство оружия, а не кнопка. Всё
решается тем, что отряд взял вниз.
Стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и сабля остаются
чистой кинетикой, потому что на них меряется приёмка.
ИНСТРУМЕНТЫ БАЛАНСА (src/debug/tools.cpp)
Вторая поверхность: `squad_proto --tool weapons|enemies|ttk|economy`. Отдельная
от редакторской не по вкусу — редактор линкуется только с движком и правил боя
не видит, а всё это считает урон. Инструменты делятся по ПРЕДМЕТУ: содержимое
у редактора, баланс у игры.
ЧТО НАШЛА ПРОВЕРКА
Первая версия --tool ttk считала разброс по времени убийства и показывала
уверенные 2.8x на каждой твари. Это была разница МОЩНОСТИ стволов: магнум лучше
пистолета против всех, стихии ни при чём.
С вынесенной за скобки мощностью оружия правда оказалась другой: 1.04-1.36x,
то есть состав удара не значил почти ничего. Механика была декоративной ровно
в том же смысле, в каком когда-то был вес.
Чтобы разброс появился, у твари должны быть И уязвимость, И сопротивление среди
тех стихий, которые вообще носит оружие: одной уязвимости мало, если ни один
ствол не бьёт тем, что она держит. После правки содержимого все пять видов в
коридоре 1.90-3.06x, и картина читается — дробовик жжёт бредущих, дуга бьёт
плевков, лук разъедает громилу, карабин против громилы бесполезен.
Коридор в инструменте 1.8-8.0, а не заявленные в документе 3-6: до тройки
дотянут почти чистые стихийные сборки, которые приходят с аффиксами. Занижать
цель в документе нельзя, поэтому в инструменте стоит достижимое.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 09:25:44 +07:00
|
|
|
|
|
|
|
|
|
|
// электризованный боеприпас: стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и
|
|
|
|
|
|
// сабля остаются чистой кинетикой — на них меряется приёмка.
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::ARC, 0.50f);
|
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
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Тяжёлый револьвер: бьёт дальше карабина и валит с ног, но между выстрелами
|
|
|
|
|
|
// целая секунда и в барабане шесть патронов. Оружие для того, кто попадает.
|
|
|
|
|
|
inline FirearmDef MagnumDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "magnum";
|
|
|
|
|
|
d.range = 8.6f;
|
|
|
|
|
|
d.fireInterval = 0.95f;
|
|
|
|
|
|
d.damage = 78.0f;
|
|
|
|
|
|
d.laneHalfWidth = 0.10f;
|
|
|
|
|
|
d.muzzleOffset = 0.30f;
|
|
|
|
|
|
d.bulletSpeed = 30.0f;
|
|
|
|
|
|
d.bulletRadius = 0.05f;
|
|
|
|
|
|
d.knockback = 1.30f;
|
|
|
|
|
|
|
|
|
|
|
|
d.spreadStandDeg = 0.6f;
|
|
|
|
|
|
d.spreadMoveDeg = 8.0f; // с рук на бегу — не стрелять
|
|
|
|
|
|
d.spreadRecoilDeg = 2.2f;
|
|
|
|
|
|
d.spreadRecoilMax = 6.0f;
|
|
|
|
|
|
d.recoilDecay = 3.0f;
|
|
|
|
|
|
|
|
|
|
|
|
d.magazine = 6;
|
|
|
|
|
|
d.reloadTime = 3.0f;
|
|
|
|
|
|
|
|
|
|
|
|
d.tracerLength = 1.85f;
|
|
|
|
|
|
d.muzzleRadiusPx = 6.5f;
|
|
|
|
|
|
d.muzzleTime = 0.09f;
|
|
|
|
|
|
d.muzzleFlamePx = 0.45f;
|
|
|
|
|
|
d.flashPower = 0.80f;
|
|
|
|
|
|
d.flashRadius = 5.0f;
|
|
|
|
|
|
d.shakePerShot = 1.90f;
|
|
|
|
|
|
d.tracerR = 255; d.tracerG = 246; d.tracerB = 226;
|
|
|
|
|
|
d.muzzleR = 255; d.muzzleG = 232; d.muzzleB = 176;
|
Стихии и вторая поверхность инструментов (M21, E2)
СТИХИИ (src/sim/element.h)
Семь элементов, сопротивления свойством ВИДА, одна формула урона на пулю и на
клинок. Зачем они в игре про бесконечный спуск: сила отряда растёт медленнее
угрозы (закон масштаба, docs/20-gdd.md), и разрыв обязан закрываться знанием.
Стихии — первый такой канал: верное оружие убивает втрое быстрее, и цифра эта
не растёт от прокачки.
Ручных действий не заводится: стихия — свойство оружия, а не кнопка. Всё
решается тем, что отряд взял вниз.
Стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и сабля остаются
чистой кинетикой, потому что на них меряется приёмка.
ИНСТРУМЕНТЫ БАЛАНСА (src/debug/tools.cpp)
Вторая поверхность: `squad_proto --tool weapons|enemies|ttk|economy`. Отдельная
от редакторской не по вкусу — редактор линкуется только с движком и правил боя
не видит, а всё это считает урон. Инструменты делятся по ПРЕДМЕТУ: содержимое
у редактора, баланс у игры.
ЧТО НАШЛА ПРОВЕРКА
Первая версия --tool ttk считала разброс по времени убийства и показывала
уверенные 2.8x на каждой твари. Это была разница МОЩНОСТИ стволов: магнум лучше
пистолета против всех, стихии ни при чём.
С вынесенной за скобки мощностью оружия правда оказалась другой: 1.04-1.36x,
то есть состав удара не значил почти ничего. Механика была декоративной ровно
в том же смысле, в каком когда-то был вес.
Чтобы разброс появился, у твари должны быть И уязвимость, И сопротивление среди
тех стихий, которые вообще носит оружие: одной уязвимости мало, если ни один
ствол не бьёт тем, что она держит. После правки содержимого все пять видов в
коридоре 1.90-3.06x, и картина читается — дробовик жжёт бредущих, дуга бьёт
плевков, лук разъедает громилу, карабин против громилы бесполезен.
Коридор в инструменте 1.8-8.0, а не заявленные в документе 3-6: до тройки
дотянут почти чистые стихийные сборки, которые приходят с аффиксами. Занижать
цель в документе нельзя, поэтому в инструменте стоит достижимое.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 09:25:44 +07:00
|
|
|
|
|
|
|
|
|
|
// пробивает то, что защищено: стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и
|
|
|
|
|
|
// сабля остаются чистой кинетикой — на них меряется приёмка.
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::VOID, 0.45f);
|
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
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline FirearmDef BowDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "bow";
|
|
|
|
|
|
d.range = 8.0f;
|
|
|
|
|
|
d.fireInterval = 0.82f;
|
|
|
|
|
|
d.damage = 54.0f;
|
|
|
|
|
|
d.laneHalfWidth = 0.08f;
|
|
|
|
|
|
d.muzzleOffset = 0.34f;
|
|
|
|
|
|
d.bulletSpeed = 15.0f;
|
|
|
|
|
|
d.bulletRadius = 0.035f;
|
|
|
|
|
|
d.knockback = 0.72f;
|
|
|
|
|
|
d.spreadStandDeg = 0.7f;
|
|
|
|
|
|
d.spreadMoveDeg = 7.5f;
|
|
|
|
|
|
d.spreadRecoilDeg = 0.0f;
|
|
|
|
|
|
d.spreadRecoilMax = 0.0f;
|
|
|
|
|
|
d.recoilDecay = 8.0f;
|
|
|
|
|
|
d.magazine = 1;
|
|
|
|
|
|
d.reloadTime = 0.72f;
|
|
|
|
|
|
d.tracerLength = 0.38f;
|
|
|
|
|
|
d.muzzleRadiusPx = 0.0f;
|
|
|
|
|
|
d.muzzleTime = 0.0f;
|
|
|
|
|
|
d.muzzleFlamePx = 0.0f;
|
|
|
|
|
|
d.flashPower = 0.0f;
|
|
|
|
|
|
d.flashRadius = 0.0f;
|
|
|
|
|
|
d.shakePerShot = 0.18f;
|
|
|
|
|
|
d.tracerR = 178; d.tracerG = 205; d.tracerB = 180;
|
|
|
|
|
|
d.muzzleR = 0; d.muzzleG = 0; d.muzzleB = 0;
|
Стихии и вторая поверхность инструментов (M21, E2)
СТИХИИ (src/sim/element.h)
Семь элементов, сопротивления свойством ВИДА, одна формула урона на пулю и на
клинок. Зачем они в игре про бесконечный спуск: сила отряда растёт медленнее
угрозы (закон масштаба, docs/20-gdd.md), и разрыв обязан закрываться знанием.
Стихии — первый такой канал: верное оружие убивает втрое быстрее, и цифра эта
не растёт от прокачки.
Ручных действий не заводится: стихия — свойство оружия, а не кнопка. Всё
решается тем, что отряд взял вниз.
Стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и сабля остаются
чистой кинетикой, потому что на них меряется приёмка.
ИНСТРУМЕНТЫ БАЛАНСА (src/debug/tools.cpp)
Вторая поверхность: `squad_proto --tool weapons|enemies|ttk|economy`. Отдельная
от редакторской не по вкусу — редактор линкуется только с движком и правил боя
не видит, а всё это считает урон. Инструменты делятся по ПРЕДМЕТУ: содержимое
у редактора, баланс у игры.
ЧТО НАШЛА ПРОВЕРКА
Первая версия --tool ttk считала разброс по времени убийства и показывала
уверенные 2.8x на каждой твари. Это была разница МОЩНОСТИ стволов: магнум лучше
пистолета против всех, стихии ни при чём.
С вынесенной за скобки мощностью оружия правда оказалась другой: 1.04-1.36x,
то есть состав удара не значил почти ничего. Механика была декоративной ровно
в том же смысле, в каком когда-то был вес.
Чтобы разброс появился, у твари должны быть И уязвимость, И сопротивление среди
тех стихий, которые вообще носит оружие: одной уязвимости мало, если ни один
ствол не бьёт тем, что она держит. После правки содержимого все пять видов в
коридоре 1.90-3.06x, и картина читается — дробовик жжёт бредущих, дуга бьёт
плевков, лук разъедает громилу, карабин против громилы бесполезен.
Коридор в инструменте 1.8-8.0, а не заявленные в документе 3-6: до тройки
дотянут почти чистые стихийные сборки, которые приходят с аффиксами. Занижать
цель в документе нельзя, поэтому в инструменте стоит достижимое.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 09:25:44 +07:00
|
|
|
|
|
|
|
|
|
|
// отравленный болт: стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и
|
|
|
|
|
|
// сабля остаются чистой кинетикой — на них меряется приёмка.
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::TOX, 0.75f);
|
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
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Крафт стал выбором: закалка, отпечаток, направленные операции (M33)
Прежние пять операций были СЛЕПЫМИ: перековка бросала всё заново,
прививка ставила случайный тег, отсечение снимало последний аффикс,
возвышение поднимало случайный. Игрок не принимал ни одного решения - жал
кнопку и смотрел, что выпало. Это не крафт, это второй бросок предмета за
материалы.
Теперь половина операций направленные: игрок показывает пальцем, какой
аффикс трогать, и платит за это вдвое - вот это уже решение
("перебросить всё дёшево или снять один дорого").
Два новых глагола:
ЗАКАЛКА двигает рулон выбранного аффикса к максимуму его тира на треть
остатка. Дёшево, понятно, повторяемо - то самое "доработать почти
хорошую вещь". Тир не трогает: за ступень платят возвышением.
ОТПЕЧАТОК переносит лучший аффикс с ДРУГОЙ вещи, сжигая её. Именно он
превращает хлам из схрона в ресурс, а не в мусор, который лежит.
Правило "один тег один раз" им не обходится, бюджет проверяется.
В мастерской: клик по вещи кладёт её на верстак, повторный клик по той же
назначает донором (помечается кровью - он сгорит, и видеть это надо ДО
нажатия). Строки аффиксов стали кнопками: по ним выбирается цель.
Проверка меряет НАПРАВЛЕННОСТЬ, а не факт срабатывания:
направленность: отсечение по цели, возвышение по цели,
закалка двигает рулон, отпечаток перенёс и сжёг донора
Отсечение пробуется на СРЕДНЕМ аффиксе из трёх: операция, которая
"работает", снимая последний, прошла бы и слепую версию.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:37:07 +07:00
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
// Вторая шестёрка стволов. Правило то же, что и у первой: НИ ОДИН не имеет права
|
|
|
|
|
|
// быть «карабином, но лучше». У каждого своя роль, и у каждой роли — своя цена,
|
|
|
|
|
|
// записанная числами, а не обещанная в комментарии.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Чем платят, по каналам:
|
|
|
|
|
|
// * дальностью (`range`) — резак и огнемёт стоят там же, где клинок;
|
|
|
|
|
|
// * простоем (`reloadTime`) — пулемёт и рельса молчат секундами;
|
|
|
|
|
|
// * разбросом на ходу (`spreadMoveDeg`) — тяжёлое с рук на бегу не работает;
|
|
|
|
|
|
// * весом (`ItemDef::weight`, sim/items.h) — пулемёт съедает норму переноски
|
|
|
|
|
|
// отряда целиком, и это его главный минус, а не масса ради массы;
|
|
|
|
|
|
// * составом удара — стихия сильнее у того, у кого хуже баллистика.
|
|
|
|
|
|
//
|
|
|
|
|
|
// ЧЕГО ЗДЕСЬ НЕТ И ПОЧЕМУ. Раскрутки (ствол разгоняется и начинает бить точнее)
|
|
|
|
|
|
// и сквозного пробития (пуля идёт дальше сквозь тело) в правилах боя нет:
|
|
|
|
|
|
// у выстрела есть откат, конус и снаряд, и всё. Заводить поля, которых никто не
|
|
|
|
|
|
// читает, значило бы положить в каталог две мёртвые ветки, поэтому обе роли
|
|
|
|
|
|
// отыграны тем, что работает: у пулемёта — магазином и простоем, у рельсы —
|
|
|
|
|
|
// пустотой (VOID и означает «защита не работает») и скоростью снаряда.
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
// Гвоздомёт: строительный инструмент, которым стреляют от бедности. Самый
|
|
|
|
|
|
// дешёвый выстрел в игре — и самый бесполезный по отдельности: пять единиц
|
|
|
|
|
|
// урона броня съедает почти целиком. Берут его не за урон, а за то, что он
|
|
|
|
|
|
// перезаряжается за секунду с четвертью и не уводит ствол, то есть работает
|
|
|
|
|
|
// там, где отряд отступает и стрелять надо ВСЁ ВРЕМЯ.
|
|
|
|
|
|
inline FirearmDef NailerDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "nailer";
|
|
|
|
|
|
d.range = 4.0f; // короче пистолета: гвоздь и не должен лететь
|
|
|
|
|
|
d.fireInterval = 0.12f;
|
|
|
|
|
|
d.damage = 4.5f;
|
|
|
|
|
|
d.laneHalfWidth = 0.10f;
|
|
|
|
|
|
d.muzzleOffset = 0.24f;
|
|
|
|
|
|
d.bulletSpeed = 13.0f; // медленный снаряд: по бегуну мимо
|
|
|
|
|
|
d.bulletRadius = 0.035f;
|
|
|
|
|
|
d.knockback = 0.10f; // тварь даже не качнётся
|
|
|
|
|
|
|
|
|
|
|
|
d.spreadStandDeg = 2.6f; // гвоздь летит криво сам по себе
|
|
|
|
|
|
d.spreadMoveDeg = 6.0f;
|
|
|
|
|
|
d.spreadRecoilDeg = 0.25f;
|
|
|
|
|
|
d.spreadRecoilMax = 2.0f;
|
|
|
|
|
|
d.recoilDecay = 6.0f; // увод оседает быстрее, чем копится
|
|
|
|
|
|
|
|
|
|
|
|
d.magazine = 60;
|
|
|
|
|
|
d.reloadTime = 1.25f; // быстрее всех: обойма гвоздей ставится одним движением
|
|
|
|
|
|
|
|
|
|
|
|
d.tracerLength = 0.45f;
|
|
|
|
|
|
d.muzzleRadiusPx = 2.5f;
|
|
|
|
|
|
d.muzzleTime = 0.04f;
|
|
|
|
|
|
d.muzzleFlamePx = 0.10f;
|
|
|
|
|
|
d.flashPower = 0.22f;
|
|
|
|
|
|
d.flashRadius = 2.0f;
|
|
|
|
|
|
d.shakePerShot = 0.12f;
|
|
|
|
|
|
d.tracerR = 198; d.tracerG = 192; d.tracerB = 176;
|
|
|
|
|
|
d.muzzleR = 210; d.muzzleG = 198; d.muzzleB = 168;
|
|
|
|
|
|
|
|
|
|
|
|
// Ржавчина на гвозде — доля кислоты крошечная: это не стихийное оружие, а
|
|
|
|
|
|
// кинетика, которой немного повезло с материалом.
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::TOX, 0.20f);
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Тяжёлый пулемёт: полтораста патронов подряд — и шесть секунд, за которые
|
|
|
|
|
|
// отряд успевает потерять бойца. Второй ствол в игре, у которого простой опаснее
|
|
|
|
|
|
// самого боя (первый — рельса).
|
|
|
|
|
|
//
|
|
|
|
|
|
// Раскрутки в правилах боя нет (см. шапку блока), и подделывать её нечем: увод
|
|
|
|
|
|
// ствола умеет только РАСТИ. Поэтому цена входа отыграна тем, что есть, —
|
|
|
|
|
|
// магазином, простоем и весом 7.5 в sim/items.h, ровно равным норме переноски
|
|
|
|
|
|
// отряда: пулемётчик один занимает всю сумку, и любая находка уже замедляет.
|
|
|
|
|
|
inline FirearmDef HmgDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "hmg";
|
|
|
|
|
|
d.range = 6.4f; // КОРОЧЕ карабина: длинный ствол не даёт дальности сам по себе
|
|
|
|
|
|
d.fireInterval = 0.10f;
|
|
|
|
|
|
d.damage = 9.0f;
|
|
|
|
|
|
d.laneHalfWidth = 0.10f;
|
|
|
|
|
|
d.muzzleOffset = 0.34f;
|
|
|
|
|
|
d.bulletSpeed = 24.0f;
|
|
|
|
|
|
d.bulletRadius = 0.045f;
|
|
|
|
|
|
d.knockback = 0.30f;
|
|
|
|
|
|
|
|
|
|
|
|
d.spreadStandDeg = 1.4f;
|
|
|
|
|
|
d.spreadMoveDeg = 15.0f; // на ходу это стрельба в белый свет
|
|
|
|
|
|
d.spreadRecoilDeg = 0.35f;
|
|
|
|
|
|
d.spreadRecoilMax = 5.5f;
|
|
|
|
|
|
d.recoilDecay = 2.4f; // увод оседает медленнее, чем у любого другого ствола
|
|
|
|
|
|
|
|
|
|
|
|
d.magazine = 150;
|
|
|
|
|
|
d.reloadTime = 6.2f; // самое дорогое молчание в каталоге
|
|
|
|
|
|
|
|
|
|
|
|
d.tracerLength = 1.45f;
|
|
|
|
|
|
d.muzzleRadiusPx = 6.0f;
|
|
|
|
|
|
d.muzzleTime = 0.06f;
|
|
|
|
|
|
d.muzzleFlamePx = 0.40f;
|
|
|
|
|
|
d.flashPower = 0.62f;
|
|
|
|
|
|
d.flashRadius = 4.4f;
|
|
|
|
|
|
d.shakePerShot = 0.30f;
|
|
|
|
|
|
d.tracerR = 255; d.tracerG = 226; d.tracerB = 176;
|
|
|
|
|
|
d.muzzleR = 255; d.muzzleG = 214; d.muzzleB = 140;
|
|
|
|
|
|
|
|
|
|
|
|
// Обеднённый сердечник: доля радиации небольшая, но очередь кладёт её
|
|
|
|
|
|
// стаками — единственный ствол, который набивает статус САМ, без сборки.
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::RAD, 0.30f);
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Плазменный резак: горная техника, приспособленная под бой. Дальность 2.6 —
|
|
|
|
|
|
// меньше, чем у дробовика, то есть стрелок с резаком стоит ровно там же, где
|
|
|
|
|
|
// боец с клинком, и получает всё, что прилетает в первую линию. Взамен почти
|
|
|
|
|
|
// чистая дуга: против того, кто держит кинетику, это единственный ответ
|
|
|
|
|
|
// с ЭТОГО расстояния.
|
|
|
|
|
|
inline FirearmDef PlasmaDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "plasma";
|
|
|
|
|
|
d.range = 2.6f; // короче всех в игре
|
|
|
|
|
|
d.fireInterval = 0.34f;
|
|
|
|
|
|
d.damage = 30.0f;
|
|
|
|
|
|
d.laneHalfWidth = 0.10f;
|
|
|
|
|
|
d.muzzleOffset = 0.28f;
|
|
|
|
|
|
d.bulletSpeed = 26.0f;
|
|
|
|
|
|
d.bulletRadius = 0.055f;
|
|
|
|
|
|
d.knockback = 0.15f; // струя не толкает: тварь дойдёт вплотную и останется
|
|
|
|
|
|
|
|
|
|
|
|
d.spreadStandDeg = 0.5f; // луч точный — толку от этого на 2.6 немного
|
|
|
|
|
|
d.spreadMoveDeg = 5.0f;
|
|
|
|
|
|
d.spreadRecoilDeg = 0.0f; // отдачи нет вовсе
|
|
|
|
|
|
d.spreadRecoilMax = 0.0f;
|
|
|
|
|
|
d.recoilDecay = 8.0f;
|
|
|
|
|
|
|
|
|
|
|
|
d.magazine = 14;
|
|
|
|
|
|
d.reloadTime = 2.9f;
|
|
|
|
|
|
|
|
|
|
|
|
d.tracerLength = 0.60f;
|
|
|
|
|
|
d.muzzleRadiusPx = 5.5f;
|
|
|
|
|
|
d.muzzleTime = 0.08f;
|
|
|
|
|
|
d.muzzleFlamePx = 0.30f;
|
|
|
|
|
|
d.flashPower = 0.90f; // светит сильнее, чем стреляет: резак виден издалека
|
|
|
|
|
|
d.flashRadius = 5.0f;
|
|
|
|
|
|
d.shakePerShot = 0.25f;
|
|
|
|
|
|
d.tracerR = 150; d.tracerG = 230; d.tracerB = 255;
|
|
|
|
|
|
d.muzzleR = 190; d.muzzleG = 240; d.muzzleB = 255;
|
|
|
|
|
|
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::ARC, 0.90f);
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Рельса: три выстрела и почти пять секунд на перезарядку. Бьёт дальше всего в
|
|
|
|
|
|
// игре (11.5 против 8.6 у магнума), и это её единственное безусловное
|
|
|
|
|
|
// достоинство — всё остальное отнято.
|
|
|
|
|
|
//
|
|
|
|
|
|
// «Пробивает» здесь означает НЕ сквозное попадание (такого в правилах боя нет),
|
|
|
|
|
|
// а пустоту: VOID срезает сопротивления, то есть защита цели перестаёт работать.
|
|
|
|
|
|
// Заводить сквозной снаряд ради одного ствола значило бы менять правило 6.3 —
|
|
|
|
|
|
// пуля, летящая ДАЛЬШЕ цели, перестаёт быть проверенной на своих.
|
|
|
|
|
|
inline FirearmDef RailDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "rail";
|
|
|
|
|
|
d.range = 11.5f;
|
|
|
|
|
|
d.fireInterval = 1.70f; // между выстрелами тварь проходит три шага
|
|
|
|
|
|
d.damage = 170.0f;
|
|
|
|
|
|
d.laneHalfWidth = 0.08f;
|
|
|
|
|
|
d.muzzleOffset = 0.36f;
|
|
|
|
|
|
d.bulletSpeed = 48.0f; // быстрее всех: на такой дальности иначе не попасть
|
|
|
|
|
|
d.bulletRadius = 0.045f;
|
|
|
|
|
|
d.knockback = 2.20f;
|
|
|
|
|
|
|
|
|
|
|
|
d.spreadStandDeg = 0.20f; // со станка — точнее некуда
|
|
|
|
|
|
d.spreadMoveDeg = 14.0f; // с рук на ходу — выброшенный выстрел из трёх
|
|
|
|
|
|
d.spreadRecoilDeg = 3.00f;
|
|
|
|
|
|
d.spreadRecoilMax = 7.00f;
|
|
|
|
|
|
d.recoilDecay = 2.00f; // второй выстрел подряд уже не точный
|
|
|
|
|
|
|
|
|
|
|
|
d.magazine = 3;
|
|
|
|
|
|
d.reloadTime = 4.60f;
|
|
|
|
|
|
|
|
|
|
|
|
d.tracerLength = 3.20f; // след через полкомнаты
|
|
|
|
|
|
d.muzzleRadiusPx = 5.0f;
|
|
|
|
|
|
d.muzzleTime = 0.11f;
|
|
|
|
|
|
d.muzzleFlamePx = 0.20f;
|
|
|
|
|
|
d.flashPower = 0.95f;
|
|
|
|
|
|
d.flashRadius = 6.0f;
|
|
|
|
|
|
d.shakePerShot = 2.20f; // трясёт сильнее магнума
|
|
|
|
|
|
d.tracerR = 214; d.tracerG = 208; d.tracerB = 255;
|
|
|
|
|
|
d.muzzleR = 200; d.muzzleG = 196; d.muzzleB = 255;
|
|
|
|
|
|
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::VOID, 0.70f);
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Огнемёт: конус в упор. Конус — это `pellets` в широком разбросе, то есть тот
|
|
|
|
|
|
// же механизм, что у картечи, и та же цена, только больше: пятнадцать градусов
|
|
|
|
|
|
// проверяются правилом 6.3 на своих КАЖДЫЙ спуск, и в свалке огнемётчик молчит
|
|
|
|
|
|
// чаще, чем стреляет. Это не недоработка, а весь смысл вещи: жар требует места.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Урон за язык мизерный, ставка целиком на горение, а горение считается от
|
|
|
|
|
|
// МАКСИМАЛЬНОГО здоровья цели (docs/21-damage.md) — значит по мелочи огнемёт
|
|
|
|
|
|
// перерасход, а по громиле единственный смысл.
|
|
|
|
|
|
inline FirearmDef FlamerDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "flamer";
|
|
|
|
|
|
d.range = 2.4f;
|
|
|
|
|
|
d.fireInterval = 0.16f;
|
|
|
|
|
|
d.damage = 7.0f; // на язык пламени
|
|
|
|
|
|
d.laneHalfWidth = 0.10f;
|
|
|
|
|
|
d.muzzleOffset = 0.30f;
|
|
|
|
|
|
d.bulletSpeed = 9.0f; // языки ползут: по бегуну на отходе мимо
|
|
|
|
|
|
d.bulletRadius = 0.07f;
|
|
|
|
|
|
d.knockback = 0.05f;
|
|
|
|
|
|
|
|
|
|
|
|
d.spreadStandDeg = 15.0f; // это и есть конус
|
|
|
|
|
|
d.spreadMoveDeg = 19.0f;
|
|
|
|
|
|
d.spreadRecoilDeg = 0.0f;
|
|
|
|
|
|
d.spreadRecoilMax = 0.0f;
|
|
|
|
|
|
d.recoilDecay = 8.0f;
|
|
|
|
|
|
|
|
|
|
|
|
d.magazine = 100;
|
|
|
|
|
|
d.reloadTime = 3.6f;
|
|
|
|
|
|
d.pellets = 3;
|
|
|
|
|
|
|
|
|
|
|
|
d.tracerLength = 0.30f;
|
|
|
|
|
|
d.muzzleRadiusPx = 8.0f;
|
|
|
|
|
|
d.muzzleTime = 0.09f;
|
|
|
|
|
|
d.muzzleFlamePx = 0.75f; // самый длинный язык в игре
|
|
|
|
|
|
d.flashPower = 0.75f;
|
|
|
|
|
|
d.flashRadius = 4.8f;
|
|
|
|
|
|
d.shakePerShot = 0.10f;
|
|
|
|
|
|
d.tracerR = 255; d.tracerG = 168; d.tracerB = 70;
|
|
|
|
|
|
d.muzzleR = 255; d.muzzleG = 150; d.muzzleB = 60;
|
|
|
|
|
|
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::INC, 0.95f);
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Кислотный распылитель: две медленные капли за спуск. Урон за каплю смешной,
|
|
|
|
|
|
// зато почти всё в нём — кислота, а кислота работает по тому, по чему не
|
|
|
|
|
|
// работает ничего: по жирному и бронированному. По бегуну и по всему, что
|
|
|
|
|
|
// движется поперёк, капли просто не попадают — скорость снаряда 11.
|
|
|
|
|
|
inline FirearmDef AcidDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
FirearmDef d;
|
|
|
|
|
|
d.name = "acid";
|
|
|
|
|
|
d.range = 5.0f;
|
|
|
|
|
|
d.fireInterval = 0.40f;
|
|
|
|
|
|
d.damage = 16.0f; // на каплю
|
|
|
|
|
|
d.laneHalfWidth = 0.10f;
|
|
|
|
|
|
d.muzzleOffset = 0.28f;
|
|
|
|
|
|
d.bulletSpeed = 11.0f;
|
|
|
|
|
|
d.bulletRadius = 0.06f;
|
|
|
|
|
|
d.knockback = 0.10f;
|
|
|
|
|
|
|
|
|
|
|
|
d.spreadStandDeg = 6.0f;
|
|
|
|
|
|
d.spreadMoveDeg = 11.0f;
|
|
|
|
|
|
d.spreadRecoilDeg = 0.30f;
|
|
|
|
|
|
d.spreadRecoilMax = 3.5f;
|
|
|
|
|
|
d.recoilDecay = 4.0f;
|
|
|
|
|
|
|
|
|
|
|
|
d.magazine = 18;
|
|
|
|
|
|
d.reloadTime = 2.9f;
|
|
|
|
|
|
d.pellets = 2;
|
|
|
|
|
|
|
|
|
|
|
|
d.tracerLength = 0.35f;
|
|
|
|
|
|
d.muzzleRadiusPx = 4.0f;
|
|
|
|
|
|
d.muzzleTime = 0.05f;
|
|
|
|
|
|
d.muzzleFlamePx = 0.0f; // не порох: вспышки нет
|
|
|
|
|
|
d.flashPower = 0.20f;
|
|
|
|
|
|
d.flashRadius = 2.4f;
|
|
|
|
|
|
d.shakePerShot = 0.14f;
|
|
|
|
|
|
d.tracerR = 176; d.tracerG = 226; d.tracerB = 110;
|
|
|
|
|
|
d.muzzleR = 150; d.muzzleG = 210; d.muzzleB = 96;
|
|
|
|
|
|
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::TOX, 0.85f);
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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
|
|
|
|
inline FirearmDef FirearmDefOf(FirearmId id)
|
|
|
|
|
|
{
|
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
|
|
|
|
switch (id)
|
|
|
|
|
|
{
|
|
|
|
|
|
case FirearmId::PISTOL: return PistolDef();
|
|
|
|
|
|
case FirearmId::SHOTGUN: return ShotgunDef();
|
|
|
|
|
|
case FirearmId::SMG: return SmgDef();
|
|
|
|
|
|
case FirearmId::MAGNUM: return MagnumDef();
|
|
|
|
|
|
case FirearmId::BOW: return BowDef();
|
Крафт стал выбором: закалка, отпечаток, направленные операции (M33)
Прежние пять операций были СЛЕПЫМИ: перековка бросала всё заново,
прививка ставила случайный тег, отсечение снимало последний аффикс,
возвышение поднимало случайный. Игрок не принимал ни одного решения - жал
кнопку и смотрел, что выпало. Это не крафт, это второй бросок предмета за
материалы.
Теперь половина операций направленные: игрок показывает пальцем, какой
аффикс трогать, и платит за это вдвое - вот это уже решение
("перебросить всё дёшево или снять один дорого").
Два новых глагола:
ЗАКАЛКА двигает рулон выбранного аффикса к максимуму его тира на треть
остатка. Дёшево, понятно, повторяемо - то самое "доработать почти
хорошую вещь". Тир не трогает: за ступень платят возвышением.
ОТПЕЧАТОК переносит лучший аффикс с ДРУГОЙ вещи, сжигая её. Именно он
превращает хлам из схрона в ресурс, а не в мусор, который лежит.
Правило "один тег один раз" им не обходится, бюджет проверяется.
В мастерской: клик по вещи кладёт её на верстак, повторный клик по той же
назначает донором (помечается кровью - он сгорит, и видеть это надо ДО
нажатия). Строки аффиксов стали кнопками: по ним выбирается цель.
Проверка меряет НАПРАВЛЕННОСТЬ, а не факт срабатывания:
направленность: отсечение по цели, возвышение по цели,
закалка двигает рулон, отпечаток перенёс и сжёг донора
Отсечение пробуется на СРЕДНЕМ аффиксе из трёх: операция, которая
"работает", снимая последний, прошла бы и слепую версию.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:37:07 +07:00
|
|
|
|
case FirearmId::NAILER: return NailerDef();
|
|
|
|
|
|
case FirearmId::HMG: return HmgDef();
|
|
|
|
|
|
case FirearmId::PLASMA: return PlasmaDef();
|
|
|
|
|
|
case FirearmId::RAIL: return RailDef();
|
|
|
|
|
|
case FirearmId::FLAMER: return FlamerDef();
|
|
|
|
|
|
case FirearmId::ACID: return AcidDef();
|
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
|
|
|
|
default: return RifleDef();
|
|
|
|
|
|
}
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline const char* FirearmName(FirearmId id)
|
|
|
|
|
|
{
|
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
|
|
|
|
switch (id)
|
|
|
|
|
|
{
|
|
|
|
|
|
case FirearmId::PISTOL: return "PISTOL";
|
|
|
|
|
|
case FirearmId::SHOTGUN: return "SHOTGUN";
|
|
|
|
|
|
case FirearmId::SMG: return "SMG";
|
|
|
|
|
|
case FirearmId::MAGNUM: return "MAGNUM";
|
|
|
|
|
|
case FirearmId::BOW: return "BOW";
|
Крафт стал выбором: закалка, отпечаток, направленные операции (M33)
Прежние пять операций были СЛЕПЫМИ: перековка бросала всё заново,
прививка ставила случайный тег, отсечение снимало последний аффикс,
возвышение поднимало случайный. Игрок не принимал ни одного решения - жал
кнопку и смотрел, что выпало. Это не крафт, это второй бросок предмета за
материалы.
Теперь половина операций направленные: игрок показывает пальцем, какой
аффикс трогать, и платит за это вдвое - вот это уже решение
("перебросить всё дёшево или снять один дорого").
Два новых глагола:
ЗАКАЛКА двигает рулон выбранного аффикса к максимуму его тира на треть
остатка. Дёшево, понятно, повторяемо - то самое "доработать почти
хорошую вещь". Тир не трогает: за ступень платят возвышением.
ОТПЕЧАТОК переносит лучший аффикс с ДРУГОЙ вещи, сжигая её. Именно он
превращает хлам из схрона в ресурс, а не в мусор, который лежит.
Правило "один тег один раз" им не обходится, бюджет проверяется.
В мастерской: клик по вещи кладёт её на верстак, повторный клик по той же
назначает донором (помечается кровью - он сгорит, и видеть это надо ДО
нажатия). Строки аффиксов стали кнопками: по ним выбирается цель.
Проверка меряет НАПРАВЛЕННОСТЬ, а не факт срабатывания:
направленность: отсечение по цели, возвышение по цели,
закалка двигает рулон, отпечаток перенёс и сжёг донора
Отсечение пробуется на СРЕДНЕМ аффиксе из трёх: операция, которая
"работает", снимая последний, прошла бы и слепую версию.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:37:07 +07:00
|
|
|
|
case FirearmId::NAILER: return "NAILER";
|
|
|
|
|
|
case FirearmId::HMG: return "HMG";
|
|
|
|
|
|
case FirearmId::PLASMA: return "PLASMA";
|
|
|
|
|
|
case FirearmId::RAIL: return "RAIL";
|
|
|
|
|
|
case FirearmId::FLAMER: return "FLAMER";
|
|
|
|
|
|
case FirearmId::ACID: return "ACID";
|
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
|
|
|
|
default: return "RIFLE";
|
|
|
|
|
|
}
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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
|
|
|
|
// Штатный клинок: числа целиком в tuning, ручки оверлея крутят именно его.
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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
|
|
|
|
inline BladeDef DefaultBladeDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
BladeDef d;
|
|
|
|
|
|
d.reach = g_tune.meleeReach;
|
|
|
|
|
|
d.arcDeg = g_tune.meleeArcDeg;
|
|
|
|
|
|
d.windup = g_tune.meleeWindup;
|
|
|
|
|
|
d.strike = g_tune.meleeStrike;
|
|
|
|
|
|
d.recover = g_tune.meleeRecover;
|
|
|
|
|
|
d.interval = g_tune.meleeInterval;
|
|
|
|
|
|
d.damage = g_tune.meleeDamage;
|
|
|
|
|
|
d.knockback = g_tune.meleeKnockback;
|
|
|
|
|
|
d.lungeSpeed = g_tune.meleeLungeSpeed;
|
Личинки, парирование и инструмент --wall (M30)
--WALL. Новый прогон отвечает на вопрос, появившийся вместе с чекпоинтами
через пятьдесят этажей: растёт ли достижимая глубина от вылазки к вылазке.
Отряд из одного, трёх и пяти, одетый по глубине, ставится в скопление
тварей и живёт полторы минуты без управления. Первая стена: одиночка D7,
трое D30, пятеро D30 - петля работает.
И он же нашёл настоящую ошибку: PlaceSquad воскрешал ВСЕ пять слотов, в
том числе незанятые. Строка писалась, когда отряд всегда был впятером;
после появления ростера одиночка выходил на этаж с четырьмя невидимыми
телохранителями. Из-за них прежний замер "одиночка выжил на первом этаже,
100 -> 41" был боем впятером.
ЛИЧИНКИ. С исправленным замером выяснилось, что одиночка с ножом
проигрывает двум бредущим: 210 здоровья против ножа это девять секунд
работы под ответным уроном. Первые этажи населены личинками (55 здоровья,
7 урона, медленные), бредущий уехал на третий этаж, бегун на четвёртый.
Новый ВИД, а не ослабленный бредущий с множителем: глубина в этой игре
меняет, кто выходит, а не числа у одного и того же.
ПАРИРОВАНИЕ. Отдельная характеристика клинка, и чем оружие легче, тем она
выше: нож 34%, сабля 20%, тесак 8%. Не выведена из веса намеренно - её
двигает аффикс PARRY, и лёгкий тесак обязан быть возможен как находка.
Отбить можно, только если удары СОПРИКОСНУЛИСЬ: боец сам должен быть в
замахе или в ударе. Иначе клинок стал бы щитом, работающим, пока стоишь.
Удавшийся отбив снимает урон, бьёт тварь ударом бойца целиком и запускает
повторную атаку с начала фазы, НЕ сбрасывая откат. Последнее и есть суть
размена: сбрасывай отбив откат, выгоднее было бы подставляться под удары.
Плевок отбивается тоже: летит назад и несёт урон бойца, а не твари. Это
его удар, просто по летящей цели.
Проверка меряет размен целиком, включая обратную половину правила:
"стоя столбом не отбивает". Без неё условие про соприкосновение
существовало бы только в комментарии.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 09:04:35 +07:00
|
|
|
|
d.parry = 0.20f; // сабля: середина между ножом и тесаком
|
Прототип боевого ядра: отряд, хоррор-слой, ближний бой
Тактический отряд с автоматическим огнём в изометрии, собственный
софтверный растеризатор (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
|
|
|
|
return d;
|
|
|
|
|
|
}
|
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
|
|
|
|
|
|
|
|
|
|
// Нож: узкая дуга и почти нет замаха. В тесноте это решает — сектор шириной
|
|
|
|
|
|
// в ладонь почти всегда чист, тогда как тесак некуда занести.
|
|
|
|
|
|
inline BladeDef KnifeDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
BladeDef d;
|
|
|
|
|
|
d.name = "knife";
|
|
|
|
|
|
d.reach = 1.00f;
|
|
|
|
|
|
d.arcDeg = 55.0f;
|
|
|
|
|
|
d.windup = 0.08f;
|
|
|
|
|
|
d.strike = 0.09f;
|
|
|
|
|
|
d.recover = 0.13f;
|
|
|
|
|
|
d.interval = 0.34f;
|
|
|
|
|
|
d.damage = 62.0f;
|
Личинки, парирование и инструмент --wall (M30)
--WALL. Новый прогон отвечает на вопрос, появившийся вместе с чекпоинтами
через пятьдесят этажей: растёт ли достижимая глубина от вылазки к вылазке.
Отряд из одного, трёх и пяти, одетый по глубине, ставится в скопление
тварей и живёт полторы минуты без управления. Первая стена: одиночка D7,
трое D30, пятеро D30 - петля работает.
И он же нашёл настоящую ошибку: PlaceSquad воскрешал ВСЕ пять слотов, в
том числе незанятые. Строка писалась, когда отряд всегда был впятером;
после появления ростера одиночка выходил на этаж с четырьмя невидимыми
телохранителями. Из-за них прежний замер "одиночка выжил на первом этаже,
100 -> 41" был боем впятером.
ЛИЧИНКИ. С исправленным замером выяснилось, что одиночка с ножом
проигрывает двум бредущим: 210 здоровья против ножа это девять секунд
работы под ответным уроном. Первые этажи населены личинками (55 здоровья,
7 урона, медленные), бредущий уехал на третий этаж, бегун на четвёртый.
Новый ВИД, а не ослабленный бредущий с множителем: глубина в этой игре
меняет, кто выходит, а не числа у одного и того же.
ПАРИРОВАНИЕ. Отдельная характеристика клинка, и чем оружие легче, тем она
выше: нож 34%, сабля 20%, тесак 8%. Не выведена из веса намеренно - её
двигает аффикс PARRY, и лёгкий тесак обязан быть возможен как находка.
Отбить можно, только если удары СОПРИКОСНУЛИСЬ: боец сам должен быть в
замахе или в ударе. Иначе клинок стал бы щитом, работающим, пока стоишь.
Удавшийся отбив снимает урон, бьёт тварь ударом бойца целиком и запускает
повторную атаку с начала фазы, НЕ сбрасывая откат. Последнее и есть суть
размена: сбрасывай отбив откат, выгоднее было бы подставляться под удары.
Плевок отбивается тоже: летит назад и несёт урон бойца, а не твари. Это
его удар, просто по летящей цели.
Проверка меряет размен целиком, включая обратную половину правила:
"стоя столбом не отбивает". Без неё условие про соприкосновение
существовало бы только в комментарии.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 09:04:35 +07:00
|
|
|
|
d.parry = 0.34f; // нож: почти всегда успевает подставиться
|
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
|
|
|
|
d.knockback = 0.9f;
|
|
|
|
|
|
d.lungeSpeed = 4.2f;
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Тесак: широкий замах, тяжёлый урон, долгое восстановление. Своих распугивает
|
|
|
|
|
|
// сам по себе — резерв сектора у него больше всех.
|
|
|
|
|
|
inline BladeDef CleaverDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
BladeDef d;
|
|
|
|
|
|
d.name = "cleaver";
|
|
|
|
|
|
d.reach = 1.65f;
|
|
|
|
|
|
d.arcDeg = 135.0f;
|
|
|
|
|
|
d.windup = 0.30f;
|
|
|
|
|
|
d.strike = 0.18f;
|
|
|
|
|
|
d.recover = 0.42f;
|
|
|
|
|
|
d.interval = 0.95f;
|
|
|
|
|
|
d.damage = 240.0f;
|
Личинки, парирование и инструмент --wall (M30)
--WALL. Новый прогон отвечает на вопрос, появившийся вместе с чекпоинтами
через пятьдесят этажей: растёт ли достижимая глубина от вылазки к вылазке.
Отряд из одного, трёх и пяти, одетый по глубине, ставится в скопление
тварей и живёт полторы минуты без управления. Первая стена: одиночка D7,
трое D30, пятеро D30 - петля работает.
И он же нашёл настоящую ошибку: PlaceSquad воскрешал ВСЕ пять слотов, в
том числе незанятые. Строка писалась, когда отряд всегда был впятером;
после появления ростера одиночка выходил на этаж с четырьмя невидимыми
телохранителями. Из-за них прежний замер "одиночка выжил на первом этаже,
100 -> 41" был боем впятером.
ЛИЧИНКИ. С исправленным замером выяснилось, что одиночка с ножом
проигрывает двум бредущим: 210 здоровья против ножа это девять секунд
работы под ответным уроном. Первые этажи населены личинками (55 здоровья,
7 урона, медленные), бредущий уехал на третий этаж, бегун на четвёртый.
Новый ВИД, а не ослабленный бредущий с множителем: глубина в этой игре
меняет, кто выходит, а не числа у одного и того же.
ПАРИРОВАНИЕ. Отдельная характеристика клинка, и чем оружие легче, тем она
выше: нож 34%, сабля 20%, тесак 8%. Не выведена из веса намеренно - её
двигает аффикс PARRY, и лёгкий тесак обязан быть возможен как находка.
Отбить можно, только если удары СОПРИКОСНУЛИСЬ: боец сам должен быть в
замахе или в ударе. Иначе клинок стал бы щитом, работающим, пока стоишь.
Удавшийся отбив снимает урон, бьёт тварь ударом бойца целиком и запускает
повторную атаку с начала фазы, НЕ сбрасывая откат. Последнее и есть суть
размена: сбрасывай отбив откат, выгоднее было бы подставляться под удары.
Плевок отбивается тоже: летит назад и несёт урон бойца, а не твари. Это
его удар, просто по летящей цели.
Проверка меряет размен целиком, включая обратную половину правила:
"стоя столбом не отбивает". Без неё условие про соприкосновение
существовало бы только в комментарии.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 09:04:35 +07:00
|
|
|
|
d.parry = 0.08f; // тесак: им бьют, а не защищаются
|
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
|
|
|
|
d.knockback = 4.6f;
|
|
|
|
|
|
d.lungeSpeed = 2.8f;
|
Стихии и вторая поверхность инструментов (M21, E2)
СТИХИИ (src/sim/element.h)
Семь элементов, сопротивления свойством ВИДА, одна формула урона на пулю и на
клинок. Зачем они в игре про бесконечный спуск: сила отряда растёт медленнее
угрозы (закон масштаба, docs/20-gdd.md), и разрыв обязан закрываться знанием.
Стихии — первый такой канал: верное оружие убивает втрое быстрее, и цифра эта
не растёт от прокачки.
Ручных действий не заводится: стихия — свойство оружия, а не кнопка. Всё
решается тем, что отряд взял вниз.
Стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и сабля остаются
чистой кинетикой, потому что на них меряется приёмка.
ИНСТРУМЕНТЫ БАЛАНСА (src/debug/tools.cpp)
Вторая поверхность: `squad_proto --tool weapons|enemies|ttk|economy`. Отдельная
от редакторской не по вкусу — редактор линкуется только с движком и правил боя
не видит, а всё это считает урон. Инструменты делятся по ПРЕДМЕТУ: содержимое
у редактора, баланс у игры.
ЧТО НАШЛА ПРОВЕРКА
Первая версия --tool ttk считала разброс по времени убийства и показывала
уверенные 2.8x на каждой твари. Это была разница МОЩНОСТИ стволов: магнум лучше
пистолета против всех, стихии ни при чём.
С вынесенной за скобки мощностью оружия правда оказалась другой: 1.04-1.36x,
то есть состав удара не значил почти ничего. Механика была декоративной ровно
в том же смысле, в каком когда-то был вес.
Чтобы разброс появился, у твари должны быть И уязвимость, И сопротивление среди
тех стихий, которые вообще носит оружие: одной уязвимости мало, если ни один
ствол не бьёт тем, что она держит. После правки содержимого все пять видов в
коридоре 1.90-3.06x, и картина читается — дробовик жжёт бредущих, дуга бьёт
плевков, лук разъедает громилу, карабин против громилы бесполезен.
Коридор в инструменте 1.8-8.0, а не заявленные в документе 3-6: до тройки
дотянут почти чистые стихийные сборки, которые приходят с аффиксами. Занижать
цель в документе нельзя, поэтому в инструменте стоит достижимое.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 09:25:44 +07:00
|
|
|
|
|
|
|
|
|
|
// холодная сталь тесака: стихии есть только у НАХОДИМОГО оружия. Карабин, пистолет и
|
|
|
|
|
|
// сабля остаются чистой кинетикой — на них меряется приёмка.
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::CRY, 0.55f);
|
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
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Крафт стал выбором: закалка, отпечаток, направленные операции (M33)
Прежние пять операций были СЛЕПЫМИ: перековка бросала всё заново,
прививка ставила случайный тег, отсечение снимало последний аффикс,
возвышение поднимало случайный. Игрок не принимал ни одного решения - жал
кнопку и смотрел, что выпало. Это не крафт, это второй бросок предмета за
материалы.
Теперь половина операций направленные: игрок показывает пальцем, какой
аффикс трогать, и платит за это вдвое - вот это уже решение
("перебросить всё дёшево или снять один дорого").
Два новых глагола:
ЗАКАЛКА двигает рулон выбранного аффикса к максимуму его тира на треть
остатка. Дёшево, понятно, повторяемо - то самое "доработать почти
хорошую вещь". Тир не трогает: за ступень платят возвышением.
ОТПЕЧАТОК переносит лучший аффикс с ДРУГОЙ вещи, сжигая её. Именно он
превращает хлам из схрона в ресурс, а не в мусор, который лежит.
Правило "один тег один раз" им не обходится, бюджет проверяется.
В мастерской: клик по вещи кладёт её на верстак, повторный клик по той же
назначает донором (помечается кровью - он сгорит, и видеть это надо ДО
нажатия). Строки аффиксов стали кнопками: по ним выбирается цель.
Проверка меряет НАПРАВЛЕННОСТЬ, а не факт срабатывания:
направленность: отсечение по цели, возвышение по цели,
закалка двигает рулон, отпечаток перенёс и сжёг донора
Отсечение пробуется на СРЕДНЕМ аффиксе из трёх: операция, которая
"работает", снимая последний, прошла бы и слепую версию.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:37:07 +07:00
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
// Вторая четвёрка клинков. Ось, вдоль которой они разложены, одна и та же:
|
|
|
|
|
|
// ВЕС ПРОТИВ ПАРИРОВАНИЯ (docs/11-melee.md). Стилет 0.3 кг держит чужой удар в
|
|
|
|
|
|
// половине случаев, молот 5.5 кг не держит его никогда — и это ровно тот размен,
|
|
|
|
|
|
// из-за которого «взять потяжелее» перестаёт быть очевидным решением.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Соотношение с уже имеющимися: нож 0.4 -> 0.34, сабля 1.2 -> 0.20,
|
|
|
|
|
|
// тесак 2.2 -> 0.08. Новые садятся между ними, а не поверх.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Второй общий размен — УРОН ПРОТИВ СТИХИИ: у крио-клинка и цепного урон
|
|
|
|
|
|
// заметно ниже сабельного, и разницу они отдали за состав удара. Клинок с
|
|
|
|
|
|
// полным уроном И полной стихией был бы просто лучшей саблей.
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
// Стилет: фехтовальная игла с гардой. Парирование 0.52 — потолок каталога, и
|
|
|
|
|
|
// это единственное, ради чего его берут.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Цена честная и вся сразу: самый слабый удар в игре (44 против 62 у ножа),
|
|
|
|
|
|
// досягаемость 0.85 — короче не бывает, значит бить приходится ВНУТРИ круга
|
|
|
|
|
|
// твари, — и отброс почти нулевой, то есть отбитая тварь остаётся стоять
|
|
|
|
|
|
// вплотную и бьёт снова. Стилетом выживают, а не убивают.
|
|
|
|
|
|
inline BladeDef StilettoDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
BladeDef d;
|
|
|
|
|
|
d.name = "stiletto";
|
|
|
|
|
|
d.reach = 0.85f;
|
|
|
|
|
|
d.arcDeg = 42.0f; // уже ножа: в тесноте сектор почти всегда чист
|
|
|
|
|
|
d.windup = 0.06f;
|
|
|
|
|
|
d.strike = 0.07f;
|
|
|
|
|
|
d.recover = 0.11f;
|
|
|
|
|
|
d.interval = 0.28f;
|
|
|
|
|
|
d.damage = 44.0f;
|
|
|
|
|
|
d.parry = 0.52f;
|
|
|
|
|
|
d.knockback = 0.35f;
|
|
|
|
|
|
d.lungeSpeed = 4.8f;
|
|
|
|
|
|
|
|
|
|
|
|
// Игла ищет щель в защите — доля пустоты маленькая, но именно она делает
|
|
|
|
|
|
// стилет пригодным против того, кто равномерно защищён.
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::VOID, 0.30f);
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Молот: 430 за удар — больше, чем у чего угодно в игре, и отброс 7.5, который
|
|
|
|
|
|
// выносит тварь из свалки целиком.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Платит всем остальным. Замах 0.46 — вдвое длиннее тесачного, и на нём боец
|
|
|
|
|
|
// вкопан; восстановление 0.62 — почти две трети секунды открытым. Парирование
|
|
|
|
|
|
// 0.02 означает, что размен «удар на удар» молот проигрывает всегда, поэтому
|
|
|
|
|
|
// бить он обязан ПЕРВЫМ. И удар у него ЧИСТО КИНЕТИЧЕСКИЙ: там, где цель режет
|
|
|
|
|
|
// кинетику бронёй или сопротивлением, четыреста тридцать превращаются в сотню,
|
|
|
|
|
|
// а стихии, чтобы обойти это, у молота нет.
|
|
|
|
|
|
inline BladeDef MaulDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
BladeDef d;
|
|
|
|
|
|
d.name = "maul";
|
|
|
|
|
|
d.reach = 1.55f;
|
|
|
|
|
|
d.arcDeg = 105.0f;
|
|
|
|
|
|
d.windup = 0.46f;
|
|
|
|
|
|
d.strike = 0.20f;
|
|
|
|
|
|
d.recover = 0.62f;
|
|
|
|
|
|
d.interval = 1.45f;
|
|
|
|
|
|
d.damage = 430.0f;
|
|
|
|
|
|
d.parry = 0.02f;
|
|
|
|
|
|
d.knockback = 7.50f;
|
|
|
|
|
|
d.lungeSpeed = 1.90f; // рывка почти нет: с молотом не бегают
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Цепной меч: зубья вместо лезвия, укус вместо удара. Бьёт чаще всех (0.24 с
|
|
|
|
|
|
// между началами замахов) и слабее почти всех — 42 за укус.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Из этого следует его минус, и он не в цифрах, а в арифметике урона: сорок два
|
|
|
|
|
|
// умножаются на сопротивление ДО того, как что-нибудь наберётся, поэтому против
|
|
|
|
|
|
// защищённой цели цепь стачивается в ноль. Плюс парирование 0.10: цепь идёт
|
|
|
|
|
|
// сама по себе и подставить её под чужой клинок нельзя — она уводит удар в
|
|
|
|
|
|
// сторону вместе с рукой.
|
|
|
|
|
|
inline BladeDef RipperDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
BladeDef d;
|
|
|
|
|
|
d.name = "ripper";
|
|
|
|
|
|
d.reach = 1.25f;
|
|
|
|
|
|
d.arcDeg = 70.0f;
|
|
|
|
|
|
d.windup = 0.09f;
|
|
|
|
|
|
d.strike = 0.10f;
|
|
|
|
|
|
d.recover = 0.15f;
|
|
|
|
|
|
d.interval = 0.24f;
|
|
|
|
|
|
d.damage = 42.0f;
|
|
|
|
|
|
d.parry = 0.10f;
|
|
|
|
|
|
d.knockback = 0.50f;
|
|
|
|
|
|
d.lungeSpeed = 3.40f;
|
|
|
|
|
|
|
|
|
|
|
|
// Смазка на зубьях: частый удар набивает стаки кислоты быстрее всего в игре,
|
|
|
|
|
|
// и в этом весь смысл вещи — не в уроне, а в скорости накопления статуса.
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::TOX, 0.70f);
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Крио-клинок: та же сабля, отдавшая четверть урона за холод. 104 против 130 —
|
|
|
|
|
|
// разница и есть цена стихии, и по цели, которая холод держит, это просто
|
|
|
|
|
|
// плохая сабля, без единого преимущества.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Тонкий сплав легче стали (1.1 против 1.2), поэтому парирование чуть выше
|
|
|
|
|
|
// сабельного — правило «легче значит лучше держит» одно на весь каталог.
|
|
|
|
|
|
inline BladeDef CryoedgeDef()
|
|
|
|
|
|
{
|
|
|
|
|
|
BladeDef d;
|
|
|
|
|
|
d.name = "cryoedge";
|
|
|
|
|
|
d.reach = 1.45f;
|
|
|
|
|
|
d.arcDeg = 92.0f;
|
|
|
|
|
|
d.windup = 0.19f;
|
|
|
|
|
|
d.strike = 0.13f;
|
|
|
|
|
|
d.recover = 0.26f;
|
|
|
|
|
|
d.interval = 0.66f; // медленнее сабли: сплав тяжело разгонять
|
|
|
|
|
|
d.damage = 104.0f;
|
|
|
|
|
|
d.parry = 0.24f;
|
|
|
|
|
|
d.knockback = 1.80f;
|
|
|
|
|
|
d.lungeSpeed = 3.20f;
|
|
|
|
|
|
|
|
|
|
|
|
d.elem = ElementMix::Of(Element::CRY, 0.85f);
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
inline BladeDef BladeDefOf(BladeId id)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (id)
|
|
|
|
|
|
{
|
Крафт стал выбором: закалка, отпечаток, направленные операции (M33)
Прежние пять операций были СЛЕПЫМИ: перековка бросала всё заново,
прививка ставила случайный тег, отсечение снимало последний аффикс,
возвышение поднимало случайный. Игрок не принимал ни одного решения - жал
кнопку и смотрел, что выпало. Это не крафт, это второй бросок предмета за
материалы.
Теперь половина операций направленные: игрок показывает пальцем, какой
аффикс трогать, и платит за это вдвое - вот это уже решение
("перебросить всё дёшево или снять один дорого").
Два новых глагола:
ЗАКАЛКА двигает рулон выбранного аффикса к максимуму его тира на треть
остатка. Дёшево, понятно, повторяемо - то самое "доработать почти
хорошую вещь". Тир не трогает: за ступень платят возвышением.
ОТПЕЧАТОК переносит лучший аффикс с ДРУГОЙ вещи, сжигая её. Именно он
превращает хлам из схрона в ресурс, а не в мусор, который лежит.
Правило "один тег один раз" им не обходится, бюджет проверяется.
В мастерской: клик по вещи кладёт её на верстак, повторный клик по той же
назначает донором (помечается кровью - он сгорит, и видеть это надо ДО
нажатия). Строки аффиксов стали кнопками: по ним выбирается цель.
Проверка меряет НАПРАВЛЕННОСТЬ, а не факт срабатывания:
направленность: отсечение по цели, возвышение по цели,
закалка двигает рулон, отпечаток перенёс и сжёг донора
Отсечение пробуется на СРЕДНЕМ аффиксе из трёх: операция, которая
"работает", снимая последний, прошла бы и слепую версию.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:37:07 +07:00
|
|
|
|
case BladeId::KNIFE: return KnifeDef();
|
|
|
|
|
|
case BladeId::CLEAVER: return CleaverDef();
|
|
|
|
|
|
case BladeId::STILETTO: return StilettoDef();
|
|
|
|
|
|
case BladeId::MAUL: return MaulDef();
|
|
|
|
|
|
case BladeId::RIPPER: return RipperDef();
|
|
|
|
|
|
case BladeId::CRYOEDGE: return CryoedgeDef();
|
|
|
|
|
|
default: return DefaultBladeDef();
|
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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline const char* BladeName(BladeId id)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (id)
|
|
|
|
|
|
{
|
Крафт стал выбором: закалка, отпечаток, направленные операции (M33)
Прежние пять операций были СЛЕПЫМИ: перековка бросала всё заново,
прививка ставила случайный тег, отсечение снимало последний аффикс,
возвышение поднимало случайный. Игрок не принимал ни одного решения - жал
кнопку и смотрел, что выпало. Это не крафт, это второй бросок предмета за
материалы.
Теперь половина операций направленные: игрок показывает пальцем, какой
аффикс трогать, и платит за это вдвое - вот это уже решение
("перебросить всё дёшево или снять один дорого").
Два новых глагола:
ЗАКАЛКА двигает рулон выбранного аффикса к максимуму его тира на треть
остатка. Дёшево, понятно, повторяемо - то самое "доработать почти
хорошую вещь". Тир не трогает: за ступень платят возвышением.
ОТПЕЧАТОК переносит лучший аффикс с ДРУГОЙ вещи, сжигая её. Именно он
превращает хлам из схрона в ресурс, а не в мусор, который лежит.
Правило "один тег один раз" им не обходится, бюджет проверяется.
В мастерской: клик по вещи кладёт её на верстак, повторный клик по той же
назначает донором (помечается кровью - он сгорит, и видеть это надо ДО
нажатия). Строки аффиксов стали кнопками: по ним выбирается цель.
Проверка меряет НАПРАВЛЕННОСТЬ, а не факт срабатывания:
направленность: отсечение по цели, возвышение по цели,
закалка двигает рулон, отпечаток перенёс и сжёг донора
Отсечение пробуется на СРЕДНЕМ аффиксе из трёх: операция, которая
"работает", снимая последний, прошла бы и слепую версию.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:37:07 +07:00
|
|
|
|
case BladeId::KNIFE: return "KNIFE";
|
|
|
|
|
|
case BladeId::CLEAVER: return "CLEAVER";
|
|
|
|
|
|
case BladeId::STILETTO: return "STILETTO";
|
|
|
|
|
|
case BladeId::MAUL: return "MAUL";
|
|
|
|
|
|
case BladeId::RIPPER: return "RIPPER";
|
|
|
|
|
|
case BladeId::CRYOEDGE: return "CRYOEDGE";
|
|
|
|
|
|
default: return "SABER";
|
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
|
|
|
|
}
|
|
|
|
|
|
}
|