176 lines
8.7 KiB
C
176 lines
8.7 KiB
C
|
|
#pragma once
|
|||
|
|
// -----------------------------------------------------------------------------
|
|||
|
|
// Редактор комнат — ОТДЕЛЬНОЕ приложение поверх движка.
|
|||
|
|
//
|
|||
|
|
// Он линкуется только с tile2d и не видит ни одного символа из sim/ и ai/.
|
|||
|
|
// Поэтому «редактор знает про правила боя» — это ошибка линковки, а не то, что
|
|||
|
|
// надо ловить на ревью.
|
|||
|
|
//
|
|||
|
|
// Ни одной игры по имени он тоже не знает: где содержимое, говорит проект
|
|||
|
|
// (engine/project.h), а что можно ставить в комнату — его каталог сущностей.
|
|||
|
|
//
|
|||
|
|
// Рисует он ровно тем же софтверным фреймбуфером и той же ПРОЕКЦИЕЙ, что и
|
|||
|
|
// игра: комната в редакторе выглядит так же, как в бою, потому что это один и
|
|||
|
|
// тот же код, а не похожий. В проекте с видом сверху редактор тоже показывает
|
|||
|
|
// вид сверху — иначе он планировал бы не то, что получится.
|
|||
|
|
//
|
|||
|
|
// Раскладка кадра 480x270:
|
|||
|
|
// y 0..11 верхняя панель: файл, операции над комнатами
|
|||
|
|
// x 0..379 холст: изометрия комнаты (или карта предпросмотра уровня)
|
|||
|
|
// x 380..479 правая панель: кисти, свойства комнаты, палитра
|
|||
|
|
// y 232..269 нижняя полоса поверх холста: таблица выбранной точки спавна
|
|||
|
|
// -----------------------------------------------------------------------------
|
|||
|
|
#include <string>
|
|||
|
|
#include <vector>
|
|||
|
|
|
|||
|
|
#include "engine/math.h"
|
|||
|
|
#include "engine/catalog.h"
|
|||
|
|
#include "engine/level_gen.h"
|
|||
|
|
#include "engine/project.h"
|
|||
|
|
#include "engine/room.h"
|
|||
|
|
#include "engine/tileset.h"
|
|||
|
|
#include "engine/framebuffer.h"
|
|||
|
|
#include "engine/sfa_library.h"
|
|||
|
|
|
|||
|
|
// Кисть. Пол, стена и дверь пишут в сетку; спавн ставит точку; ластик снимает
|
|||
|
|
// и то и другое — иначе «убрать» требовало бы помнить, чем это поставлено.
|
|||
|
|
enum class Tool : uint8_t
|
|||
|
|
{
|
|||
|
|
FLOOR = 0,
|
|||
|
|
WALL,
|
|||
|
|
DOOR,
|
|||
|
|
SPAWN,
|
|||
|
|
ERASE,
|
|||
|
|
COUNT,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Что делает элемент интерфейса. Действие, а не «номер кнопки»: раскладка
|
|||
|
|
// панелей меняется чаще, чем набор операций.
|
|||
|
|
enum class Act : uint8_t
|
|||
|
|
{
|
|||
|
|
NONE = 0,
|
|||
|
|
TOOL, PALETTE,
|
|||
|
|
ROOM_PREV, ROOM_NEXT, ROOM_NEW, ROOM_SAVE, ROOM_RELOAD,
|
|||
|
|
W_DEC, W_INC, H_DEC, H_INC,
|
|||
|
|
WEIGHT_DEC, WEIGHT_INC, THEME_CYCLE, START_TOGGLE,
|
|||
|
|
DMIN_DEC, DMIN_INC, DMAX_DEC, DMAX_INC,
|
|||
|
|
PALETTE_TAB,
|
|||
|
|
CHANCE_DEC, CHANCE_INC, OPT_DEC, OPT_INC,
|
|||
|
|
PREVIEW_TOGGLE, PREVIEW_RESEED, PREVIEW_DEEPER, PREVIEW_SHALLOWER, PLAYTEST,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
struct EdRect
|
|||
|
|
{
|
|||
|
|
int x = 0, y = 0, w = 0, h = 0;
|
|||
|
|
bool Has(float fx, float fy) const
|
|||
|
|
{
|
|||
|
|
return fx >= float(x) && fy >= float(y) && fx < float(x + w) && fy < float(y + h);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
struct EdWidget
|
|||
|
|
{
|
|||
|
|
Act act = Act::NONE;
|
|||
|
|
int arg = 0; // номер кисти, индекс каталога, ...
|
|||
|
|
EdRect rect;
|
|||
|
|
std::string label;
|
|||
|
|
bool active = false; // включённое состояние (нажатая кисть)
|
|||
|
|
bool enabled = true;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
struct Editor
|
|||
|
|
{
|
|||
|
|
// --- данные ---------------------------------------------------------------
|
|||
|
|
// Проект — первое, что поднимается: из него берутся пути к содержимому,
|
|||
|
|
// вид отображения и кривая глубины. Редактор не знает ни одной игры по
|
|||
|
|
// имени и открывает тот проект, который ему назвали.
|
|||
|
|
Project project;
|
|||
|
|
EntityCatalog catalog;
|
|||
|
|
RoomLibrary lib;
|
|||
|
|
TileSet tiles;
|
|||
|
|
SpriteLibrary forge;
|
|||
|
|
bool forgeOpen = false;
|
|||
|
|
|
|||
|
|
// Рабочая копия правится, а lib.rooms остаётся тем, что лежит на диске:
|
|||
|
|
// без этого «не сохранять» было бы нечем осуществить.
|
|||
|
|
Room room;
|
|||
|
|
int roomIndex = -1;
|
|||
|
|
bool dirty = false;
|
|||
|
|
|
|||
|
|
// --- инструменты ----------------------------------------------------------
|
|||
|
|
Tool tool = Tool::WALL;
|
|||
|
|
int paletteIndex = 0;
|
|||
|
|
// Каталог вырос до трёх десятков записей и в столбик больше не помещается.
|
|||
|
|
// Вкладки по классу — не украшение: твари, обстановка и лут ставятся в
|
|||
|
|
// разные моменты работы, и мешать их в одном списке значит листать чужое.
|
|||
|
|
EntityClass paletteTab = EntityClass::MONSTER;
|
|||
|
|
int paletteScroll = 0;
|
|||
|
|
int selX = -1, selY = -1; // выбранная точка спавна; -1 — не выбрана
|
|||
|
|
|
|||
|
|
// --- вид ------------------------------------------------------------------
|
|||
|
|
int panX = 0, panY = 0; // сдвиг холста в пикселях
|
|||
|
|
int hoverX = -1, hoverY = -1;
|
|||
|
|
bool painting = false; // ЛКМ зажата: кисть тянется
|
|||
|
|
bool erasing = false;
|
|||
|
|
|
|||
|
|
// --- предпросмотр этажа ---------------------------------------------------
|
|||
|
|
bool preview = false;
|
|||
|
|
uint32_t seed = 1;
|
|||
|
|
// Какой этаж собирается в предпросмотре. Отдельно от диапазона комнаты:
|
|||
|
|
// проверять надо не только «попадает ли комната», но и «во что превращается
|
|||
|
|
// весь набор» на этой глубине.
|
|||
|
|
int previewDepth = 1;
|
|||
|
|
Tilemap previewMap;
|
|||
|
|
GeneratedLevel previewLevel;
|
|||
|
|
std::string previewError;
|
|||
|
|
|
|||
|
|
// --- строка состояния -----------------------------------------------------
|
|||
|
|
std::string status;
|
|||
|
|
float statusTimer = 0.0f;
|
|||
|
|
|
|||
|
|
std::vector<EdWidget> widgets;
|
|||
|
|
|
|||
|
|
// Поднять проект, каталог, набор комнат и тайлы. false — работать не с чем
|
|||
|
|
// (нет проекта или каталога сущностей), причина уходит в status.
|
|||
|
|
// projectName — имя или путь; пусто — единственный найденный проект.
|
|||
|
|
bool Init(const std::string& projectName = {});
|
|||
|
|
|
|||
|
|
// Ввод и действия. fit — куда лёг фреймбуфер в окне.
|
|||
|
|
void Update(const ScreenRect& fit, float dt);
|
|||
|
|
|
|||
|
|
// Комната (или карта предпросмотра) в софтверный буфер.
|
|||
|
|
void DrawWorld(Framebuffer& fb);
|
|||
|
|
|
|||
|
|
// Панели поверх готового кадра: примитивы raylib, потому что здесь нужны
|
|||
|
|
// слова, и много.
|
|||
|
|
void DrawUi(const ScreenRect& fit) const;
|
|||
|
|
|
|||
|
|
// Выполнить действие интерфейса. Публично, потому что через него же
|
|||
|
|
// работают ключи командной строки: «показать сборку уровня» с клавиши и
|
|||
|
|
// из аргумента обязаны делать буквально одно и то же.
|
|||
|
|
void Apply(Act act, int arg);
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
// u — целый масштаб кадра в окне. Нужен, чтобы мерить подписи ТЕМ ЖЕ
|
|||
|
|
// шрифтом и кеглем, каким они будут нарисованы: у встроенного шрифта raylib
|
|||
|
|
// межбуквенный интервал зависит от размера, и посчитанная «по пять пикселей
|
|||
|
|
// на букву» ширина кнопки разъезжается с настоящей.
|
|||
|
|
void BuildWidgets(int u);
|
|||
|
|
|
|||
|
|
void LoadRoomAt(int index);
|
|||
|
|
void SaveCurrent();
|
|||
|
|
void ReloadLibrary();
|
|||
|
|
void NewRoom();
|
|||
|
|
|
|||
|
|
void PaintAt(int x, int y, bool erase);
|
|||
|
|
void Regenerate();
|
|||
|
|
void Say(const std::string& s);
|
|||
|
|
|
|||
|
|
// Спрайт-маркер сущности каталога: кадр из SpriteForge, если он собран,
|
|||
|
|
// иначе капсула по цвету и радиусу. Пустая палитра не должна мешать
|
|||
|
|
// планировать уровень.
|
|||
|
|
const Sprite* MarkerSprite(int catalogIndex) const;
|
|||
|
|
|
|||
|
|
mutable std::vector<Sprite> markers; // кеш, по индексу каталога
|
|||
|
|
};
|