diff --git a/Assets/Resources/HospitalConfig.asset b/Assets/Resources/HospitalConfig.asset index c922945..d30f23c 100644 --- a/Assets/Resources/HospitalConfig.asset +++ b/Assets/Resources/HospitalConfig.asset @@ -12,7 +12,11 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: c25bbe993b314a15a6d1a8aaf0017d55, type: 3} m_Name: HospitalConfig m_EditorClassIdentifier: - temporaryBeds: 20 + bedsByLevel: 140000001e00000028000000320000003c000000 + temporaryBeds: 0 + requiredTownHallLevel: 1 + buildWood: 250 + buildStone: 150 healGameHours: 04000000040000000600000008000000 healFoodPerSoldier: 00000000000000000000000000000000 healGoldPerSoldier: 00000000000000000000000000000000 diff --git a/Assets/Scripts/App/BuildingWindow.cs b/Assets/Scripts/App/BuildingWindow.cs index 41bdc58..78418ac 100644 --- a/Assets/Scripts/App/BuildingWindow.cs +++ b/Assets/Scripts/App/BuildingWindow.cs @@ -54,10 +54,12 @@ public static class BuildingWindow var storage = focus != null ? focus.GetComponent() : null; var townHall = focus != null ? focus.GetComponent() : null; var barracks = focus != null ? focus.GetComponent() : null; + var hospital = focus != null ? focus.GetComponent() : null; if (focus == null) WorkersList(card); else if (townHall != null) TownHallSection(card, townHall); else if (barracks != null) BarracksSection(card, barracks); + else if (hospital != null) HospitalSection(card, hospital); else if (rc != null && rc.IsWorkplace) WorkplaceSection(card, rc); else if (rc != null && rc.IsHouse) HouseSection(card, rc); else if (rc != null && rc.resourceType == ResourceType.Gems) CrystalSection(card); @@ -298,6 +300,46 @@ public static class BuildingWindow }, -1, 80, 26); } + // ---------------- Госпиталь (решения 31.10, п. 7) ---------------- + + static void HospitalSection(RectTransform card, Hospital hospital) + { + UIKit.Text(card, $"Госпиталь лечит раненых после боёв. Мест: {Hospital.BedsAt(Hospital.MaxLevel)} " + + "(считается самый прокачанный госпиталь). Кто не поместился -- ждёт места.", + 24, UIKit.Theme.TextDim, TextAlignmentOptions.Center); + + if (GameServices.TryGet(out IArmy army)) + { + Header(card, "Раненые"); + Live(card, () => + { + int healingNow = 0, waiting = 0; + long readyMs = long.MaxValue; + foreach (var h in army.Healing) { healingNow += h.Count; readyMs = Math.Min(readyMs, h.EndMs); } + for (int t = 0; t < army.TypeCount; t++) waiting += army.GetWoundedWaiting(t); + if (healingNow + waiting == 0) return "Раненых нет"; + string next = readyMs != long.MaxValue + ? $", ближайшие вылечатся через {GameData.FormatTime(Math.Max(0, readyMs - GameClock.UnixNowMs) / 1000.0)}" + : ""; + return $"Лечатся {healingNow} из {army.HospitalBeds} мест, ждут места {waiting}{next}"; + }, 24, UIKit.Theme.Text); + Live(card, () => + { + var lines = new List(); + foreach (var h in army.Healing) + if (h.Type >= 0 && h.Type < GameData.Troops.Count) + lines.Add($"{GameData.Troops[h.Type].name} ×{h.Count} — {GameData.FormatTime(Math.Max(0, h.EndMs - GameClock.UnixNowMs) / 1000.0)}"); + return lines.Count > 0 ? string.Join("\n", lines.ToArray()) : ""; + }, 22, UIKit.Theme.TextDim); + var rules = HospitalConfig.Load().healGameHours; + UIKit.Text(card, $"Лечение одного воина: пехота и лучники {rules[0]}, кавалерия {rules[2]}, осадные {rules[3]} игровых ч " + + "(игровой час = 5 мин). Лечатся и пока игра закрыта.", 22, UIKit.Theme.TextDim, TextAlignmentOptions.Center); + } + + UpgradeBlock(card, hospital.level, $"мест {Hospital.BedsAt(hospital.level)} → {Hospital.BedsAt(hospital.level + 1)}", + () => hospital.Level = hospital.level + 1, Hospital.MaxBuildLevel); + } + // Резерв для найма на уровне Ратуши (те же числа, что у модуля Населения) static int ReserveAt(int townHallLevel) { @@ -320,10 +362,10 @@ public static class BuildingWindow // ---------------- Улучшение (общее для зданий с уровнем, кроме Ратуши) ---------------- - static void UpgradeBlock(RectTransform card, int level, string effect, Action raiseLevel) + static void UpgradeBlock(RectTransform card, int level, string effect, Action raiseLevel, int maxLevel = 0) { var config = EconomyConfig.Load(); - if (level >= config.upgrade.maxLevel) + if (level >= (maxLevel > 0 ? maxLevel : config.upgrade.maxLevel)) // у госпиталя свой предел: I-V { UIKit.Text(card, "Максимальный уровень", 26, UIKit.Theme.Gold, TextAlignmentOptions.Center); return; diff --git a/Assets/Scripts/App/GameModules.cs b/Assets/Scripts/App/GameModules.cs index 18211ee..c588a24 100644 --- a/Assets/Scripts/App/GameModules.cs +++ b/Assets/Scripts/App/GameModules.cs @@ -99,6 +99,10 @@ public static class GameModules // Казармы появляются в списке стройки кодом -- до восстановления зданий из сохранения EventBus.Subscribe(_ => BarracksBootstrap.Ensure()); + // Госпиталь (решения 31.10, п. 7): здание города даёт места для лечения Армии через контракт IHospital + GameServices.Register(new HospitalBeds()); + EventBus.Subscribe(_ => HospitalBootstrap.Ensure()); + // Статистика для заданий (GameProgress) -- по событиям Мира EventBus.Subscribe(OnMarchStarted); EventBus.Subscribe(OnMarchReturned); diff --git a/Assets/Scripts/App/HospitalConfig.cs b/Assets/Scripts/App/HospitalConfig.cs index 8a47cfd..5423e3e 100644 --- a/Assets/Scripts/App/HospitalConfig.cs +++ b/Assets/Scripts/App/HospitalConfig.cs @@ -7,13 +7,21 @@ using UnityEngine; // Create -> Bogatyri -> Hospital Config, имя файла -- HospitalConfig, папка -- Resources. // // Модуль Армии таблицу не видит: App/GameModules передаёт числа в ArmyService. -// Здание госпиталя (уровни I-V, 20-60 мест) -- следующий шаг; пока его нет, работают временные места отсюда. +// Места даёт здание «Госпиталь» (Meta/Build/Hospital.cs) через контракт IHospital. [CreateAssetMenu(fileName = "HospitalConfig", menuName = "Bogatyri/Hospital Config")] public class HospitalConfig : ScriptableObject { - [Header("Места")] - [Tooltip("Мест для лечения, пока в городе нет здания госпиталя. Регламент 31.10: госпиталь I уровня -- 20")] - public int temporaryBeds = 20; + [Header("Здание «Госпиталь» (ТЕСТ)")] + [Tooltip("Мест для лечения по уровням госпиталя I..V. Регламент 31.10: 20..60. Число строк -- число уровней")] + public int[] bedsByLevel = { 20, 30, 40, 50, 60 }; + [Tooltip("Мест, пока в городе нет госпиталя. 0 -- раненые ждут, пока госпиталь не построят")] + public int temporaryBeds = 0; + [Tooltip("С какого уровня Ратуши можно строить")] + public int requiredTownHallLevel = 1; + [Tooltip("Цена постройки: дерево")] + public int buildWood = 250; + [Tooltip("Цена постройки: камень")] + public int buildStone = 150; [Header("Лечение одного воина, ИГРОВЫХ часов (1 игровой час = 5 реальных минут)")] [Tooltip("Пехота, лучники, кавалерия, осадные. Регламент 31.10: 4 / 4 / 6 / 8")] diff --git a/Assets/Scripts/Meta/Build/Hospital.cs b/Assets/Scripts/Meta/Build/Hospital.cs new file mode 100644 index 0000000..d7e3638 --- /dev/null +++ b/Assets/Scripts/Meta/Build/Hospital.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using UnityEngine; + +// ГОСПИТАЛЬ -- здание для лечения раненых (регламент 31.10; решения по 8 вопросам Саши от 31.10, п. 7). +// - Даёт места для лечения: уровни I-V, 20-60 мест (таблица HospitalConfig, ТЕСТ). +// - Считаются самые прокачанные госпитали: второй госпиталь мест не добавляет (регламент про несколько не говорит). +// - Раненых хранит и лечит модуль Армии; здание только сообщает число мест через контракт IHospital (HospitalBeds). +// - Тап открывает окно госпиталя (App/BuildingWindow): места, кто лечится и ждёт, улучшение. +// - Уровень сохраняет модуль Поселения (ILeveledBuilding). +// Вешается на копию обычного здания (HospitalBootstrap). +public class Hospital : MonoBehaviour, ILeveledBuilding +{ + public static readonly List All = new List(); + public static int Count => All.Count; + + public int level = 1; + public int Level { get => level; set => level = Mathf.Clamp(value, 1, MaxBuildLevel); } + + // Уровней у госпиталя столько, сколько строк мест в таблице (регламент: I-V) + public static int MaxBuildLevel => Mathf.Max(1, HospitalConfig.Load().bedsByLevel.Length); + + // Уровень самого прокачанного госпиталя (0 -- госпиталя нет) + public static int MaxLevel + { + get { int m = 0; foreach (var h in All) if (h != null && h.level > m) m = h.level; return m; } + } + + // Мест на уровне (1..5) по таблице + public static int BedsAt(int lvl) + { + var beds = HospitalConfig.Load().bedsByLevel; + if (lvl <= 0 || beds == null || beds.Length == 0) return 0; + return Mathf.Max(0, beds[Mathf.Min(lvl, beds.Length) - 1]); + } + + void OnEnable() { if (!All.Contains(this)) All.Add(this); } + void OnDisable() { All.Remove(this); } + + Vector3 downPos; + + void OnMouseDown() + { + downPos = Input.mousePosition; + } + + // Тап (а не перетаскивание камеры) -- окно госпиталя + void OnMouseUpAsButton() + { + if (UIBlocker.IsPointerOverUI()) return; + if ((Input.mousePosition - downPos).magnitude > 25f) return; + BuildingWindow.Open(gameObject); + } +} + +// Места госпиталя для модуля Армии (контракт IHospital). Регистрирует App/GameModules. +// Госпиталя нет -- 0 мест: Армия берёт HospitalConfig.temporaryBeds. +public sealed class HospitalBeds : IHospital +{ + public int Beds => Hospital.BedsAt(Hospital.MaxLevel); +} diff --git a/Assets/Scripts/Meta/Build/Hospital.cs.meta b/Assets/Scripts/Meta/Build/Hospital.cs.meta new file mode 100644 index 0000000..5c3c4dc --- /dev/null +++ b/Assets/Scripts/Meta/Build/Hospital.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2f4c537a26bd44b3ae851af08c652a34 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Meta/Build/HospitalBootstrap.cs b/Assets/Scripts/Meta/Build/HospitalBootstrap.cs new file mode 100644 index 0000000..2cb3421 --- /dev/null +++ b/Assets/Scripts/Meta/Build/HospitalBootstrap.cs @@ -0,0 +1,65 @@ +using UnityEngine; + +// Добавляет «Госпиталь» в список стройки САМ при запуске сцены (как Казармы -- BarracksBootstrap). +// Срабатывает до восстановления зданий из сохранения (SettlementRestoringEvent), поэтому построенный +// госпиталь и его уровень корректно восстанавливаются из save.json. +public static class HospitalBootstrap +{ + public const string HospitalName = "Госпиталь"; + static GameObject runtimeTemplate; + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] + static void Init() + { + Ensure(); + // при каждом новом входе в город (например, возврат с карты мира) -- снова добавить в новый BuildManager + UnityEngine.SceneManagement.SceneManager.sceneLoaded += (s, m) => Ensure(); + } + + public static void Ensure() + { + var bm = BuildManager.Instance != null ? BuildManager.Instance : Object.FindAnyObjectByType(); + if (bm == null || bm.allBuildings == null) return; + + BuildingData template = null; + foreach (var b in bm.allBuildings) + { + if (b == null) continue; + if (b.buildingName == HospitalName) return; // уже есть + if (template == null && b.prefab != null) template = b; + } + if (template == null) { Debug.LogWarning("[ГОСПИТАЛЬ] Нет ни одного здания-шаблона в BuildManager"); return; } + + // Префаб-шаблон в памяти: копия обычного здания без сбора ресурсов, с компонентом Hospital + if (runtimeTemplate == null) + { + runtimeTemplate = Object.Instantiate(template.prefab); + runtimeTemplate.name = "Hospital"; + runtimeTemplate.SetActive(false); + Object.DontDestroyOnLoad(runtimeTemplate); + foreach (var c in runtimeTemplate.GetComponents()) Object.DestroyImmediate(c); + foreach (var c in runtimeTemplate.GetComponents()) Object.DestroyImmediate(c); + foreach (var c in runtimeTemplate.GetComponents()) Object.DestroyImmediate(c); + for (int i = runtimeTemplate.transform.childCount - 1; i >= 0; i--) + Object.DestroyImmediate(runtimeTemplate.transform.GetChild(i).gameObject); // полоска запаса ресурса + var sr = runtimeTemplate.GetComponent(); + if (sr != null) sr.color = new Color(0.85f, 0.95f, 1f); // пока нет арта -- отличаем цветом (светло-голубой) + runtimeTemplate.AddComponent(); + } + + var config = HospitalConfig.Load(); + var data = ScriptableObject.CreateInstance(); + data.name = "Data_Hospital_Runtime"; + data.buildingName = HospitalName; + data.icon = template.icon; + data.prefab = runtimeTemplate; + data.requiredTownHallLevel = Mathf.Max(1, config.requiredTownHallLevel); + data.cost = new[] + { + new ResourceCost { type = ResourceType.Wood, amount = config.buildWood }, + new ResourceCost { type = ResourceType.Stone, amount = config.buildStone }, + }; + bm.allBuildings.Add(data); + Debug.Log("[ГОСПИТАЛЬ] Добавлен в список стройки"); + } +} diff --git a/Assets/Scripts/Meta/Build/HospitalBootstrap.cs.meta b/Assets/Scripts/Meta/Build/HospitalBootstrap.cs.meta new file mode 100644 index 0000000..489597a --- /dev/null +++ b/Assets/Scripts/Meta/Build/HospitalBootstrap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3301bf2246b4444182d1362fe7bffcc5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Modules/Tests/ModuleTests.cs b/Assets/Scripts/Modules/Tests/ModuleTests.cs index 43b2592..f6c313e 100644 --- a/Assets/Scripts/Modules/Tests/ModuleTests.cs +++ b/Assets/Scripts/Modules/Tests/ModuleTests.cs @@ -1921,4 +1921,30 @@ public class ModuleTests Assert.Greater(wounded, 0, "в бою есть раненые"); Assert.LessOrEqual(m.survivors + wounded, 40, "выжившие + раненые не больше ушедших"); } + + class FakeHospital : IHospital { public int Beds { get; set; } } + + // Места даёт здание госпиталя (IHospital); без здания -- временные из таблицы (0 -- ждут постройки) + [Test] + public void Hospital_BedsFromBuilding_UpgradeFreesWaiting() + { + var army = new ArmyService(2, new HospitalRules { TemporaryBeds = 0 }); + GameServices.Register(army); + army.AddTroops(0, 50); + army.TakeForMarch(1, 50); + army.ReturnUnits(1, null, new[] { 50, 0, 0, 0 }, 0); + Assert.AreEqual(0, army.HospitalBeds, "госпиталя нет -- мест нет"); + Assert.AreEqual(50, army.GetWoundedWaiting(0), "все ждут"); + + var hospital = new FakeHospital { Beds = 20 }; + GameServices.Register(hospital); + army.ProcessHealing(1000); + Assert.AreEqual(20, army.Healing[0].Count, "построили госпиталь I уровня -- 20 легли"); + Assert.AreEqual(1000, army.Healing[0].StartMs); + + hospital.Beds = 40; // улучшили до III + army.ProcessHealing(2000); + Assert.AreEqual(20, army.GetWoundedWaiting(0) + 10, "ещё 20 легли сразу после улучшения: ждут 10"); + Assert.AreEqual(40, army.Healing[0].Count + army.Healing[1].Count); + } } diff --git a/Docs/Modules/Army.md b/Docs/Modules/Army.md index 447799e..a0a125c 100644 --- a/Docs/Modules/Army.md +++ b/Docs/Modules/Army.md @@ -45,16 +45,18 @@ | Мир | Раненые идут домой вместе с войском (`MarchData.woundedUnits`). | | Армия | Принимает раненых при возвращении, кладёт в госпиталь, лечит, возвращает в город. Сохраняет. | | Население | Считает раненых людьми: `Soldiers` = в городе + в походах + раненые. Погибшие уходят из людей, вылеченные — не новые люди. | -| Поселение | Здание госпиталя: уровень и места (`IHospital.Beds`, регламент — 20…60 мест на уровнях I–V). **Здания пока нет** — Армия берёт временные места из таблицы. | +| Город | Здание «Госпиталь» (`Meta/Build/Hospital.cs`): уровни I–V, места 20 / 30 / 40 / 50 / 60 (`HospitalConfig.bedsByLevel`, ТЕСТ), считается самый прокачанный госпиталь. Места — Армии через `IHospital.Beds`. Без госпиталя — `temporaryBeds` (0: раненые ждут постройки). | | Экономика | Цена лечения — `IEconomy.TrySpend` целиком; **цена не утверждена**, в таблице 0. | -- Таблица `Assets/Resources/HospitalConfig.asset` (ТЕСТ): `temporaryBeds` 20 (пока нет здания), `healGameHours` 4 / 4 / 6 / 8 игровых часа (пехота, лучники, кавалерия, осадные; 1 игровой час = 5 реальных минут), цена за воина `healFoodPerSoldier`, `healGoldPerSoldier` — 0. +- Таблица `Assets/Resources/HospitalConfig.asset` (ТЕСТ): `bedsByLevel` 20…60, `temporaryBeds` 0 (без госпиталя), постройка — Ратуша 1, 250 дерева, 150 камня, `healGameHours` 4 / 4 / 6 / 8 игровых часа (пехота, лучники, кавалерия, осадные; 1 игровой час = 5 реальных минут), цена за воина `healFoodPerSoldier`, `healGoldPerSoldier` — 0. - Раненые ложатся на места по типам по порядку; каждая группа лечится своё время и возвращается в город целиком. - **Мест не хватает — лишние ждут** (ТЕСТ, решение Саши 01.11; регламент не задаёт). Место освободилось — ждущие ложатся в тот же момент, в том числе пока игра закрыта. - Не хватает ресурсов на лечение — группа ждёт, ничего не списано. - Повторное возвращение похода или повторный расчёт никого не создают и не лечат дважды. - Окно «Войска»: раненые (лечатся / ждут места), мест в госпитале, когда вылечатся ближайшие. Окно возвращения и итог боя на карте показывают, сколько ранено. -- **Дальше:** здание госпиталя в городе (уровни I–V, окно с очередью) — регистрирует `IHospital`; герои — отдельная очередь (регламент), когда появятся герои. +- Окно госпиталя (тап по зданию): мест, кто лечится и сколько осталось, кто ждёт места, улучшение до V уровня (цена — общая цена улучшения из `EconomyConfig`). +- Госпиталь улучшили — ждущие ложатся сразу (в течение секунды). +- **Дальше:** герои — отдельная очередь лечения (регламент), когда появятся герои. ## Очередь обучения @@ -146,6 +148,7 @@ healNext=<номер следующей группы лечения> | Дата | Кто | Что | | --- | --- | --- | +| 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` (код Вальдемара, с разрешения команды). Тесты: порядок и однократность, лимит и ускорение, люди из Населения и обратно, сохранение и перенос. | | 31.10.2026 | Claude | Слоты похода: `MarchSlots`, `ActiveMarches`; `ArmyService(baseMarchSlots)`, база 2 из таблицы `MarchConfig`; `TakeForMarch` отказывает при занятых слотах. Тест `ArmyService_MarchSlots`. | diff --git a/Docs/Modules/Core.md b/Docs/Modules/Core.md index 8d1cdb1..655b4d4 100644 --- a/Docs/Modules/Core.md +++ b/Docs/Modules/Core.md @@ -36,7 +36,7 @@ | `IWorld` | Мир (`MarchManager`) | Сколько походов в пути; начать поход; начать поход «Разведка» (`TryStartScout`). | | `IWorldMap` | Мир (`MarchManager`) | Для Разведки: границы карты, где поселение, все армии в момент времени (`ArmyOnMap`). | | `IIntelligence` | Разведка (`IntelligenceService`) | Туман (`GetVisibility`), «Обзор», известные чужие армии (`KnownContact`), `ObserveUntil`. | -| `IHospital` | Поселение — здание госпиталя (**пока не реализовано**) | Мест для лечения (`Beds`). Нет — Армия берёт временные места из `HospitalConfig`. | +| `IHospital` | Город — здание «Госпиталь» (`HospitalBeds`, регистрирует `App/GameModules`) | Мест для лечения (`Beds`) по уровню самого прокачанного госпиталя; 0 — госпиталя нет. | | `IBattleSystem` | Бой (`BattleSystem`) | Посчитать исход боя по `BattleRequest`: состав сторон по типам, стены, бонусы. | | `IChat` | Чат (`ChatService`) | История каналов, можно ли писать в канал, отправка сообщения от локального игрока. | | `ICircle` | Круг (`CircleService`) | Круг игрока, список Кругов; основать, вступить, выйти. | diff --git a/Docs/Modules/README.md b/Docs/Modules/README.md index a972349..ac2589d 100644 --- a/Docs/Modules/README.md +++ b/Docs/Modules/README.md @@ -105,6 +105,7 @@ EditMode-тесты: `Assets/Scripts/Modules/Tests/ModuleTests.cs`. Запуск | Дата | Кто | Что | | --- | --- | --- | +| 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. | | 01.11.2026 | Claude | Решения 31.10, п. 2: поход «Разведка». Обновлены World, Core, Intelligence. | diff --git a/Docs/Modules/Settlement.md b/Docs/Modules/Settlement.md index d51af10..8281aa7 100644 --- a/Docs/Modules/Settlement.md +++ b/Docs/Modules/Settlement.md @@ -82,6 +82,7 @@ | Дата | Кто | Что | | --- | --- | --- | +| 01.11.2026 | Claude (Саша) | Новое здание «Госпиталь» в списке стройки (`Meta/Build/HospitalBootstrap.cs`, как Казармы): уровни I–V сохраняются как у остальных (`ILeveledBuilding`), тап — окно госпиталя. Места — в `HospitalConfig`. Арта нет: копия обычного здания светло-голубого цвета. | | 01.11.2026 | Claude (Саша) | Для госпиталя (решения 31.10, п. 7) в ядре есть контракт `IHospital.Beds` — его будет регистрировать здание госпиталя (уровни I–V, 20–60 мест). Здания пока нет. | | 31.10.2026 | Claude | Ратуша: тап больше не улучшает сразу, а открывает окно Ратуши (событие `TownHall.Tapped`, окно — `App/BuildingWindow`). Улучшение — кнопкой в окне, с ценой и тем, что откроется. | | 31.10.2026 | Claude | Казармы стали уровневым зданием (`ILeveledBuilding`, уровень сохраняется как у остальных). Тап открывает окно Казарм: очередь обучения, какие войска открыты и что нужно для остальных, улучшение, кнопка «Нанять войска». Войско открывается, когда хватает и уровня Ратуши, и уровня Казарм; считаются самые прокачанные Казармы, каждые Казармы по-прежнему дают +1 место в очереди. Уровни Казарм: пехота 1, лучники 2, конница 3, осадные 5 (`EconomyConfig`, `soldier.requiredBarracksLevel`) — **ТЕСТ, согласовать с Димой**; цена улучшения общая, тоже не утверждена. |