#pragma once // ----------------------------------------------------------------------------- // Тайлы пола и стен — по набору на каждую тему отделки. // // Живёт в движке, а не в render/sprites.h, ровно по одной причине: этим // набором рисует и игра, и редактор комнат. Редактор линкуется только с // движком и про бойцов, тварей и предметы не знает, поэтому тащить ради двух // спрайтов весь SpriteSet он не может и не должен. // // Выбор варианта плиты — чистая функция от координат тайла, без состояния и без // генератора: пол обязан быть одинаковым в любом прогоне, иначе запечённая // кровь легла бы после перезапуска на другую плиту (render/blood.h). // ----------------------------------------------------------------------------- #include #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(); };