Прототип пошаговой боевой системы с юнитами на клеточной карте.
- Карта представляет собой прямоугольную сетку размером W×H клеток.
- Каждый юнит имеет уникальный целочисленный идентификатор.
- Один ход соответствует одному действию одного юнита.
- Юниты действуют в порядке их создания.
- Некоторые юниты могут перемещаться:
- за один ход юнит может перейти в любую из 8 соседних клеток, если она свободна;
- если цель недостижима за один ход, юнит продолжает движение к ней в последующих ходах.
- Некоторые юниты могут атаковать, уменьшая количество HP у цели:
- при достижении 0 HP юнит умирает;
- после смерти юнит больше не действует и освобождает занимаемую клетку.
- Симуляция завершается, когда не остаётся юнитов, способных совершать действия.
При запуске приложение получает путь к файлу со сценарием симуляции. Сценарий содержит команды для создания карты, юнитов и их перемещения. Формат входных и выходных данных остаётся неизменным. Событие атаки содержит суммарный урон, который нанес один юнит другому.
- Ближняя атака: атакует случайного юнита в соседней клетке, нанося урон, равный
Strength. - Способность: с вероятностью
Chance / 1000вместо обычной атаки применяет «Рваные раны», наносяRendingурона. - Поведение: если цель для атаки отсутствует, перемещается.
- Дальняя атака: стреляет в случайного юнита на расстоянии от 2 до
Rangeклеток, нанося урон, равныйAgility. Атака возможна только при отсутствии других юнитов в соседних клетках. - Ближняя атака: если дальняя атака невозможна, атакует случайного юнита в соседней клетке, нанося урон, равный
Strength. - Способность: с вероятностью
Chance / 1000вместо обычной дальней атаки применяет отравленные стрелы, нанося в суммеPoisonурона за пять ходов. Урон от яда удваивается в тот ход, когда отравленный юнит получает эффект «Рваные раны». - Урон от яда засчитывается как урон охотника, наложившего эффект.
- Поведение: если цель для атаки отсутствует, перемещается.
- Дальняя атака: стреляет в случайного юнита на расстоянии от 2 до
Rangeклеток, нанося урон, равныйSpirit. - Способность: с вероятностью
Chance / 1000призывает огненную стену из 3 клеток на 5 ходов. Стена не блокирует движение и наноситFirewallурона за ход каждому юниту, находящемуся на ней. - Поведение: если цель для атаки отсутствует, перемещается.
- Дальняя атака: стреляет в случайного юнита на расстоянии от 2 до
Rangeклеток, нанося урон, равныйPower. - Поведение: не перемещается, неуязвима, занимает 2×2 клетки.
- Летающий юнит:
- Не занимает клетку на карте.
- Может перемещаться на 2 клетки за ход.
- Не может быть атакован в ближнем бою.
- Минимальная и максимальная дистанция дальней атаки на ворона снижена на 1.
- Ближняя атака: атакует случайного юнита в соседней клетке, нанося
Strengthурона. - Поведение: если цель для атаки отсутствует, перемещается.
В данной реализации мы отойдём от альтернативного предложения про Очередь Событий. Причина в том, что очередь событий сильно усложняет ответ на вопрос "с какой картиной мира работает каждое последующее событие?". Мы хотим объединить простоту (картина мира всегда одна) и убрать реактивную систему. Решение и нюансы:
- Основная идея в том, чтобы упаковать Намерение действия в данные и прогнать их через этапы: препроцессинг, исполнение, постпроцессинг.
- При этом каждый юнит будет иметь свою цепочку действий, которая последовательно будет создавать Намерение, прогонять через три этапа и, в случае выполнения действия, отменять все остальные.
- Намерение, проходя через подсистемы, будет модифицироваться или генерировать новые намерения.
- Фаза исполнения предобработанного Намерения меняет мир.
- Постпроцессинг при необходимости создаёт новые намерения.
- Текущий ход Юнита заканчивается, когда стек Намерений пуст, и очередь переходит к новому Юниту (на следующем тике), который работает уже в "новой реальности". Это тот момент, который помогает нам обойти ограничение Очереди Событий.
- Архитектура данных базируется на упрощенном ECS-подходе, где свойства (HP, Позиция, Атака) — это компоненты, а логика вынесена в системы и планоровщики интентов.
- System Tick Order: введен слой глобальных системных событий. Это позволяет реализовать «законы мира» (Яд, Смерть, Смерч), которые выполняются в строго определенном порядке (Pre-tick / Post-tick), независимо от «воли» конкретных юнитов.
- Также мы хоть и оставляем Позицию как Кор фичу (контракт на событие ЮнитСпаунд требует этого), мы выносим логику, связанную с Маршем в Фичи. В идеале я бы перешёл от Юнита к Сущности и убрал Позицию как фундаментальное свойство.
- Полное разделение Core/Features, нет связности (Позиция как компромиссное решение).
- Решение более подходящее для TBS, чем Очередь Событий или Реактивная Системы.