Files
MVP1/Docs/Modules/Battle.md
T
AlexanderandClaude Opus 5.5 721881b844 Исправления по ревью 27.10.2026: безопасная отправка и возвращение войск
Армия (п. 2.1):
- ReturnFromMarch / ReturnUnits возвращают bool и зачисляют воинов только по известному походу и только один раз
  (раньше неизвестный поход добавлял выживших пехотой, ReturnUnits — без ограничения)
- вернуться может не больше, чем ушло
- TakeForMarch отклоняет повторный номер похода (раньше перезапись теряла воинов первого)
- загрузка сохранения и перенос старых данных пропускают дубликаты походов

Мир (п. 2.2):
- TryStartMarch проверяет TakeForMarch; при отказе поход откатывается (WorldSimulation.CancelMarch)
- если Армия не знает возвращающийся поход — ни воинов, ни добычи, предупреждение в консоли

Тесты (п. 2.1, 2.2, 2.4):
- неизвестный поход, повторное возвращение, повторный номер, откат похода
- крайние случаи боя: 1×1, 1×10, 10×1, 0×0, пустая сторона, бонусы −100% / −500% / +100000%, бой числом

Формула потерь не менялась. Найдено и записано в Battle.md как открытый вопрос:
одиночный проигравший не гибнет (потери округляются вниз) — правило округления решает команда.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:38:01 +01:00

8.2 KiB
Raw Blame History

Боевая система

Урон, потери, исход боя с учётом типов войск. Модуль только считает: кто победил и сколько погибло каждого типа. Кто идёт в бой и что делать с итогом, решают другие модули.

  • Сборка: Bogatyri.Battle (без Unity и без float — тот же код будет считать бои на сервере).
  • Папка: Assets/Scripts/Modules/Battle.
  • Зависит от: ядра.

Контракт IBattleSystem

BattleOutcome Resolve(BattleRequest request)

BattleRequest Что это
AttackerUnits, DefenderUnits Состав сторон по типам (TroopType: пехота, лучники, кавалерия, осадные).
Attackers, Defenders Число воинов, если состав не задан (null) — тогда все считаются пехотой.
DefenderFortified Защитники за стенами (город, замок). У лагерей разбойников стен нет — бой в поле.
AttackerBonusPercent, DefenderBonusPercent Бонусы сторон в процентах: герой, технологии, голод (минус).
BattleOutcome Что это
Victory Победил атакующий.
AttackerPower, DefenderPower Сила сторон ×100 (целые числа): с контрами, стенами и бонусами.
AttackerLosses, DefenderLosses, Survivors Потери сторон и выжившие атакующие — всего.
AttackerSurvivorUnits, DefenderLossUnits То же по типам войск.

Регистрирует App/GameModules: силу воина каждого типа передаёт из GameData.Troops (4 / 5 / 9 / 16), чтобы числа были в одном месте. Мир получает модуль при создании симуляции, не через реестр — так симуляцию можно запустить на сервере.

Правила

Это предложение. Формула боя — открытый вопрос этапа 6 плана, команда должна её утвердить. Все числа — константы в начале BattleSystem.cs.

Правило Значение
Сила стороны Сумма по типам: воины × сила типа × (100% + контры + осадный бонус) × (100% + бонусы стороны).
Контры, +25% Пехота сильнее кавалерии, кавалерия сильнее лучников, лучники сильнее пехоты. Бонус взвешивается по составу врага: против армии, где кавалерии 20%, пехота получает +5%.
Осадные Против стен +50%, в поле −25%.
Стены Защитники за стенами сильнее на 20%.
Исход Побеждает тот, у кого сила больше; ничья — защитнику.
Потери проигравшего Половина каждого типа.
Потери победителя Половина × (сила проигравшего / сила победителя): чем ровнее бой, тем больше потерь, но не больше половины.

Примеры (замок Змея Горыныча, гарнизон 20: пехота 8, лучники 6, кавалерия 4, осадные 2, за стенами):

Атакующие Сила Исход Вернутся
30 пехоты 126 против 155 поражение 15
40 пехоты 168 против 155 победа 22
30 смешанных (12 / 8 / 6 / 4) 248 против 154 победа —

Бой без состава (старые походы, Attackers и Defenders числами) считается так, будто все — пехота. Без стен 30 против 20 — по-прежнему победа и 20 вернувшихся.

События

Нет: исход возвращается вызвавшему модулю.

Сохранение

Нет данных.

Решения и план

  • Командный план и ревью от 27.10.2026 (Bogatyri_Plan_razrabotki.docx, Bogatyri_Battle_Army_Review_27-10-2026.docx): случайность ±5% каждой стороне (воспроизводимая), прогноз риска вместо точного итога, потери от соотношения сил, раненые и погибшие, голод −50%, усталость −20%, числа — в настройки. Делается на этапе 2 плана.
  • Открыто: малые отряды и округление. Потери округляются вниз по каждому типу, поэтому одиночный проигравший не гибнет никогда. Проверено 28.10.2026:
Бой Исход Потери атакующего Потери защитника
1 против 1 ничья, защитнику 0 0
1 против 10 поражение 0 0
10 против 1 победа 0 0

Варианты: округлять потери проигравшего вверх; или воспроизводимая случайность для остатка (одиночка гибнет с шансом 50%). Решает команда, тест Battle_EdgeCases_OutcomeAndArithmetic правило не фиксирует.

  • Утвердить формулу (контры, стены, осадные, потери) или поправить числа в BattleSystem.cs.
  • Бонус героя пока не передаётся в поход: GameProgress.ArmyBonus и сила героя в слое приложения. Нужен способ передать их в AttackerBonusPercent — например, через новый контракт или поле похода.
  • Решение команды от 19.10.2026: при голоде сильные дебаффы на боевую эффективность, включая гарнизон и походы. Для этого есть AttackerBonusPercent / DefenderBonusPercent (минус), проценты — из EconomyConfig. Голод уже считает модуль Населения (IsStarving).
  • Гарнизоны соседей пока не уменьшаются после боя: соседи — заглушки до сервера.

Журнал изменений

Дата Кто Что
28.10.2026 Claude Тест крайних случаев (ревью, п. 2.4): 1×1, 1×10, 10×1, 0×0, пустая сторона, смешанный состав, бонусы −100%, −500%, +100000%, бой числом. Проверяются исход и арифметика (нет отрицательных, выжившие + погибшие = ушедшие). Правило округления потерь — открытый вопрос.
27.10.2026 Claude Бой с типами войск: сила по типам из GameData.Troops, контры (пехота > кавалерия > лучники > пехота, +25%), стены +20%, осадные +50% против стен и −25% в поле, потери по типам. BattleRequest и BattleOutcome расширены, старый бой «числом» считается пехотой. Тесты: контры, стены, осадные, потери в пределах состава.
19.10.2026 Claude Модуль выделен: BattleResolver из WorldSimulation.cs стал BattleSystem с контрактом IBattleSystem; BattleOutcome перенесён в ядро. Правила не изменились.