Время как первоклассный ресурс
Время — главный ресурс игры наравне с HP, MP и деньгами. Объясняет, как оно устроено сейчас (дни) и куда движется (тики), и почему per-actor clock меняет всё.
«Вся игра про время. Про то, как время утекает.»
В west-marches формате с асинхронным игровым временем у каждого персонажа свой «сейчас». Один PC ведёт данж в день 5 петли; другой торгует в городе в день 12. Одновременно, в реальном времени. Система должна это поддерживать, а не притворяться, что все находятся в одной точке.
Что есть сейчас: (loop_number, day_in_loop)
В проде временно́й якорь — два целых числа:
| Поле | Таблица | Смысл |
|---|---|---|
loop_number | transactions, chronicles | какая петля |
day_in_loop | transactions | день внутри петли (1..length_days) |
day_from, day_to | nodes.fields у session-ноды | диапазон дней сессии |
length_days | nodes.fields у loop-ноды | длина петли, дефолт 30 |
Это грубая модель: день — атомарная единица. Нельзя сказать «событие произошло в 3 часа дня». Для текущего use-case — достаточно; для движка с модификаторами времени суток — нет.
Целевая модель: тики
HANDOFF-архитектура вводит Campaign.tick_unit_seconds — длину тика в
секундах, задаётся per-кампания. Для «Мать Учения» — 6 секунд (один раунд
боёвки D&D 5e). Время в БД хранится как at_tick: int от точки отсчёта
кампании.
Зачем целые числа, а не datetime/timestamp?
- Временны́е сравнения — простые операции над int, без timezone-магии.
- Один
at_tickперекрывает весь масштаб: раунд боёвки (6с), дневной переход (86 400с / 6 = 14 400 тиков), месяц — всё одним числом. - Конвертация в человекочитаемый формат («День 5, Час 22») — presentation
layer, не хранимые данные. Разные кампании с разными
tick_unit_secondsчитают один и тот же формат хранения.
Per-actor clock
У каждого персонажа свой current_tick (в коде сейчас — нет явного поля,
time_position хранится неявно через последнее событие). В целевой модели
это Actor.current_tick — в HANDOFF-терминологии Actor = всё, что имеет
clock. В коде сейчас: PC-нода (node_type='pc' / 'character'); NPC —
обычная нода без clock.
Это и есть асинхронное время: когда DM записывает транзакцию, он указывает
day_in_loop конкретного PC, а не «сегодняшний день кампании». Два персонажа
могут существовать в разных «сейчас» одновременно.
Длинные действия
В целевой модели: «тренируюсь магии 3 месяца» — один event со start_tick
и duration_ticks, не итерация миллиона тиков. Resolver знает, что это
действие занимает диапазон и блокирует actor'а на это время.
Сейчас: длинные действия выражаются хроникой или комментарием к транзакции,
без формальной модели duration.
Модификаторы стоимости
Целевая архитектура: стоимость любого действия = base_cost × modifier_stack.
Стек применяется в фиксированном порядке: terrain × weather × time-of-day ×
travel-mode × condition. UI показывает разложенную цену до принятия
решения.
Сейчас: стоимости неявные, DM считает в голове. Modifier stack и weather resolver — roadmap.
→ roadmap/time-and-modifiers.md,
roadmap/tick-time-model.md
Переход от
(loop_number, day_in_loop)к(loop_id, at_tick)— одна из крупнейших миграций в engine pivot. Затрагивает практически все таблицы. Риски и план — вroadmap/tick-time-model.md.