diff --git a/Assets/Resources/BattleConfig.asset b/Assets/Resources/BattleConfig.asset index ac80f8c..9c5fc4c 100644 --- a/Assets/Resources/BattleConfig.asset +++ b/Assets/Resources/BattleConfig.asset @@ -12,7 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 7264ce138a844adc81fa69b77a27c8a6, type: 3} m_Name: BattleConfig m_EditorClassIdentifier: - version: 6 + version: 7 counterPercent: 25 wallsPercent: 20 siegeVsWallsPercent: 50 @@ -30,8 +30,8 @@ MonoBehaviour: - 450 - 160 - 110 - - 110 - - 110 + - 74 + - 20 weakLossPermille: - 450 - 740 diff --git a/Assets/Resources/HospitalConfig.asset b/Assets/Resources/HospitalConfig.asset index d30f23c..99dffa2 100644 --- a/Assets/Resources/HospitalConfig.asset +++ b/Assets/Resources/HospitalConfig.asset @@ -13,10 +13,10 @@ MonoBehaviour: m_Name: HospitalConfig m_EditorClassIdentifier: bedsByLevel: 140000001e00000028000000320000003c000000 - temporaryBeds: 0 + temporaryBeds: 5 requiredTownHallLevel: 1 buildWood: 250 buildStone: 150 healGameHours: 04000000040000000600000008000000 healFoodPerSoldier: 00000000000000000000000000000000 - healGoldPerSoldier: 00000000000000000000000000000000 + healGoldPerSoldier: 01000000010000000200000003000000 diff --git a/Assets/Scripts/App/BattleConfig.cs b/Assets/Scripts/App/BattleConfig.cs index 9bbbf3c..cfe2f27 100644 --- a/Assets/Scripts/App/BattleConfig.cs +++ b/Assets/Scripts/App/BattleConfig.cs @@ -30,7 +30,7 @@ public class BattleConfig : ScriptableObject [Tooltip("Опорные точки соотношения сил (сильная / слабая) в десятых долях процента: 1000 = 1:1, 3000 = 3:1. Первая -- обязательно 1000, дальше по возрастанию")] public int[] lossRatioPermille = { 1000, 3000, 5000, 7000, 10000 }; [Tooltip("Доля выбывших у сильной стороны в каждой точке, десятые доли процента: 450 = 45%")] - public int[] strongLossPermille = { 450, 160, 110, 110, 110 }; + public int[] strongLossPermille = { 450, 160, 110, 74, 20 }; [Tooltip("Доля выбывших у слабой стороны в каждой точке, десятые доли процента: 740 = 74%")] public int[] weakLossPermille = { 450, 740, 880, 920, 950 }; [Tooltip("Разгром дальше последней точки: слабая сторона плавно стремится к этому пределу, десятые доли процента. Решения 31.10, п. 5: 990 = 99%")] diff --git a/Assets/Scripts/App/HospitalConfig.cs b/Assets/Scripts/App/HospitalConfig.cs index 5423e3e..7341cce 100644 --- a/Assets/Scripts/App/HospitalConfig.cs +++ b/Assets/Scripts/App/HospitalConfig.cs @@ -14,8 +14,8 @@ public class HospitalConfig : ScriptableObject [Header("Здание «Госпиталь» (ТЕСТ)")] [Tooltip("Мест для лечения по уровням госпиталя I..V. Регламент 31.10: 20..60. Число строк -- число уровней")] public int[] bedsByLevel = { 20, 30, 40, 50, 60 }; - [Tooltip("Мест, пока в городе нет госпиталя. 0 -- раненые ждут, пока госпиталь не построят")] - public int temporaryBeds = 0; + [Tooltip("Мест, пока в городе нет госпиталя. Решения 01.11, п. 3: 5. После постройки заменяются штатными, не складываются")] + public int temporaryBeds = 5; [Tooltip("С какого уровня Ратуши можно строить")] public int requiredTownHallLevel = 1; [Tooltip("Цена постройки: дерево")] @@ -27,11 +27,11 @@ public class HospitalConfig : ScriptableObject [Tooltip("Пехота, лучники, кавалерия, осадные. Регламент 31.10: 4 / 4 / 6 / 8")] public int[] healGameHours = { 4, 4, 6, 8 }; - [Header("Цена лечения одного воина -- НЕ УТВЕРЖДЕНА (0 -- без цены)")] + [Header("Цена лечения одного воина (решения 01.11, п. 3: золото 1 / 1 / 2 / 3)")] [Tooltip("Еды за воина: пехота, лучники, кавалерия, осадные")] public int[] healFoodPerSoldier = { 0, 0, 0, 0 }; [Tooltip("Золота за воина: пехота, лучники, кавалерия, осадные")] - public int[] healGoldPerSoldier = { 0, 0, 0, 0 }; + public int[] healGoldPerSoldier = { 1, 1, 2, 3 }; static HospitalConfig cached; diff --git a/Assets/Scripts/Modules/Army/ArmyService.cs b/Assets/Scripts/Modules/Army/ArmyService.cs index 41f7267..fcdbd33 100644 --- a/Assets/Scripts/Modules/Army/ArmyService.cs +++ b/Assets/Scripts/Modules/Army/ArmyService.cs @@ -14,10 +14,10 @@ using System.Text; // Числа госпиталя (регламент 31.10, ТЕСТ). В игре -- из таблицы HospitalConfig. public sealed class HospitalRules { - public int TemporaryBeds = 20; // мест, пока здания госпиталя нет (как у госпиталя I уровня) + public int TemporaryBeds = 5; // мест, пока здания госпиталя нет (решения 01.11, п. 3); после постройки -- штатные, не вместе public int[] HealGameHours = { 4, 4, 6, 8 }; // лечение одного воина: пехота и лучники 4, кавалерия 6, осадные 8 ИГРОВЫХ часов public long GameHourMs = 5 * 60 * 1000; // 1 игровой час = 5 реальных минут - // Цена лечения одного воина по типам -- НЕ УТВЕРЖДЕНА: пока пусто, лечение без цены + // Цена лечения одного воина по типам (решения 01.11, п. 3: 1 / 1 / 2 / 3 золота -- в таблице HospitalConfig); пусто -- без цены public ResourceAmount[][] HealCostPerSoldier = null; public static HospitalRules Default => new HospitalRules(); diff --git a/Assets/Scripts/Modules/Battle/BattleRules.cs b/Assets/Scripts/Modules/Battle/BattleRules.cs index a2b499f..1a9b697 100644 --- a/Assets/Scripts/Modules/Battle/BattleRules.cs +++ b/Assets/Scripts/Modules/Battle/BattleRules.cs @@ -6,8 +6,9 @@ // итог боя запоминает версию, по которой посчитан. public sealed class BattleRules { - public int Version = 6; // 2 -- голод и усталость; 3 -- сила без округления, потери шансом (№12, №13); 4 -- кривая потерь (№14); - // 5 -- разгром дальше 5:1 (решения по 8 вопросам Саши, 31.10, п. 5); 6 -- раненые (п. 7) + public int Version = 7; // 2 -- голод и усталость; 3 -- сила без округления, потери шансом (№12, №13); 4 -- кривая потерь (№14); + // 5 -- разгром дальше 5:1 (решения по 8 вопросам Саши, 31.10, п. 5); 6 -- раненые (п. 7); + // 7 -- сильная сторона 5:1 -> 10:1 с 11% до 2% (решения по отчёту Саши, 01.11, п. 1) // Сила одного воина каждого типа. В игре -- из GameData.Troops (там же цена и время обучения). public int[] UnitPower = { 4, 5, 9, 16 }; @@ -23,9 +24,9 @@ public sealed class BattleRules // (линейно), без порогов-переключателей. // Разгром (решения 31.10, п. 5): 7:1 -- 92%, 10:1 -- 95%, дальше слабая сторона плавно стремится к WeakLossMaxPermille (99%): // v = max - (max - v_last) * R_last / R -- при 20:1 97%, при 100:1 98,6%; 100% не достигается. - // Сильная сторона за последней точкой -- значение последней точки (документ числа для неё не задаёт). + // Сильная сторона (решения по отчёту Саши, 01.11, п. 1): 5:1 -- 11%, плавно до 2% при 10:1, дальше 2%. public int[] LossRatioPermille = { 1000, 3000, 5000, 7000, 10000 }; // 1:1, 3:1, 5:1, 7:1, 10:1 - public int[] StrongLossPermille = { 450, 160, 110, 110, 110 }; // сильная сторона: 45%, 16%, 11%, 11%, 11% + public int[] StrongLossPermille = { 450, 160, 110, 74, 20 }; // сильная сторона: 45%, 16%, 11%, 7,4%, 2% public int[] WeakLossPermille = { 450, 740, 880, 920, 950 }; // слабая сторона: 45%, 74%, 88%, 92%, 95% public int WeakLossMaxPermille = 990; // верхний предел ожидаемых потерь слабой стороны: 99% diff --git a/Assets/Scripts/Modules/Tests/ModuleTests.cs b/Assets/Scripts/Modules/Tests/ModuleTests.cs index f6c313e..d8ab5fa 100644 --- a/Assets/Scripts/Modules/Tests/ModuleTests.cs +++ b/Assets/Scripts/Modules/Tests/ModuleTests.cs @@ -562,7 +562,9 @@ public class ModuleTests Assert.AreEqual(950, Fight(10000, 1000).DefenderLosses, "10:1 -- 95%"); Assert.AreEqual(970, Fight(20000, 1000).DefenderLosses, "20:1 -- 99 - 4 x 10/20 = 97%"); Assert.AreEqual(986, Fight(100000, 1000).DefenderLosses, "100:1 -- 98,6%"); - Assert.AreEqual(1100, Fight(10000, 1000).AttackerLosses, "сильная сторона дальше 5:1 -- 11%"); + Assert.AreEqual(518, Fight(7000, 1000).AttackerLosses, "сильная 7:1 -- 7,4% от 7000 (решения 01.11, п. 1)"); + Assert.AreEqual(200, Fight(10000, 1000).AttackerLosses, "сильная 10:1 -- 2%"); + Assert.AreEqual(400, Fight(20000, 1000).AttackerLosses, "сильная дальше 10:1 -- 2%"); // Симметрия: тот же перевес у защитника -- те же доли (одинаково для PvE, PvP и Круга) BattleOutcome threeDef = Fight(1000, 3000); diff --git a/Docs/Modules/Army.md b/Docs/Modules/Army.md index a0a125c..ce38eb2 100644 --- a/Docs/Modules/Army.md +++ b/Docs/Modules/Army.md @@ -45,10 +45,10 @@ | Мир | Раненые идут домой вместе с войском (`MarchData.woundedUnits`). | | Армия | Принимает раненых при возвращении, кладёт в госпиталь, лечит, возвращает в город. Сохраняет. | | Население | Считает раненых людьми: `Soldiers` = в городе + в походах + раненые. Погибшие уходят из людей, вылеченные — не новые люди. | -| Город | Здание «Госпиталь» (`Meta/Build/Hospital.cs`): уровни I–V, места 20 / 30 / 40 / 50 / 60 (`HospitalConfig.bedsByLevel`, ТЕСТ), считается самый прокачанный госпиталь. Места — Армии через `IHospital.Beds`. Без госпиталя — `temporaryBeds` (0: раненые ждут постройки). | -| Экономика | Цена лечения — `IEconomy.TrySpend` целиком; **цена не утверждена**, в таблице 0. | +| Город | Здание «Госпиталь» (`Meta/Build/Hospital.cs`): уровни I–V, места 20 / 30 / 40 / 50 / 60 (`HospitalConfig.bedsByLevel`, ТЕСТ), считается самый прокачанный госпиталь. Места — Армии через `IHospital.Beds`. Без госпиталя — 5 временных мест (`temporaryBeds`, решения 01.11, п. 3); после постройки — штатные, не вместе с временными. | +| Экономика | Цена лечения — `IEconomy.TrySpend` целиком: золото за воина 1 / 1 / 2 / 3 (пехота, лучники, кавалерия, осадные; решения 01.11, п. 3). Нет золота — раненые ждут. | -- Таблица `Assets/Resources/HospitalConfig.asset` (ТЕСТ): `bedsByLevel` 20…60, `temporaryBeds` 0 (без госпиталя), постройка — Ратуша 1, 250 дерева, 150 камня, `healGameHours` 4 / 4 / 6 / 8 игровых часа (пехота, лучники, кавалерия, осадные; 1 игровой час = 5 реальных минут), цена за воина `healFoodPerSoldier`, `healGoldPerSoldier` — 0. +- Таблица `Assets/Resources/HospitalConfig.asset` (ТЕСТ): `bedsByLevel` 20…60, `temporaryBeds` 5 (без госпиталя), постройка — Ратуша 1, 250 дерева, 150 камня, `healGameHours` 4 / 4 / 6 / 8 игровых часа (пехота, лучники, кавалерия, осадные; 1 игровой час = 5 реальных минут), цена за воина `healGoldPerSoldier` 1 / 1 / 2 / 3, `healFoodPerSoldier` 0. Цена улучшения госпиталя II–V не утверждена — пока общая цена улучшения зданий. - Раненые ложатся на места по типам по порядку; каждая группа лечится своё время и возвращается в город целиком. - **Мест не хватает — лишние ждут** (ТЕСТ, решение Саши 01.11; регламент не задаёт). Место освободилось — ждущие ложатся в тот же момент, в том числе пока игра закрыта. - Не хватает ресурсов на лечение — группа ждёт, ничего не списано. @@ -148,6 +148,7 @@ healNext=<номер следующей группы лечения> | Дата | Кто | Что | | --- | --- | --- | +| 04.11.2026 | Claude | Решения по отчёту Саши от 01.11, п. 3: 5 временных мест до постройки госпиталя, цена лечения 1 / 1 / 2 / 3 золота за воина. | | 01.11.2026 | Claude | Здание «Госпиталь» в городе: уровни I–V, 20–60 мест, окно с ранеными и улучшением; места Армии — через `IHospital` (`HospitalBeds`). Без госпиталя мест нет (`temporaryBeds` = 0). Тест `Hospital_BedsFromBuilding_UpgradeFreesWaiting`. | | 01.11.2026 | Claude | Решения 31.10, п. 7: раненые и лечение у Армии — `WoundedTotal`, `Healing`, `HospitalBeds`, `ReturnUnits(…, woundedUnits, nowMs)`, `ProcessHealing`, `SoldiersHealedEvent`; места от `IHospital` (пока нет здания — `HospitalConfig.temporaryBeds`), лишние ждут, цена через Экономику (не утверждена). Сохранение `wounded=`, `heal=`. Тесты: места и сроки, однократность, люди в Населении, сохранение и цена. | | 01.11.2026 | Claude | Решения 31.10, п. 8: очередь обучения перенесена из `GameProgress` в Армию (`Training`, `CanQueueTraining`, `TryQueueTraining`, `ProcessTraining`, `FinishAllTraining`, `TroopsTrainedEvent`, строки `train=` в сохранении), время — часы игры, перенос старой очереди из `progress.json`. Изменены `GameProgress`, `TroopsPanel`, `Barracks`, `BuildingWindow` (код Вальдемара, с разрешения команды). Тесты: порядок и однократность, лимит и ускорение, люди из Населения и обратно, сохранение и перенос. | diff --git a/Docs/Modules/Battle.md b/Docs/Modules/Battle.md index 5e827ff..a3c4109 100644 --- a/Docs/Modules/Battle.md +++ b/Docs/Modules/Battle.md @@ -62,7 +62,7 @@ | Голод и усталость вместе | Последовательно: 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). Сильная сторона после 5:1 — 11%. Точки — в `BattleConfig` (`lossRatioPermille`, `strongLossPermille`, `weakLossPermille`, предел `weakLossMaxPermille`). | +| Потери (решение №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, за стенами): @@ -108,7 +108,7 @@ - **Командный план и ревью от 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 документ не задаёт — пока 11%, как при 5:1. +- **Сделано 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). - **Открыто:** «пропавшие без вести» (регламент) — отдельно не считаются. @@ -121,6 +121,7 @@ | Дата | Кто | Что | | --- | --- | --- | +| 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`; тесты округления пересчитаны под кривую. | diff --git a/Docs/Modules/README.md b/Docs/Modules/README.md index ac2589d..3336948 100644 --- a/Docs/Modules/README.md +++ b/Docs/Modules/README.md @@ -105,6 +105,7 @@ EditMode-тесты: `Assets/Scripts/Modules/Tests/ModuleTests.cs`. Запуск | Дата | Кто | Что | | --- | --- | --- | +| 04.11.2026 | Claude | Решения по отчёту Саши от 01.11: потери сильной стороны до 2% при 10:1, 5 временных мест госпиталя, цена лечения 1 / 1 / 2 / 3 золота. Обновлены Battle, Army. | | 01.11.2026 | Claude | Здание «Госпиталь» (решения 31.10, п. 7). Обновлены Army, Core, Settlement. | | 01.11.2026 | Claude | Решения 31.10, п. 7: раненые и госпиталь (логика; здание — следующий шаг). Обновлены Battle, Army, Population, World, Core, Settlement. | | 01.11.2026 | Claude | Решения 31.10, п. 8: очередь обучения — у Армии. Обновлены Army, Core, Population. |