Десять вех одним состоянием: разделять их пофайлово нечем, потому что
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>
70 lines
4.2 KiB
C++
70 lines
4.2 KiB
C++
#pragma once
|
||
// -----------------------------------------------------------------------------
|
||
// Тайлы пола и стен — по набору на каждую тему отделки.
|
||
//
|
||
// Живёт в движке, а не в render/sprites.h, ровно по одной причине: этим
|
||
// набором рисует и игра, и редактор комнат. Редактор линкуется только с
|
||
// движком и про бойцов, тварей и предметы не знает, поэтому тащить ради двух
|
||
// спрайтов весь SpriteSet он не может и не должен.
|
||
//
|
||
// Выбор варианта плиты — чистая функция от координат тайла, без состояния и без
|
||
// генератора: пол обязан быть одинаковым в любом прогоне, иначе запечённая
|
||
// кровь легла бы после перезапуска на другую плиту (render/blood.h).
|
||
// -----------------------------------------------------------------------------
|
||
#include <cstdint>
|
||
|
||
#include "engine/hash.h"
|
||
#include "engine/theme.h"
|
||
#include "engine/framebuffer.h"
|
||
#include "engine/config.h"
|
||
|
||
class SpriteLibrary;
|
||
|
||
// Сколько разных «рисунков» может лежать на плите пола: ровная, треснувшая,
|
||
// решётка, засыпанная трухой. Шахматка при этом остаётся — вариант ложится
|
||
// ПОВЕРХ неё, а не вместо: без чередования двух тонов изометрическая сетка
|
||
// перестаёт читаться, и пол превращается в кашу.
|
||
constexpr int FLOOR_VARIANTS = 4;
|
||
|
||
struct TileSet
|
||
{
|
||
// [тема][чётность шахматки][вариант рисунка].
|
||
Sprite floor[THEME_COUNT][2][FLOOR_VARIANTS];
|
||
Sprite wall[THEME_COUNT][2]; // industrial / biomass-infested, 32x32
|
||
|
||
// Процедурный набор. Не зависит ни от одного файла на диске: игра и
|
||
// редактор обязаны запускаться на голом репозитории.
|
||
void Generate();
|
||
|
||
// Заменить базовую тему на тайлы из библиотеки SpriteForge и пересобрать
|
||
// по ней остальные. Всё или ничего: при любой осечке набор остаётся тем,
|
||
// что дал Generate(), и возвращается false. Вызывать только после Generate().
|
||
bool LoadFromForge(const SpriteLibrary& lib);
|
||
|
||
const Sprite& Floor(int tx, int ty, uint8_t theme) const
|
||
{
|
||
// Раскладка намеренно неравномерная: ровная плита занимает больше
|
||
// половины карты. Равные доли превращают пол в пёстрый ковёр, на
|
||
// котором не видно ни крови, ни тварей.
|
||
static const int kPick[8] = {0, 0, 0, 0, 0, 1, 2, 3};
|
||
const uint32_t h = Hash2(uint32_t(tx) * 2654435761u, uint32_t(ty));
|
||
return floor[Clamp(theme)][(tx + ty) & 1][kPick[h & 7u]];
|
||
}
|
||
|
||
const Sprite& Wall(int tx, int ty, uint8_t theme) const
|
||
{
|
||
// Редкие заражённые блоки, детерминированно по координате.
|
||
const uint32_t h = Hash2(uint32_t(tx) * 2246822519u, uint32_t(ty));
|
||
return wall[Clamp(theme)][(h & 7u) == 0u ? 1 : 0];
|
||
}
|
||
|
||
private:
|
||
// Комната может сослаться на тему, которой в этой сборке движка ещё нет.
|
||
// Падать из-за этого нельзя: неизвестная тема рисуется базовой.
|
||
static int Clamp(uint8_t theme) { return (theme < THEME_COUNT) ? int(theme) : 0; }
|
||
|
||
// Темы 1..N-1 — перекраска темы 0. Вызывается и после Generate(), и после
|
||
// LoadFromForge(): иначе привезённые тайлы остались бы только в базовой.
|
||
void BuildThemes();
|
||
};
|