squad-proto/editor/terminal.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

91 lines
5.5 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
// -----------------------------------------------------------------------------
// Терминал в редакторе: работать над проектом, не выходя из него.
//
// ЧТО ЭТО НЕ ТАКОЕ. Это не эмулятор терминала. Полноценный VT100 (альтернативный
// экран, адресация курсора, рамки из псевдографики) нужен ровно одному сорту
// программ — интерактивным TUI, и `claude` без ключей именно такой. Написать
// его поверх raylib можно, но результат всё равно рассыпался бы на первом же
// символе рамки: встроенный шрифт их не знает.
//
// Поэтому здесь СТРОЧНЫЙ вывод, и это не упрощение, а выбор в пользу того,
// как с моделью работают из редактора на самом деле:
//
// * `claude -p` и `codex exec` печатают обычный текст — им эмулятор не нужен;
// * инструменты Tile2D (--tool) печатают таблицы и карты — тоже текст;
// * git, cmake, прогоны игры — текст.
//
// Полный интерактивный CLI открывается одной кнопкой во ВНЕШНЕМ терминале, в
// том же рабочем каталоге. Так у каждого способа остаётся то, в чём он хорош.
//
// Строка ввода работает в трёх режимах (Tab): команда оболочки, вопрос Claude,
// вопрос Codex. Одно поле, а не три разных — потому что работа выглядит как
// чередование: спросил, посмотрел, проверил инструментом, снова спросил.
// -----------------------------------------------------------------------------
#include <cstdint>
#include <string>
#include <vector>
struct ScreenRect;
enum class AskMode : uint8_t
{
SHELL = 0, // команда оболочки как есть
CLAUDE, // claude -p "..."
CODEX, // codex exec "..."
COUNT,
};
const char* AskModeName(AskMode m);
struct Terminal
{
bool open = false;
// Скроллбек. Строки, а не поток байт: их листают, ищут глазами и копируют
// в отчёт, и все три действия требуют границ строк.
std::vector<std::string> lines;
std::string input;
std::vector<std::string> history;
int histPos = -1; // -1 — набирается новая строка, не из истории
int scroll = 0; // на сколько строк отмотано вверх от конца
AskMode mode = AskMode::SHELL;
// Где выполняются команды. Корень репозитория, а не каталог проекта:
// работа идёт над всем — код движка, содержимое, документация.
std::string workDir;
// Какой проект открыт в редакторе. Уходит детям переменной окружения
// TILE2D_PROJECT, поэтому `--tool validate` без ключей проверяет ИМЕННО
// его — и то же самое видит агент, запущенный отсюда.
std::string projectName;
void Init(const std::string& dir, const std::string& project = {});
void Shutdown();
bool Running() const;
// Ввод и кнопки. true — терминал забрал ввод себе, редактору его не
// отдавать: иначе набранная в строке `w` встанет кистью стены на холст.
bool Update(int windowW, int windowH, float dt);
void Draw(int windowW, int windowH) const;
// Забрать то, что процесс успел напечатать. Звать каждый кадр: вывод
// обязан идти потоком, а не появляться целиком по завершении.
void Poll();
void Submit(); // выполнить строку ввода по режиму
void RunShell(const std::string& cmd); // выполнить команду напрямую (UTF-8)
// То же, но строка пришла из argv. Аргументы командной строки приходят в
// системной кодировке, а всё остальное здесь — UTF-8: без перевода русская
// команда из --term отображалась бы вопросами.
void RunShellFromArgv(const char* cmd);
void Stop(); // прервать текущий процесс
void Print(const std::string& s); // своя строка в скроллбек
void LaunchExternal(); // интерактивный CLI отдельным окном
// Занимаемая часть окна снизу, в пикселях ОКНА. Редактор спрашивает её,
// чтобы не рисовать и не ловить мышь под панелью.
int PanelTop(int windowH) const;
};