Files
g-world/CLAUDE.md
T

152 lines
12 KiB
Markdown
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.
# CLAUDE.md
Заметки для агентов, работающих с этим репозиторием.
## Что это за проект
Двумерная игра с видом сбоку на Godot 4.7. Референс — Fallout Shelter: разрез убежища,
комнаты в сетке, жители. **Игра полностью 2D.** Новые игровые сцены — на `Node2D`/`Control`;
3D-узлам в проекте места нет. Рендерер при этом Forward+ — так решил владелец проекта,
не переключайте его без явной просьбы.
## Работа через MCP, а не через файлы
В проекте включён плагин `addons/godot_ai`, редактор Godot обычно запущен и подключён.
Сцены и ресурсы правьте инструментами MCP (`scene_manage`, `ui_manage`, `node_create`,
`theme_manage`, `resource_manage`), а не редактированием `.tscn` руками — иначе открытый
редактор перезапишет изменения при сохранении.
Скрипты — через `script_create` / `script_patch`: они возвращают диагностику парсера сразу.
Проверять результат — `project_run` + `editor_screenshot(source="game")`. Ввод в запущенную
игру шлётся через `game_manage(op="input_key")`; **`input_action` не двигает фокус Control-ов**,
для навигации по UI нужны настоящие события клавиш.
### Известные особенности
- `node_set_property` не принимает целочисленный `0` — используйте `batch_execute`
с командой `set_property`.
- Правка скрипта, который зарегистрирован автозагрузкой, даёт в ответе
`GDScript reload failed with error code 43`. Это неудачная горячая перезагрузка в редакторе,
а не ошибка разбора: проверяйте `logs_read(source="editor")`, игра при этом запускается чисто.
- `project_manage(op="settings_set")` отказывается менять `application/run/main_scene`.
- Большие `ui_manage(op="build_layout")` иногда не доезжают целиком — стройте дерево
несколькими вызовами поменьше.
- Запуск из редактора отдаёт встроенное окно: `Embedded window can't be resized` в логе — норма.
## Конвенции кода
- Отступы — табы (см. `.editorconfig`), статическая типизация везде, где возможно.
- Комментарии и весь UI-текст — по-русски; имена узлов, файлов, методов, сигналов — по-английски.
- Комментарий объясняет **почему**, а не пересказывает код.
- Приватные члены — с подчёркиванием: `_card`, `_focus_first_button()`.
- Документирующие комментарии `##` — на классах, сигналах и неочевидных методах.
## Архитектура UI
- Единая тема `themes/main_menu.theme.tres` на корне каждого экрана. Цвета в узлах не хардкодим.
- Экран = сцена со своим скриптом, наружу торчат `open()` / `close()` и сигнал `closed`.
- Оверлеи центрируются `CenterContainer`, а **не** пресетом `center`: пресет ставит левый
верхний угол карточки в центр экрана. Та же ловушка у `bottom_wide` — он прижимает к нижнему
краю *верх* панели, и она уезжает за экран; для нижней панели используйте `MarginContainer`
во весь экран + `HBoxContainer` с `size_flags_vertical = SIZE_SHRINK_END` у содержимого.
- Контейнеры не раскладывают скрытые узлы — после `visible = true` нужен
`await get_tree().process_frame`, прежде чем читать `size` (например, для `pivot_offset`).
- Наведение мышью делается фокусом (`mouse_entered``grab_focus`), чтобы у мыши и клавиатуры
была одна подсветка.
## Игровой слой
Файлы в `scripts/game/` разделены по принципу «данные и правила отдельно, отрисовка отдельно».
Модель и правила — без узлов и без `_draw()`:
- `vault_grid.gd` — сетка и комнаты. Сюда идут типы комнат, размещение, ресурсы.
Вертикальное смещение бункера под землю — константа `DEPTH`, её учитывают и
`cell_rect()`, и `cell_at()`, поэтому картинка и попадание мышью не разъезжаются.
- `game_clock.gd` — игровые дата и время, скорость, своя пауза. Такт наружу — сигнал
`minute_passed`; темп задаётся `MINUTES_PER_SECOND`.
- `weather_system.gd` — тип погоды, матрица переходов и плавная смена параметров.
Такт берёт у часов, поэтому стоит вместе с паузой. Форма осадков решается
температурой, а не месяцем: у нуля дождь становится снегом.
- `climate.gd` — сезонная норма температуры и ветра (средняя полоса Европы).
Перечисление погоды сюда не тянем — вышла бы циклическая ссылка.
- `sky_cycle.gd` — палитра неба по часу суток, положение солнца и луны. Чистые вычисления.
Отрисовка — только `_draw()`, читают модель и ничего не решают:
- `sky_view.gd` — градиент неба, звёзды, светила.
- `surface_view.gd` — поверхность: горы, холмы, деревья, облака, осадки.
- `vault_view.gd` — разрез: порода, корпус, шахта, комнаты, будка входа.
`vault_scene.gd` связывает всё это с камерой и HUD и держит контракт сохранения.
Новый тип комнаты добавляется одной записью в `VaultGrid.KINDS`: кнопка в панели строительства
и подсчёт ресурсов подхватятся сами. Новый тип погоды — записью в `WeatherSystem.KINDS`
плюс строкой в `TRANSITIONS`.
### Текстуры фона
**Ловушка с плиткой.** `draw_texture_rect(tex, rect, tile = true)` повторяет
текстуру в её натуральную величину и **по обеим осям**. Если растянуть
прямоугольник ради масштаба, текстура просто уложится в него несколько раз, и
на стыке копий пойдёт горизонтальная полоса через весь экран. Плюс при
`texture_repeat = ENABLED` фильтрация на краю подмешивает противоположный край
текстуры — ещё одна тонкая линия.
Поэтому полосы пейзажа кладутся плитками вручную. При этом важен ещё один
момент: крайние столбцы пикселей у этих текстур полупрозрачные от сглаживания,
и встык они складываются дважды, давая **вертикальную** линию. Отсюда
`EDGE_INSET` — плитки рисуются через `draw_texture_rect_region()` с отступом
в пиксель с каждой стороны, плюс пиксель нахлёста. Повтор на узле выключен.
`background/` — набор Kenney Background Elements. Два стиля, и они не взаимозаменяемы:
`PNG/Flat/*` — бледные, почти белые силуэты, их место на дальних планах, где цвет всё равно
задаётся тонировкой; `PNG/*` — цветные спрайты для ближнего плана. Перепутать легко: деревья
из Flat на переднем плане выглядят выцветшими.
## Консоль разработчика
Открывается тильдой или F1 в любой сцене, работает и на паузе. Сама об игре
ничего не знает: команды регистрируют сцены через `DevConsole.register(...)`,
и они умирают вместе со сценой — консоль выбрасывает команду, когда её
`Callable` протухает.
Команды игровой сцены живут в `vault_scene.gd`, в разделе «Команды консоли»:
`stats`, `time`, `date`, `speed`, `weather`, `temp`, `wind`, `build`, `wipe`.
У погоды есть латинские ключи (`snow`, `rain`, …) — набирать русские названия
с латинской раскладки посреди отладки неудобно.
### Файловый мост — как выполнять команды из MCP
Набрать команду через `game_manage(op="input_key")` нельзя: внедрённые события
доносят до `LineEdit` код клавиши, но не символ. Enter при этом проходит — то
есть дело не в фокусе. Поэтому в консоли есть мост через файлы в `user://`
(только `OS.is_debug_build()`, в релиз не попадает):
```bash
DIR="$APPDATA/Godot/app_userdata/GWorld"
printf 'weather storm\ntemp -3\nstats\n' > "$DIR/console_in.txt"
cat "$DIR/console_log.txt"
```
Консоль опрашивает `console_in.txt` четыре раза в секунду, выполняет строки и
удаляет файл. Весь вывод дублируется в `console_log.txt`, который
перезаписывается на каждый запуск игры. Работает и с закрытой консолью, и на
паузе.
## Автозагрузки
`GameSettings` (`GameSettingsService`), `SaveManager` (`SaveManagerService`)
и `DevConsole` (сцена `scenes/ui/dev_console.tscn`).
У обеих есть `class_name`: константы, перечисления и типы берём через него, экземпляр —
через `get_node("/root/...")`. Имя автозагрузки редактор не резолвит без перезапуска проекта.
Контракт сохранения игровой сцены описан в [README.md](README.md).
## Проверка
Изменения в UI считаются сделанными только после запуска и скриншота. Логи смотреть в обоих
источниках: `logs_read(source="game")` и `logs_read(source="editor")` — ошибки загрузки скриптов
попадают только во второй.