squad-proto/src/render/view.h
z.kirill 7d6266aaf3 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-13 21:07:19 +03:00

76 lines
4.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
// -----------------------------------------------------------------------------
// Всё render-only состояние живёт здесь и только здесь.
//
// Зачем отдельная структура: правило «эффекты не текут в симуляцию» должно
// проверяться компилятором, а не вниманием. RenderGame принимает Game по
// КОНСТАНТНОЙ ссылке — значит ни свет, ни трассеры, ни тряска камеры физически
// не могут изменить состояние симуляции. Headless-прогоны (--accept, --headless)
// View не создают вообще, поэтому цифры приёмки остаются цифрами симуляции.
// -----------------------------------------------------------------------------
#include "render/anim.h"
#include "render/atmos.h"
#include "render/blood.h"
#include "engine/framebuffer.h"
#include "render/fx.h"
#include "engine/view2d.h"
#include "render/lighting.h"
#include "render/post.h"
#include "render/scene.h"
struct Game;
class SpriteLibrary;
struct View
{
v2d::Camera cam;
Scene scene;
Lighting lighting;
FxSystem fx;
PostFx post;
Dread dread;
BloodField blood; // вечные декали, запечённые в спрайты пола
Atmosphere atmos; // пепел в воздухе
// Дельты считаются по накопительным счётчикам метрик: за один кадр рендера
// проходит до MAX_STEPS_PER_FRAME шагов, поэтому флаг «выстрелил» не годится.
int prevShots = 0;
int prevKills = 0;
int prevFriendly = 0;
int prevMeleeHits = 0;
// Здоровье отряда прошлого кадра: урон приходит от тварей россыпью, флага
// «ударили» ни у кого нет, а дельта суммы честно ловит любой источник.
float prevSquadHp = -1.0f;
int prevSquadAlive = NUM_AGENTS;
bool horrorEnabled = true; // F5 — аварийный возврат к прежнему кадру
bool showSight = false; // F6 — показать диски обзора
// --- анимация тварей -------------------------------------------------------
// Библиотека спрайтов, если игру запустили с собранными ассетами. Пусто —
// рисуем процедурные спрайты, и это рабочий режим по умолчанию, а не отказ.
const SpriteLibrary* forge = nullptr;
// Состояние проигрывателя на КАЖДУЮ цель. Живёт в View, а не в Target,
// ровно по правилу этого файла: фаза шага — свойство картинки, и попав в
// симуляцию, она поехала бы в цифрах приёмки.
Animator enemyAnim[MAX_TARGETS];
Animator agentAnim[NUM_AGENTS];
// Где тварь была в прошлом кадре: шаг проигрывается, только когда она идёт.
// Скорости у Target нет (vel — это отдача от удара), поэтому движение
// определяется единственным честным способом — сравнением позиций.
Vec2 enemyPrevPos[MAX_TARGETS];
bool enemyPosKnown[MAX_TARGETS] = {};
// Гамма и усиление меняют САМУ таблицу рампы: без перепечатки ручки молчат.
float rampGamma = -1.0f;
float rampGain = -1.0f;
void Init();
void RefreshRamp();
void Reset(const Game& game);
};
void RenderGame(const Game& game, View& view, Framebuffer& fb, float alpha, float frameDt);