Бой: отдельный статус среди выбывших (доля не утверждена -- 0, выключено), версия правил 8. Армия: пропавшие из похода -- в группу; окно 6 игровых часов после обратного пути, возвращение 2-4 группами, 70% здоровыми / 30% ранеными, остальные списываются; сохранение без повторного возвращения. Население: занимают места, но не едят. Окна: сколько пропало и сколько ещё может вернуться. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
22 KiB
Боевая система
Урон, потери, исход боя с учётом типов войск. Модуль только считает: кто победил и сколько погибло каждого типа. Кто идёт в бой и что делать с итогом, решают другие модули.
- Сборка:
Bogatyri.Battle(без Unity и без float — тот же код будет считать бои на сервере). - Папка:
Assets/Scripts/Modules/Battle. - Зависит от: ядра.
Контракт IBattleSystem
BattleOutcome Resolve(BattleRequest request)— бой.BattleForecast Forecast(BattleRequest request)— прогноз до боя с учётом случайности. Итог конкретного боя не раскрывает.
BattleRequest |
Что это |
|---|---|
AttackerUnits, DefenderUnits |
Состав сторон по типам (TroopType: пехота, лучники, кавалерия, осадные). |
Attackers, Defenders |
Число воинов, если состав не задан (null) — тогда все считаются пехотой. |
DefenderFortified |
Защитники за стенами (город, замок). У лагерей разбойников стен нет — бой в поле. |
AttackerBonusPercent, DefenderBonusPercent |
Бонусы сторон в процентах: герой, технологии, голод (минус). |
AttackerConditions, DefenderConditions |
Состояния сторон (BattleCondition, флаги): Famine — голод, Fatigue — усталость. |
Seed |
Номер боя. Из него считается случайность сторон: тот же номер с теми же войсками — всегда тот же итог. |
BattleOutcome |
Что это |
|---|---|
Victory |
Победил атакующий. |
BattleId, RulesVersion |
Номер боя (= Seed) и версия правил, по которой посчитан итог. |
AttackerPower, DefenderPower |
Итоговая сила сторон ×100 (целые числа): с контрами, стенами, бонусами и случайностью. |
AttackerLuckPermille, DefenderLuckPermille |
Выпавшая случайность сторон в десятых долях процента: +23 = +2,3%. |
AttackerConditionPercent, DefenderConditionPercent |
Сколько силы оставили состояния: 100 — без штрафов, 50 — голод, 40 — голод и усталость. |
AttackerLosses, DefenderLosses, Survivors |
Выбывшие (погибшие + раненые) и выжившие атакующие — всего. |
…UnitsBefore, …SurvivorUnits, …DeadUnits, …WoundedUnits |
По типам войск и для обеих сторон: до боя, выжившие, погибшие, раненые. По каждому типу: до = выжившие + погибшие + раненые. Доля погибших среди выбывших — от соотношения сил своей стороны (см. «Погибшие и раненые»). |
DefenderLossUnits |
Выбывшие защитники по типам — для старого кода. |
BattleForecast |
Что это |
|---|---|
Chance |
Advantage — победа при любой случайности, Uncertain — решает случайность, HighRisk — поражение при любой случайности. |
WinChancePermille |
Шанс победы нападающего в десятых долях процента (решение №11). Точный: перебираются все 101 × 101 равновероятных сочетаний удачи сторон. |
AttackerPowerMin/Max, DefenderPowerMin/Max |
Сила сторон при крайних значениях случайности. |
AttackerLossesMin/Max |
Потери атакующего: от лучшего случая до худшего, с учётом округления потерь (№13). |
Регистрирует App/GameModules: числа боя — из таблицы BattleConfig, сила воина каждого типа — из GameData.Troops (4 / 5 / 9 / 16). Мир получает модуль при создании симуляции, не через реестр — так симуляцию можно запустить на сервере.
Правила
Числа — стартовые для тестирования (план от 27.10.2026). Все они в одном месте:
- в игре — таблица
Assets/Resources/BattleConfig.asset: выдели её в окне Project и меняй числа в Inspector, изменения действуют со следующего запуска. Скрипт таблицы —App/BattleConfig.cs; - в модуле —
BattleRules.cs(значения по умолчанию для тестов и сервера).App/GameModulesпереводит таблицу вBattleRules; - сила воинов — в
GameData.Troops, рядом с ценой и временем обучения; - поменял числа — подними
version: итог боя запоминает версию правил (RulesVersion).
| Правило | Значение |
|---|---|
| Сила стороны | Сумма по типам: воины × сила типа × (100% + контры + осадный бонус) × (100% + бонусы стороны). |
| Контры, +25% | Пехота сильнее кавалерии, кавалерия сильнее лучников, лучники сильнее пехоты. Бонус взвешивается по составу врага: против армии, где кавалерии 20%, пехота получает +5%. |
| Осадные | Против стен +50%, в поле −25%. |
| Стены | Защитники за стенами сильнее на 20%. |
| Удача, ±5% | Решение №12. Каждой стороне от −5% до +5% (с шагом 0,1%), отдельно. Считается из номера боя (Seed, алгоритм SplitMix64) — одинаково на любой платформе. Применяется после всех модификаторов. Не прокачивается. |
| Без промежуточного округления | Решение №12. Сила считается точной дробью (BigInteger): контра по составу врага, бонусы, голод и удача не округляются по дороге. Округляется один раз — только число для показа. Пример: 3 пехоты против 1 кавалерии и 2 пехоты — сила 13,00 (раньше 12,96). |
| Голод, −50% | Утверждено 27.10.2026. Сила голодной стороны ×50%. Проверяется на момент боя. Действует и на нападающих, и на защитников. |
| Усталость, −20% | Утверждено 27.10.2026. Сила ×80%. Правило уже в модуле, но пока никто её не выставляет: не утверждено, как учитывать смешивание свежих и уставших войск. |
| Голод и усталость вместе | Последовательно: 50% × 80% = 40% силы (а не 30%). |
| Порядок | Контры и осадные → бонусы стороны → состояния (голод, усталость) → случайность. |
| Исход | Побеждает тот, у кого итоговая сила больше; ничья — защитнику. |
| Потери (решение №14, регламент 31.10) | Доля выбывших каждого типа зависит от соотношения итоговых сил (сильная / слабая). Опорные точки (ТЕСТ): 1:1 — 45% / 45%, 3:1 — 16% / 74%, 5:1 — 11% / 88%. Между точками — плавно (линейно по соотношению), без порогов. Одинаково для PvE, PvP и Круга. Разгром (решения по 8 вопросам Саши, 31.10, п. 5): 7:1 — 92%, 10:1 — 95% у слабой стороны, дальше плавно к пределу 99% (v = 99% − 4% × 10 / R: 20:1 — 97%, 100:1 — 98,6%), 100% не достигается. Небольшая армия всё равно может погибнуть целиком из-за округления шансом (№13). Сильная сторона (решения по отчёту Саши, 01.11, п. 1): 5:1 — 11%, плавно до 2% при 10:1, дальше 2%. Точки — в BattleConfig (lossRatioPermille, strongLossPermille, weakLossPermille, предел weakLossMaxPermille). |
| Округление потерь | Решение №13. Целая часть гарантирована, дробная — шанс ещё одной потери. Бросок — из отдельного потока случайности (своя «соль» на сторону и тип войск, не пересекается с удачей), воспроизводим по номеру боя. Одиночка-проигравший (потеря 0,5) гибнет примерно в половине боёв; 3 проигравших (1,5) теряют 1 или 2. |
Примеры (замок Змея Горыныча, гарнизон 20: пехота 8, лучники 6, кавалерия 4, осадные 2, за стенами):
| Атакующие | Исход | Вернутся (в среднем) | Враг теряет из 20 |
|---|---|---|---|
| 30 пехоты | поражение | 15,5 | 8,3 |
| 40 пехоты | победа в 97% боёв | 22,5 | 9,2 |
| 30 смешанных (12 / 8 / 6 / 4) | победа | 19,1 | 10,8 |
| 78 смешанных | победа | 67,7 | 16,4 |
Прогноз с учётом случайности (тот же замок, 10 000 боёв с разными номерами):
| Атакующие | Прогноз | Шанс победы | Сила | Потери |
|---|---|---|---|---|
| 30 пехоты | высокий риск поражения | 0% | 119–132 против 147–163 | 14–16 |
| 36 пехоты | исход неясен | 26,5% | 143–158 против 147–163 | 16–18 |
| 40 пехоты | исход неясен | 97,4% | 159–176 против 147–163 | 16–19 |
| 45 пехоты | преимущество | 100% | 179–198 против 147–163 | 18–20 |
| 30 смешанных (12 / 8 / 6 / 4) | преимущество | 100% | 236–260 против 147–162 | 9–14 |
Шанс проверен тестом Battle_WinChance_MatchesRealBattles: совпадает с долей побед в 20 000 настоящих боёв с точностью ±2%.
Бой без состава (старые походы, Attackers и Defenders числами) считается так, будто все — пехота. Без стен 30 против 20 (1,5:1) — победа, возвращаются 18–19.
События
Нет: исход возвращается вызвавшему модулю.
Сохранение
Нет данных.
Решения и план
-
Сделано 28.10.2026 (этап 2 плана): числа — в таблицу
BattleConfigс версией; итог боя симметричный для обеих сторон; случайность ±5% из номера боя; прогноз «преимущество / исход неясен / высокий риск поражения». -
Решено комплектом 29.10.2026 и сделано 30.10.2026: шанс победы в прогнозе (№11), сила без промежуточного округления (№12), округление потерь шансом (№13), голод на момент боя по истории голода Населения.
-
Сделано 28.10.2026: голод в бою. Голод игрока берётся из модуля Населения (
IPopulation.IsStarving) в момент боя. Пример: 40 пехоты берут замок Змея Горыныча (168 против 155), голодные — нет (84 против 155). Числа —famineStrengthPercentиfatigueStrengthPercentвBattleConfig(версия правил 2). -
Голод в офлайн-бою: Население хранит историю голода (
IPopulation.WasStarvingAt, 48 часов, точность — шаг расчёта 10 минут). Перед боем, прошедшим в прошлом, мир просит Население досчитать время, затем спрашивает голод на момент прихода войска. -
Голод гарнизона пока не нужен: на город игрока никто не нападает. Правило защитника уже есть (
DefenderConditions). -
Порядок бонусов и случайности решён (№12): удача — после всех модификаторов.
-
Номер боя в мультиплеере должен выдавать сервер: иначе клиент мог бы подбирать удачный.
-
Командный план и ревью от 27.10.2026 (
Bogatyri_Plan_razrabotki.docx,Bogatyri_Battle_Army_Review_27-10-2026.docx): случайность ±5% каждой стороне (воспроизводимая), прогноз риска вместо точного итога, потери от соотношения сил, раненые и погибшие, голод −50%, усталость −20%, числа — в настройки. Делается на этапе 2 плана. -
Малые отряды решены (№13): одиночка-проигравший гибнет с шансом по дробной части потерь.
-
Сделано 31.10.2026: кривая потерь (№14) по опорным точкам регламента 31.10.
-
Сделано 01.11.2026: разгром дальше 5:1 по решениям 31.10, п. 5. Потери сильной стороны после 5:1 — решены 01.11 (до 2% при 10:1).
-
Сделано 01.11.2026: погибшие и раненые (регламент 31.10, решения по 8 вопросам Саши, п. 7). Из выбывших стороны погибает доля по её соотношению сил (своя / чужая): 1:5 — 70%, 1:2 — 45%, 1:1 — 30%, 3:1 и выше — 20%, между точками плавно; остальные — раненые. Дробная часть — бросок из отдельного потока (не удачи и не потерь). Точки —
BattleConfig(deadRatioPermille,deadSharePermille). Версия правил 6. Раненых хранит и лечит Армия (Army.md). -
Открыто: «пропавшие без вести» (регламент) — отдельно не считаются.
-
Утвердить формулу (контры, стены, осадные, потери) или поправить числа в
BattleSystem.cs. -
Бонус героя пока не передаётся в поход:
GameProgress.ArmyBonusи сила героя в слое приложения. Нужен способ передать их вAttackerBonusPercent— например, через новый контракт или поле похода. -
Решение команды от 19.10.2026: при голоде сильные дебаффы на боевую эффективность, включая гарнизон и походы. Для этого есть
AttackerBonusPercent/DefenderBonusPercent(минус), проценты — изEconomyConfig. Голод уже считает модуль Населения (IsStarving). -
Гарнизоны соседей пока не уменьшаются после боя: соседи — заглушки до сервера.
Журнал изменений
| Дата | Кто | Что |
|---|---|---|
| 04.11.2026 | Claude | Пропавшие без вести (решения 01.11, п. 5): из выбывших сначала отделяются пропавшие (MissingSharePermille, не утверждено — 0), остальные делятся на погибших и раненых. BattleOutcome.AttackerMissingUnits / DefenderMissingUnits. Версия правил 8. |
| 04.11.2026 | Claude | Решения по отчёту Саши от 01.11, п. 1: потери сильной стороны 5:1 → 10:1 плавно с 11% до 2%, дальше 2% (strongLossPermille 450 / 160 / 110 / 74 / 20). Версия правил 7. |
| 01.11.2026 | Claude | Раненые (решения 31.10, п. 7): выбывшие делятся на погибших и раненых по соотношению сил стороны — 70% / 45% / 30% / 20% погибших при 1:5 / 1:2 / 1:1 / 3:1. Таблица в BattleConfig, версия правил 6, тест Battle_DeadAndWounded_ShareBySideRatio. |
| 01.11.2026 | Claude | Разгром дальше 5:1 (решения 31.10, п. 5): точки 7:1 — 92% и 10:1 — 95%, дальше слабая сторона плавно стремится к 99% (weakLossMaxPermille). Версия правил 5. Тест кривой продлён до 30:1. |
| 31.10.2026 | Claude | Кривая потерь (решение №14, регламент 31.10): доля выбывших от соотношения итоговых сил по опорным точкам 1:1 — 45% / 45%, 3:1 — 16% / 74%, 5:1 — 11% / 88%, между ними плавно; вместо «проигравший 50%». Точки в BattleConfig, неверная таблица — значения по умолчанию. Версия правил 4. Тест Battle_LossCurve_ControlPointsAndSmoothness; тесты округления пересчитаны под кривую. |
| 30.10.2026 | Claude | Комплект требований 29.10, решения №11–13: точный шанс победы в прогнозе (BattleForecast.WinChancePermille); сила — точной дробью без промежуточного округления; потери — целая часть гарантирована, дробная — шанс из отдельного потока случайности. Версия правил 3. Тесты: шанс совпадает с настоящими боями, сила без округления, округление потерь. |
| 28.10.2026 | Claude | Голод и усталость: BattleCondition (флаги), BattleRequest.AttackerConditions / DefenderConditions, BattleOutcome.…ConditionPercent; голод −50%, усталость −20%, вместе 40% (последовательно); числа в BattleConfig и BattleRules (версия 2). Тесты: штрафы, голод переворачивает бой у нападающего и у защитника, прогноз с голодом. |
| 28.10.2026 | Claude | Этап 2 плана: числа боя — в таблицу BattleConfig (Assets/Resources/BattleConfig.asset) и BattleRules с версией; случайность ±5% каждой стороне из BattleRequest.Seed; Forecast — прогноз риска; BattleOutcome симметричный: до боя, выжившие, погибшие, раненые (пока нули) по типам у обеих сторон, номер боя, версия правил, удача сторон. Тесты: случайность воспроизводима и в пределах ±5%, прогноз не противоречит бою, итог сходится по типам, правила из таблицы. |
| 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 перенесён в ядро. Правила не изменились. |