#pragma once // ----------------------------------------------------------------------------- // Проекция: как мир кладётся на экран. // // Это ЕДИНСТВЕННОЕ место, где движок знает про изометрию. Мир везде декартов // (тайл = 1.0 юнита), симуляция, ИИ и коллизии об этом файле не знают вообще — // правило «src/ai/ не знает про изометрию» этим и держится. // // Проекция задаёт четыре вещи, и больше ничего: // * куда мировая точка попадает на экран и обратно; // * чем сортируется список отрисовки (кто «ближе к камере»); // * какой формы плита пола — ромб или квадрат; // * насколько крыша стены поднята над своей плитой. // // Всё остальное — спрайты, свет, кровь, эффекты — считается ЧЕРЕЗ эти четыре // ответа и потому переживает смену вида. Абстракция с одной реализацией была бы // самообманом, поэтому режима сразу два: изометрия 2:1 и вид сверху. // // Вид выбирается ОДИН раз при старте (проектом или ключом --view) и дальше не // меняется: тайлсет рисуется под форму плиты, и смена вида на ходу означала бы // перерисовку всех спрайтов посреди кадра. // ----------------------------------------------------------------------------- #include #include "engine/config.h" #include "engine/math.h" enum class ViewMode : uint8_t { ISO_2_1 = 0, // ромб 32x16, наклон 2:1 — классическая изометрия TOP_DOWN, // квадрат 32x32, вид строго сверху }; constexpr int VIEW_MODE_COUNT = 2; const char* ViewModeName(ViewMode m); // Разбор имени из project.txt или из ключа --view. false — имя не опознано, // out не тронут (звать с уже осмысленным значением по умолчанию). bool ParseViewMode(const char* name, ViewMode& out); struct Projection { ViewMode mode = ViewMode::ISO_2_1; int tileW = TILE_W; // размер плиты пола на экране int tileH = TILE_H; int wallH = WALL_H; // высота спрайта стены целиком static Projection For(ViewMode m); // Мир -> экран и обратно. Обратная строго обратна прямой: по ней считается // направление WASD, и рассогласование увело бы отряд не туда, куда жмут. Vec2 WorldToScreen(Vec2 w) const; Vec2 ScreenToWorld(Vec2 s) const; // Ключ сортировки: чем больше, тем ближе к камере. float Depth(Vec2 w) const; // Занятая часть строки y у плиты пола: ромб или вся строка. void TileRow(int y, int& x0, int& x1) const; // Куда внутри спрайта стены попадает центр её плиты. В изометрии крыша // поднята над полом на высоту тайла, при виде сверху подниматься некуда. int WallAnchorY() const; // Есть ли у стены видимые вертикальные грани. При виде сверху их нет, и // рисовать их значило бы показывать стену в двух проекциях сразу. bool WallHasSides() const { return mode == ViewMode::ISO_2_1; } }; // Активная проекция. Глобальная по той же причине, что и g_tune: её читают // рендер, редактор и генерация тайлов, и протаскивать её параметром через // каждый блит значило бы переписать половину рендера ради значения, которое // за весь запуск не меняется ни разу. inline Projection g_proj;