diff --git a/Assets/Scripts/App/EconomyConfig.cs b/Assets/Scripts/App/EconomyConfig.cs index 351ec30..87cbd9e 100644 --- a/Assets/Scripts/App/EconomyConfig.cs +++ b/Assets/Scripts/App/EconomyConfig.cs @@ -40,10 +40,8 @@ public class EconomyConfig : ScriptableObject [Tooltip("Офлайн-прогресс считается не больше чем за столько часов. Не утверждено")] public int maxOfflineHours = 48; - [Tooltip("Гражданский резерв: столько свободных жителей нельзя забрать в войско (решение команды от 19.10: 150 свободных, резерв 30 -> 120)")] - public int civilianReserve = 30; - [Tooltip("Прибавка к резерву за каждый следующий уровень Ратуши. Не утверждено")] - public int civilianReservePerTownHallLevel = 0; + [Tooltip("Гражданский резерв по уровню Ратуши: [0] -- уровень 1, [1] -- уровень 2... Столько свободных жителей нельзя забрать в войско. Регламент 31.10, ТЕСТ: 1-2 -- 10, 3-5 -- 20, 6-10 -- 30")] + public int[] civilianReserveByTownHallLevel = { 10, 10, 20, 20, 20, 30, 30, 30, 30, 30 }; } [Serializable] @@ -65,6 +63,8 @@ public class EconomyConfig : ScriptableObject public int workerSlotsPerLevel = 10; [Tooltip("+ к норме за каждый уровень здания выше первого (0.1 = +10%). Не утверждено")] public float efficiencyPerLevel = 0.1f; + [Tooltip("Голод: множитель добычи всех ресурсов, кроме еды (0.5 -- вдвое меньше). Еда добывается без штрафа. Регламент 31.10, ТЕСТ")] + [Range(0f, 1f)] public float famineNonFoodMultiplier = 0.5f; } [Serializable] diff --git a/Assets/Scripts/App/GameModules.cs b/Assets/Scripts/App/GameModules.cs index ad2e202..d96df7e 100644 --- a/Assets/Scripts/App/GameModules.cs +++ b/Assets/Scripts/App/GameModules.cs @@ -150,8 +150,9 @@ public static class GameModules HousingPerTownHallLevel = p.housingPerTownHallLevel, HousingPerHouseLevel = p.housingPerHouseLevel, MaxOfflineHours = p.maxOfflineHours, - CivilianReserve = p.civilianReserve, - CivilianReservePerTownHallLevel = p.civilianReservePerTownHallLevel, + CivilianReserveByTownHallLevel = p.civilianReserveByTownHallLevel != null && p.civilianReserveByTownHallLevel.Length > 0 + ? (int[])p.civilianReserveByTownHallLevel.Clone() + : new PopulationRules().CivilianReserveByTownHallLevel, PeoplePerSoldier = Mathf.Max(1, config.soldier.people) }; } @@ -165,7 +166,8 @@ public static class GameModules PerWorkerPerHour = new double[] { p.woodcutterPerHour, p.farmerPerHour, p.stonecutterPerHour, p.minerIronPerHour, p.minerGoldPerHour }, SlotsPerBuilding = p.workerSlotsT1, SlotsPerLevel = p.workerSlotsPerLevel, - EfficiencyPerLevel = p.efficiencyPerLevel + EfficiencyPerLevel = p.efficiencyPerLevel, + FamineNonFoodMultiplier = p.famineNonFoodMultiplier }; } diff --git a/Assets/Scripts/Meta/Core/GameProgress.cs b/Assets/Scripts/Meta/Core/GameProgress.cs index 777218d..0b9c458 100644 --- a/Assets/Scripts/Meta/Core/GameProgress.cs +++ b/Assets/Scripts/Meta/Core/GameProgress.cs @@ -622,7 +622,7 @@ public class GameProgress : MonoBehaviour // Войска хранит модуль Армии (Docs/Modules/Army.md) -- здесь только обращение к нему static IArmy Army => GameServices.Get(); - // Люди города -- модуль Поселения (часть «Население»). null -- модуль не подключён + // Люди города -- модуль Населения. null -- модуль не подключён static IPopulation Population => GameServices.TryGet(out IPopulation p) ? p : null; // Сколько воинов можно нанять из жителей прямо сейчас (для кнопок найма) @@ -643,7 +643,7 @@ public class GameProgress : MonoBehaviour if (!TroopUnlocked(index)) { UIKit.Toast("Нужна Ратуша " + def.requiredTownHall + " уровня"); return; } if (Barracks.Count == 0) { UIKit.Toast("Сначала постройте Казармы"); return; } if (Data.training.Count >= Barracks.QueueSize) { UIKit.Toast("Очередь обучения заполнена. Больше казарм -- длиннее очередь"); return; } - // Воин -- житель города (модуль Поселения, часть «Население»): берутся свободные жители, резерв не отдаётся + // Воин -- житель города (модуль Населения): берутся свободные жители, резерв не отдаётся var population = Population; if (population != null && population.Recruitable < count) { diff --git a/Assets/Scripts/Meta/Panels/TroopsPanel.cs b/Assets/Scripts/Meta/Panels/TroopsPanel.cs index dd51d46..300f638 100644 --- a/Assets/Scripts/Meta/Panels/TroopsPanel.cs +++ b/Assets/Scripts/Meta/Panels/TroopsPanel.cs @@ -26,7 +26,7 @@ public class TroopsPanel : BasePanel ProfilePanel.KV(army, "В походе на карте мира", GameServices.Get().TroopsInMarches.ToString("N0")); if (GameServices.TryGet(out IPopulation population)) { - // Воины берутся из свободных жителей (модуль Поселения), резерв не нанять + // Воины берутся из свободных жителей (модуль Населения), резерв не нанять ProfilePanel.KV(army, "Обучаются в Казармах", population.InTraining.ToString("N0")); ProfilePanel.KV(army, "Можно нанять из жителей", population.Recruitable.ToString("N0") + " (резерв " + population.CivilianReserve.ToString("N0") + ")"); diff --git a/Assets/Scripts/Modules/Core/Contracts/ModuleContracts.cs b/Assets/Scripts/Modules/Core/Contracts/ModuleContracts.cs index c7ac65d..32c94af 100644 --- a/Assets/Scripts/Modules/Core/Contracts/ModuleContracts.cs +++ b/Assets/Scripts/Modules/Core/Contracts/ModuleContracts.cs @@ -46,14 +46,14 @@ public interface ILeveledBuilding int Level { get; set; } } -// ---------------- Население (часть модуля Поселения): жители, еда, прирост, найм ---------------- +// ---------------- Население: жители, рабочие, еда, голод, прирост, найм ---------------- public interface IPopulation { int Total { get; } // все люди города: жители + обучаются в Казармах + воины (в городе и в походах) int Civilians { get; } // жители (не воины) int Workers { get; } // жители на работе (все профессии вместе) int Idle { get; } // остальные жители: Civilians - Workers - int Workforce { get; } // сколько жителей могут работать: 40% всех людей города (стартовый город -- 120 из 300) + int Workforce { get; } // сколько жителей могут работать: все жители (общего ограничения нет, регламент 31.10) int FreeWorkers { get; } // могут работать, но без работы: Workforce - Workers int Soldiers { get; } // воины -- их хранит модуль Армии int HousingCap { get; } // предел населения: выше него прирост не идёт diff --git a/Assets/Scripts/Modules/Economy/Rules/ProductionService.cs b/Assets/Scripts/Modules/Economy/Rules/ProductionService.cs index 32e1f76..034460c 100644 --- a/Assets/Scripts/Modules/Economy/Rules/ProductionService.cs +++ b/Assets/Scripts/Modules/Economy/Rules/ProductionService.cs @@ -12,13 +12,18 @@ public sealed class ProductionRules public int SlotsPerBuilding = 30; // мест в здании 1 уровня (Лесопилка T1 -- 30) public int SlotsPerLevel = 10; // + мест за каждый уровень выше первого. Не утверждено public double EfficiencyPerLevel = 0.1; // + к норме за каждый уровень выше первого (×1.1, ×1.2...). Не утверждено + + // Голод (регламент 31.10, ТЕСТ): добыча всех ресурсов, кроме еды, ×0.5. Еда -- без штрафа. + // Штраф снимается сразу, как только город снова сыт (модуль Населения сообщает IsStarving). + public double FamineNonFoodMultiplier = 0.5; } // МОДУЛЬ ЭКОНОМИКИ: добыча через рабочих (этап 3). Документ: Docs/Modules/Economy.md // Без Unity: правила те же, что будут на сервере. // // Ресурс производят люди, здание даёт только места и эффективность: -// добыча в час = min(рабочие, места) × норма × эффективность уровня зданий. +// добыча в час = min(рабочие, места) × норма × эффективность уровня зданий, +// а при голоде всё, кроме еды, -- ещё × FamineNonFoodMultiplier (голод считает модуль Населения, IPopulation.IsStarving). // Какие здания стоят в городе, сообщает слой приложения (App/CitySync) -- модуль их не ищет. // Места запоминаются в сохранении: на карте мира и офлайн город не открыт, а добыча идёт. // @@ -80,7 +85,10 @@ public sealed class ProductionService : IProduction, ISaveParticipant int j = (int)job; if (!GameServices.TryGet(out IPopulation population)) return 0; int working = Math.Min(population.GetWorkers(job), slots[j]); - return working * rules.PerWorkerPerHour[j] * efficiency[j]; + double rate = working * rules.PerWorkerPerHour[j] * efficiency[j]; + if (population.IsStarving && WorkJobs.Resource(job) != ResourceType.Food) + rate *= Math.Max(0, Math.Min(1, rules.FamineNonFoodMultiplier)); + return rate; } public void Produce(double hours) diff --git a/Assets/Scripts/Modules/Settlement/Population.meta b/Assets/Scripts/Modules/Population.meta similarity index 100% rename from Assets/Scripts/Modules/Settlement/Population.meta rename to Assets/Scripts/Modules/Population.meta diff --git a/Assets/Scripts/Modules/Settlement/Population/Bogatyri.Population.asmdef b/Assets/Scripts/Modules/Population/Bogatyri.Population.asmdef similarity index 87% rename from Assets/Scripts/Modules/Settlement/Population/Bogatyri.Population.asmdef rename to Assets/Scripts/Modules/Population/Bogatyri.Population.asmdef index d2bc05b..867e1b6 100644 --- a/Assets/Scripts/Modules/Settlement/Population/Bogatyri.Population.asmdef +++ b/Assets/Scripts/Modules/Population/Bogatyri.Population.asmdef @@ -1,5 +1,5 @@ { - "name": "Bogatyri.Settlement.Population", + "name": "Bogatyri.Population", "rootNamespace": "", "references": ["Bogatyri.Core"], "includePlatforms": [], diff --git a/Assets/Scripts/Modules/Settlement/Population/Bogatyri.Population.asmdef.meta b/Assets/Scripts/Modules/Population/Bogatyri.Population.asmdef.meta similarity index 100% rename from Assets/Scripts/Modules/Settlement/Population/Bogatyri.Population.asmdef.meta rename to Assets/Scripts/Modules/Population/Bogatyri.Population.asmdef.meta diff --git a/Assets/Scripts/Modules/Settlement/Population/PopulationRules.cs b/Assets/Scripts/Modules/Population/PopulationRules.cs similarity index 71% rename from Assets/Scripts/Modules/Settlement/Population/PopulationRules.cs rename to Assets/Scripts/Modules/Population/PopulationRules.cs index 5bdfbe6..1201b7a 100644 --- a/Assets/Scripts/Modules/Settlement/Population/PopulationRules.cs +++ b/Assets/Scripts/Modules/Population/PopulationRules.cs @@ -1,4 +1,4 @@ -// МОДУЛЬ ПОСЕЛЕНИЯ, часть «Население» (люди города). Документ: Docs/Modules/Population.md +// МОДУЛЬ НАСЕЛЕНИЯ. Документ: Docs/Modules/Population.md // Правила населения -- только числа, без Unity. Заполняются из EconomyConfig (App/GameModules), // в тестах -- напрямую. Значения по умолчанию -- из дизайн-документа (этап 2 плана разработки). public sealed class PopulationRules @@ -22,11 +22,10 @@ public sealed class PopulationRules public int HousingPerTownHallLevel = 50; public int HousingPerHouseLevel = 50; // каждый уровень Дома (здание "Население") - // Найм воинов (решение команды от 19.10.2026): воины набираются из свободных (не работающих) жителей, - // гражданский резерв не отдаётся. Пример команды: 150 свободных, резерв 30 -> нанять можно не больше 120. - // Резерв задаёт уровень поселения: база + прибавка за уровень Ратуши. Прибавка НЕ утверждена. - public int CivilianReserve = 30; - public int CivilianReservePerTownHallLevel = 0; + // Найм воинов: воины набираются из свободных (не работающих) жителей, гражданский резерв не отдаётся. + // Резерв зависит от уровня Ратуши (регламент 31.10, ТЕСТ): уровни 1-2 -- 10, 3-5 -- 20, 6-10 -- 30. + // Элемент [i] -- резерв на уровне Ратуши i+1; выше последнего уровня -- последнее число. + public int[] CivilianReserveByTownHallLevel = { 10, 10, 20, 20, 20, 30, 30, 30, 30, 30 }; public int PeoplePerSoldier = 1; // сколько жителей стоит один воин (EconomyConfig, раздел "Солдат") // Офлайн-прогресс досчитывается не больше чем за столько часов. Число НЕ утверждено. diff --git a/Assets/Scripts/Modules/Settlement/Population/PopulationRules.cs.meta b/Assets/Scripts/Modules/Population/PopulationRules.cs.meta similarity index 100% rename from Assets/Scripts/Modules/Settlement/Population/PopulationRules.cs.meta rename to Assets/Scripts/Modules/Population/PopulationRules.cs.meta diff --git a/Assets/Scripts/Modules/Settlement/Population/PopulationService.cs b/Assets/Scripts/Modules/Population/PopulationService.cs similarity index 93% rename from Assets/Scripts/Modules/Settlement/Population/PopulationService.cs rename to Assets/Scripts/Modules/Population/PopulationService.cs index 28f0f3f..70a5f44 100644 --- a/Assets/Scripts/Modules/Settlement/Population/PopulationService.cs +++ b/Assets/Scripts/Modules/Population/PopulationService.cs @@ -3,13 +3,13 @@ using System.Collections.Generic; using System.Globalization; using System.Text; -// МОДУЛЬ ПОСЕЛЕНИЯ, часть «Население»: сколько в городе людей, кто где работает, сколько едят и как прирастают. -// Поселение -- это город: здания (Settlement/*.cs) и люди (эта папка, сборка Bogatyri.Settlement.Population). +// МОДУЛЬ НАСЕЛЕНИЯ: сколько в городе людей, кто где работает, сколько едят и как прирастают. // Документ: Docs/Modules/Population.md. Без Unity: правила те же, что будут на сервере. // // Люди города = жители (этот модуль) + воины (модуль Армии). Воин -- тоже человек: он ест (0.2 еды в час) // и входит в общее население, но хранится в Армии, чтобы один человек не был записан в двух местах. -// Жители: часть может работать (Workforce, стартовый город -- 120 из 270), из них рабочие стоят на профессиях. +// Жители: работать может любой житель (общего ограничения нет, регламент 31.10) -- рабочих ограничивают +// свободные жители и рабочие места в зданиях. Стартовый город: 120 рабочих из 270 жителей. // Найм воина: свободный житель уходит в Казармы (InTraining), после обучения его считает Армия. Резерв не нанять. // // Время -- только из GameClock. Расчёт идёт кусками по 10 минут: раз в несколько секунд в игре, @@ -60,16 +60,9 @@ public sealed class PopulationService : IPopulation, ISaveParticipant public int Idle => Math.Max(0, civilians - Workers); - // Сколько людей могут работать: та же доля от всех людей города, что в стартовом городе (120 из 300). - // Считается от всех людей, а не от жителей: найм воина из свободных жителей рабочих не снимает. - public int Workforce - { - get - { - double share = Math.Min(1.0, rules.StartWorkers / (double)Math.Max(1, rules.StartTotal)); - return Math.Min(civilians, (int)Math.Floor(Total * share + Epsilon)); - } - } + // Работать может любой житель: общего ограничения нет (регламент 31.10). + // Рабочих ограничивают свободные жители и рабочие места (IProduction.Slots). + public int Workforce => civilians; public int FreeWorkers => Math.Max(0, Workforce - Workers); @@ -93,7 +86,9 @@ public sealed class PopulationService : IPopulation, ISaveParticipant get { int level = GameServices.TryGet(out ISettlement settlement) ? Math.Max(1, settlement.TownHallLevel) : 1; - return Math.Max(0, rules.CivilianReserve + rules.CivilianReservePerTownHallLevel * (level - 1)); + int[] table = rules.CivilianReserveByTownHallLevel; + if (table == null || table.Length == 0) return 0; + return Math.Max(0, table[Math.Min(level, table.Length) - 1]); } } @@ -303,15 +298,17 @@ public sealed class PopulationService : IPopulation, ISaveParticipant return room; } - // Расставить свободных по всем профессиям поровну (старое сохранение, первый запуск после этапа 3) + // Расставить свободных по всем профессиям поровну (новая игра, старое сохранение, первый запуск после этапа 3). + // Не больше StartWorkers рабочих: стартовый город -- 120 рабочих, остальные жители свободны. public void AutoFillAll() { bool changed = false; bool progress = true; - while (FreeWorkers > 0 && progress) + int cap = Math.Max(Workers, rules.StartWorkers); + while (FreeWorkers > 0 && Workers < cap && progress) { progress = false; - for (int j = 0; j < WorkJobs.Count && FreeWorkers > 0; j++) + for (int j = 0; j < WorkJobs.Count && FreeWorkers > 0 && Workers < cap; j++) { int slots = SlotsFor((WorkJob)j); if (slots >= 0 && jobs[j] >= slots) continue; diff --git a/Assets/Scripts/Modules/Settlement/Population/PopulationService.cs.meta b/Assets/Scripts/Modules/Population/PopulationService.cs.meta similarity index 100% rename from Assets/Scripts/Modules/Settlement/Population/PopulationService.cs.meta rename to Assets/Scripts/Modules/Population/PopulationService.cs.meta diff --git a/Assets/Scripts/Modules/Tests/Bogatyri.Tests.asmdef b/Assets/Scripts/Modules/Tests/Bogatyri.Tests.asmdef index 559fcd2..f9caf85 100644 --- a/Assets/Scripts/Modules/Tests/Bogatyri.Tests.asmdef +++ b/Assets/Scripts/Modules/Tests/Bogatyri.Tests.asmdef @@ -6,7 +6,7 @@ "UnityEditor.TestRunner", "Bogatyri.Core", "Bogatyri.Army", - "Bogatyri.Settlement.Population", + "Bogatyri.Population", "Bogatyri.Economy.Rules", "Bogatyri.Battle", "Bogatyri.World.Simulation", diff --git a/Assets/Scripts/Modules/Tests/PopulationTests.cs b/Assets/Scripts/Modules/Tests/PopulationTests.cs index 1c1c655..0187108 100644 --- a/Assets/Scripts/Modules/Tests/PopulationTests.cs +++ b/Assets/Scripts/Modules/Tests/PopulationTests.cs @@ -69,9 +69,9 @@ public class PopulationTests Assert.AreEqual(300, p.Total); Assert.AreEqual(270, p.Civilians); - Assert.AreEqual(120, p.Workforce, "могут работать 120"); + Assert.AreEqual(270, p.Workforce, "работать может любой житель -- общего ограничения нет (регламент 31.10)"); Assert.AreEqual(0, p.Workers, "рабочих расставляет игрок (или CitySync по зданиям)"); - Assert.AreEqual(120, p.FreeWorkers); + Assert.AreEqual(270, p.FreeWorkers); Assert.AreEqual(30, p.Soldiers); Assert.AreEqual(33f, p.FoodPerHour, 0.001f); @@ -123,6 +123,7 @@ public class PopulationTests [Test] public void Recruit_150Free_Reserve30_Max120() { + settlement.TownHallLevel = 6; // резерв 30 -- с 6 уровня Ратуши var p = StartCity(); p.SetWorkers(WorkJob.Farmer, 120); // 120 рабочих -- свободных 150 Assert.AreEqual(150, p.Idle); @@ -149,15 +150,45 @@ public class PopulationTests Assert.AreEqual(300, p.Total, "новобранцы не посчитаны дважды"); } + [Test] + public void Reserve_DependsOnTownHallLevel_10_20_30() + { + var p = StartCity(); + int[] expected = { 10, 10, 20, 20, 20, 30, 30, 30, 30, 30, 30 }; // 11 уровень -- как последний + for (int level = 1; level <= expected.Length; level++) + { + settlement.TownHallLevel = level; + Assert.AreEqual(expected[level - 1], p.CivilianReserve, "Ратуша " + level); + } + } + + [Test] + public void Recruit_StartCity_Reserve10() + { + var p = StartCity(); + p.SetWorkers(WorkJob.Farmer, 120); + Assert.AreEqual(10, p.CivilianReserve, "Ратуша 1 -- резерв 10"); + Assert.AreEqual(140, p.Recruitable, "150 свободных - 10"); + } + [Test] public void Recruit_TakesOnlyFree_WorkersStayAtWork() { var p = StartCity(); p.SetWorkers(WorkJob.Farmer, 120); - p.TryRecruit(120); + p.TryRecruit(140); Assert.AreEqual(120, p.Workers, "рабочие остались на работе"); - Assert.AreEqual(120, p.Workforce); - Assert.AreEqual(30, p.Idle, "остался резерв"); + Assert.AreEqual(10, p.Idle, "остался резерв"); + Assert.AreEqual(0, p.Recruitable); + } + + [Test] + public void AutoFillAll_NewGame_Places120Workers_RestStayFree() + { + var p = StartCity(); + p.AutoFillAll(); // без модуля добычи мест не меньше -- ограничивает только старт: 120 рабочих + Assert.AreEqual(120, p.Workers); + Assert.AreEqual(150, p.Idle); } [Test] diff --git a/Assets/Scripts/Modules/Tests/ProductionTests.cs b/Assets/Scripts/Modules/Tests/ProductionTests.cs index 30220a2..a7b54f5 100644 --- a/Assets/Scripts/Modules/Tests/ProductionTests.cs +++ b/Assets/Scripts/Modules/Tests/ProductionTests.cs @@ -59,7 +59,7 @@ public class ProductionTests Assert.AreEqual(22.5f, production.RatePerHour(ResourceType.Stone), 0.001f); Assert.AreEqual(15f, production.RatePerHour(ResourceType.Iron), 0.001f); Assert.AreEqual(100, population.Workers); - Assert.AreEqual(20, population.FreeWorkers); + Assert.AreEqual(170, population.FreeWorkers, "работать может любой из 270 жителей"); } [Test] @@ -93,9 +93,10 @@ public class ProductionTests production.SetBuildings(Buildings((WorkJob.Woodcutter, 1))); // 30 мест Assert.AreEqual(30, population.SetWorkers(WorkJob.Woodcutter, 50), "больше мест не поставить"); - production.SetBuildings(Buildings((WorkJob.Woodcutter, 1), (WorkJob.Farmer, 1), (WorkJob.Farmer, 1), - (WorkJob.Farmer, 1), (WorkJob.Farmer, 1))); - Assert.AreEqual(90, population.SetWorkers(WorkJob.Farmer, 120), "свободных осталось 90 из 120"); + var farms = new List> { new KeyValuePair(WorkJob.Woodcutter, 1) }; + for (int i = 0; i < 9; i++) farms.Add(new KeyValuePair(WorkJob.Farmer, 1)); // 270 мест + production.SetBuildings(farms); + Assert.AreEqual(240, population.SetWorkers(WorkJob.Farmer, 270), "свободных жителей осталось 240 из 270"); Assert.AreEqual(0, population.FreeWorkers); } @@ -137,10 +138,41 @@ public class ProductionTests Assert.AreEqual(30, population.GetWorkers(WorkJob.Woodcutter)); Assert.AreEqual(30, population.GetWorkers(WorkJob.Farmer)); Assert.AreEqual(30, population.GetWorkers(WorkJob.Stonecutter)); - Assert.AreEqual(30, population.FreeWorkers); + Assert.AreEqual(180, population.FreeWorkers); Assert.IsTrue(population.JobsInitialized); } + [Test] + public void AutoFillAll_StartCity_NoMoreThan120Workers() + { + production.SetBuildings(Buildings((WorkJob.Woodcutter, 1), (WorkJob.Farmer, 1), (WorkJob.Stonecutter, 1), + (WorkJob.MinerIron, 1), (WorkJob.MinerGold, 1))); // 150 мест + population.AutoFillAll(); + Assert.AreEqual(120, population.Workers, "стартовый город: 120 рабочих"); + Assert.AreEqual(24, population.GetWorkers(WorkJob.Woodcutter), "поровну по профессиям"); + } + + // ---------------- Голод (регламент 31.10, ТЕСТ): добыча кроме еды ×0.5 ---------------- + + [Test] + public void Famine_HalvesNonFoodProduction_FoodUnaffected_PenaltyEndsWhenFed() + { + economy.Set(ResourceType.Food, 0); + production.SetBuildings(Buildings((WorkJob.Farmer, 1), (WorkJob.Woodcutter, 1))); + population.SetWorkers(WorkJob.Farmer, 5); // 10 еды в час -- город ест 33, голод + population.SetWorkers(WorkJob.Woodcutter, 30); // 60 дерева в час + + population.Tick(T0 + Hour); + Assert.IsTrue(population.IsStarving); + Assert.AreEqual(30f, production.RatePerHour(ResourceType.Wood), 0.001f, "дерево вдвое меньше"); + Assert.AreEqual(10f, production.RatePerHour(ResourceType.Food), 0.001f, "еда без штрафа"); + + economy.Set(ResourceType.Food, 1000); // привезли еду + population.Tick(T0 + 2 * Hour); + Assert.IsFalse(population.IsStarving); + Assert.AreEqual(60f, production.RatePerHour(ResourceType.Wood), 0.001f, "сыты -- штраф снят"); + } + [Test] public void AutoFill_NewBuilding_TakesFreeWorkers() { @@ -159,7 +191,7 @@ public class ProductionTests production.SetBuildings(Buildings((WorkJob.Farmer, 1))); population.ClampJobs(); Assert.AreEqual(30, population.GetWorkers(WorkJob.Farmer)); - Assert.AreEqual(90, population.FreeWorkers); + Assert.AreEqual(240, population.FreeWorkers); } [Test] diff --git a/Docs/Modules/Core.md b/Docs/Modules/Core.md index d18fbc1..3844a2d 100644 --- a/Docs/Modules/Core.md +++ b/Docs/Modules/Core.md @@ -31,7 +31,7 @@ | `IProduction` | Экономика (`ProductionService`) | Места для рабочих по профессиям, эффективность, добыча в час; `Produce` — добыть за время. | | `ISettlement` | Поселение (`SettlementModule`) | Уровень Ратуши, число построек, открыт ли город. | | `ILeveledBuilding` | Здания с уровнем (`ResourceCollector`) | Уровень здания — его сохраняет Поселение. | -| `IPopulation` | Поселение, часть «Население» (`PopulationService`) | Жители, рабочие по профессиям, воины, предел жилья, расход еды и прирост в час, голод и его история (`WasStarvingAt`), найм воинов из жителей; `Tick` — досчитать до момента. | +| `IPopulation` | Население (`PopulationService`) | Жители, рабочие по профессиям, воины, предел жилья, расход еды и прирост в час, голод и его история (`WasStarvingAt`), найм воинов из жителей; `Tick` — досчитать до момента. | | `IArmy` | Армия (`ArmyService`) | Войска по типам, в городе и в походах; обучение, потери; забрать в поход, вернуть выживших. | | `IWorld` | Мир (`MarchManager`) | Сколько походов в пути; начать поход. | | `IBattleSystem` | Бой (`BattleSystem`) | Посчитать исход боя по `BattleRequest`: состав сторон по типам, стены, бонусы. | @@ -58,7 +58,8 @@ | Дата | Кто | Что | | --- | --- | --- | -| 28.10.2026 | Claude | `IPopulation`: `WasStarvingAt` (история голода для боя), найм — `CivilianReserve`, `Recruitable`, `InTraining`, `TryRecruit`, `RecruitsTrained`. Население — часть Поселения. | +| 31.10.2026 | Claude | `IPopulation.Workforce` — все жители (ограничения «40% всех людей» больше нет), `CivilianReserve` — по уровню Ратуши. Население — самостоятельный модуль. | +| 28.10.2026 | Claude | `IPopulation`: `WasStarvingAt` (история голода для боя), найм — `CivilianReserve`, `Recruitable`, `InTraining`, `TryRecruit`, `RecruitsTrained`. | | 28.10.2026 | Claude | Бой: `BattleCondition` (голод, усталость), `BattleRequest.AttackerConditions` / `DefenderConditions`, `BattleOutcome.AttackerConditionPercent` / `DefenderConditionPercent`. | | 28.10.2026 | Claude | Бой: `BattleRequest.Seed`; `BattleOutcome` — номер боя, версия правил, удача сторон, состав до боя, выжившие, погибшие и раненые по типам у обеих сторон; `IBattleSystem.Forecast`, `BattleForecast`, `BattleChance`. | | 28.10.2026 | Claude | `IArmy.ReturnFromMarch` и `IArmy.ReturnUnits` возвращают `bool`: зачисление только по известному походу и один раз. `TakeForMarch` отклоняет повторный номер похода. | diff --git a/Docs/Modules/Economy.md b/Docs/Modules/Economy.md index 9533283..c0768e2 100644 --- a/Docs/Modules/Economy.md +++ b/Docs/Modules/Economy.md @@ -43,6 +43,7 @@ ## Добыча через рабочих (этап 3) - **Ресурс производят люди, здание даёт места и эффективность.** Добыча в час = min(рабочие, места) × норма × эффективность. +- **Голод** (регламент 31.10, ТЕСТ): пока город голодает (`IPopulation.IsStarving`), добыча всего, кроме еды, ×0.5 (`EconomyConfig`, `famineNonFoodMultiplier`). Еда добывается без штрафа, штраф снимается сразу, как только город снова сыт. - **Нормы T1 в час:** лесоруб — 2 дерева, фермер — 2 еды, каменотёс — 1.5 камня, шахтёр — 1 металла (Шахта) или 0.4 золота (Золотой рудник). - **Здание 1 уровня — 30 мест.** Каждый следующий уровень: +10 мест и +10% к норме. Не утверждено. - **Кликер убран.** `ResourceCollector` теперь здание-рабочее место (имя класса оставлено, чтобы не перенастраивать префабы). Тап по зданию — окно здания и рабочих (`App/BuildingWindow`): `−` / `+` на каждую профессию, «Свободно: N из M», кнопка «Улучшить». `ResourceProducer` (добыча по таймеру) выключен. @@ -107,6 +108,7 @@ | Дата | Кто | Что | | --- | --- | --- | +| 31.10.2026 | Claude | Голод: добыча всего, кроме еды, ×0.5 (`ProductionRules.FamineNonFoodMultiplier`, ТЕСТ). Тест `Famine_HalvesNonFoodProduction_…`. | | 27.10.2026 | Claude | Этапы 1 и 3: лимиты склада (`GetCapacity`, Склад, Амбар), «запас / лимит» и прирост на панели; добыча через рабочих (`Bogatyri.Economy.Rules`, `IProduction`), кликер убран, окно здания и рабочих, улучшение зданий. Исправлены лишние `ResourceUIText` «Люди» на ячейках панели. | | 26.10.2026 | Claude | Таблица баланса `EconomyConfig`. Стартовые ресурсы новой игры. Ресурс «Люди» отражает модуль Населения. | | 19.10.2026 | Claude | `ResetState()`: при сбросе сохранений ресурсы возвращаются к стартовым. | diff --git a/Docs/Modules/Population.md b/Docs/Modules/Population.md index 1b6733b..7974683 100644 --- a/Docs/Modules/Population.md +++ b/Docs/Modules/Population.md @@ -1,21 +1,21 @@ -# Поселение: население +# Население -Люди города: сколько их, кто работает, сколько едят, как прирастают и кого можно нанять в войско. Это часть модуля [Поселения](Settlement.md): Поселение — это город, его здания и люди. Этап 2 плана разработки. +Люди города: жители, рабочие, прирост, еда, голод, найм в войско и демографические ограничения. Самостоятельный модуль (регламент 28.10 и 31.10.2026). С Поселением (здания, Ратуша) связан только через контракт ядра `ISettlement`. Этап 2 плана разработки. -- **Ответственный:** Вальдемар. -- **Сборка:** `Bogatyri.Settlement.Population` (без Unity). Отдельная сборка внутри Поселения — как правила добычи внутри Экономики: правила людей без Unity переедут на сервер. -- **Папка:** `Assets/Scripts/Modules/Settlement/Population`. +- **Ответственный:** Валдемар. +- **Сборка:** `Bogatyri.Population` (без Unity). +- **Папка:** `Assets/Scripts/Modules/Population`. - **Зависит от:** ядра. Еду берёт через `IEconomy`, воинов считает через `IArmy`, уровень Ратуши — через `ISettlement`. ## Как устроено - **Люди города = жители + новобранцы в Казармах + воины.** Жителей и новобранцев хранит этот модуль, воинов — Армия. Так один человек не записан в двух местах. -- **Жители:** способные работать (`Workforce` — 40% всех людей города, в стартовом городе 120 из 300) и остальные. Способные работать стоят на профессиях (`WorkJob`: лесорубы, фермеры, каменотёсы, шахтёры металла и золота) или свободны (`FreeWorkers`). -- **Найм воинов** (решение команды от 19.10.2026). Воины набираются из свободных жителей (не на работе), гражданский резерв не отдаётся: 150 свободных, резерв 30 — нанять можно не больше 120 (`Recruitable`). Воин стоит `PeoplePerSoldier` жителей (1). Человек уходит из жителей в начале обучения (`TryRecruit`), учится в Казармах (`InTraining`, ест как воин) и после обучения переходит в Армию (`RecruitsTrained`). Погибшие в бою воины уменьшают население — их больше нет в Армии. Резерв задаёт уровень поселения: `CivilianReserve` + прибавка за уровень Ратуши (прибавка не утверждена, сейчас 0). +- **Жители:** работать может любой житель — общего ограничения нет (регламент 31.10). Рабочих ограничивают свободные жители и рабочие места в зданиях. Рабочие стоят на профессиях (`WorkJob`: лесорубы, фермеры, каменотёсы, шахтёры металла и золота), остальные свободны. Стартовый город: 120 рабочих из 270 жителей — при первой расстановке (`AutoFillAll`) больше `StartWorkers` (120) не ставится. +- **Найм воинов** (решение команды от 19.10.2026). Воины набираются из свободных жителей (не на работе), гражданский резерв не отдаётся (`Recruitable` = свободные − резерв). Пример команды: 150 свободных, резерв 30 — нанять можно не больше 120. Воин стоит `PeoplePerSoldier` жителей (1). Человек уходит из жителей в начале обучения (`TryRecruit`), учится в Казармах (`InTraining`, ест как воин) и после обучения переходит в Армию (`RecruitsTrained`). Погибшие в бою воины уменьшают население — их больше нет в Армии. Резерв зависит от уровня Ратуши (регламент 31.10, ТЕСТ): уровни 1–2 — 10, 3–5 — 20, 6–10 — 30 (`EconomyConfig`, `civilianReserveByTownHallLevel`). - **Рабочих ставят** окно здания (`SetWorkers`) и `App/CitySync` (новые места заполняются свободными сами). Больше, чем мест в зданиях (`IProduction.Slots`) и свободных, не поставить. - **Еда в час:** 0.1 на жителя, 0.2 на воина. Стартовый город (270 жителей + 30 воинов) ест 33 еды в час. - **Прирост:** +2.5 человека в час, только если еды хватило и население ниже предела жилья. -- **Голод:** еды не хватило — остатки доедаются, прирост останавливается, `IsStarving = true`. Бой ослабляет голодное войско (модуль Боя, −50%). +- **Голод:** еды не хватило — остатки доедаются, прирост останавливается, `IsStarving = true`. Добыча всего, кроме еды, ×0.5 (модуль Экономики, регламент 31.10, ТЕСТ), еда добывается без штрафа. Бой ослабляет голодное войско (модуль Боя, −50%). Штраф снимается сразу, как только город снова сыт. Смерти от голода нет. - **История голода:** модуль помнит отрезки голода за последние `MaxOfflineHours` (48 ч) с точностью до шага расчёта (10 минут). Нужна Бою: поход мог дойти до цели, пока игра была закрыта, и голод берётся на момент боя (`WasStarvingAt`), а не на момент входа в игру. - **Предел жилья:** `HousingBase + HousingPerTownHallLevel × (уровень Ратуши − 1) + HousingPerHouseLevel × уровни Домов` = 300 + 50 + 50. По решению команды от 19.10 стартовый город (300) упирается в предел: прирост пойдёт после постройки Дома. Прибавки не утверждены. - **Добыча** идёт внутри расчёта населения: в каждом куске сначала `IProduction.Produce`, потом еда, потом прирост. Иначе офлайн город «голодал» бы, хотя фермы работали. @@ -91,7 +91,7 @@ - сохранение; - стартовый набор — один раз; - сброс; -- найм: 150 свободных, резерв 30 → не больше 120; людей столько же, новобранцы не считаются дважды; рабочие остаются на работе; новобранцы едят как воины и сохраняются; старый заказ обучения не уводит в минус; +- найм: 150 свободных, резерв 30 (Ратуша 6) → не больше 120; стартовый город — резерв 10; резерв по уровням Ратуши 10/20/30; стартовая расстановка — 120 рабочих; людей столько же, новобранцы не считаются дважды; рабочие остаются на работе; новобранцы едят как воины и сохраняются; старый заказ обучения не уводит в минус; - история голода: бой офлайн видит голод на свой момент, история сохраняется, сбрасывается и забывается через 48 часов. ## Решения и план @@ -103,12 +103,14 @@ - п. 7, история голода -- сделано (`WasStarvingAt`). Бою нужно перейти с `IsStarving` на `WasStarvingAt(время боя)`. Походы идут по реальному времени, Население -- по `GameClock`: после перемотки в DEV-панели они расходятся. - п. 8, норма еды на солдата -- сейчас 0.2 еды в час реального времени, игрового календаря нет. Предложение: не вводить; если нужен «игровой месяц» -- 1 месяц = 1 сутки, норма ≈ 5 еды. Решает Дима. - **Этап 5:** найм воина забирает человека из жителей (гражданский резерв — решение от 19.10.2026) — сделано 28.10.2026. Цену воина в ресурсах (`EconomyConfig`, раздел «Солдат»: 15 еды, 5 металла, 5 золота) Казармы пока не используют — у них своя цена в `GameData.Troops`. -- **Схема модулей (28.10.2026):** население — часть Поселения, как на схеме Димы из 8 модулей. +- **Схема модулей:** Население — самостоятельный модуль (регламент 28.10 и 31.10.2026). Перенос внутрь Поселения от 28.10 был ошибкой и откатан 31.10. +- **Открыто (регламент 31.10, ждёт решения):** нормы еды для четырёх групп (гражданские и рабочие, новобранцы, солдаты, раненые); переход экономики на игровое время (1 игровой час = 5 реальных минут) и единицы офлайна (48 ч) и истории голода; кто считает предел жилья и рабочие места (по регламенту — Поселение); недоступен ли резерв и для работы; перенос очереди найма из `GameProgress` в Армию (с Сашей). ## Журнал изменений | Дата | Кто | Что | | --- | --- | --- | +| 31.10.2026 | Claude | По регламенту 31.10: Население снова самостоятельный модуль (`Modules/Population`, сборка `Bogatyri.Population`); убрано ограничение «40% всех людей» — работать может любой житель, первая расстановка — 120 рабочих; резерв по уровню Ратуши 10/20/30 (ТЕСТ, `civilianReserveByTownHallLevel` вместо `civilianReserve`); при голоде добыча кроме еды ×0.5 (ТЕСТ). Тесты. | | 28.10.2026 | Claude | Население стало частью Поселения: папка `Modules/Settlement/Population`, сборка `Bogatyri.Settlement.Population`. Найм воинов из свободных жителей с гражданским резервом (`CivilianReserve`, `Recruitable`, `InTraining`, `TryRecruit`, `RecruitsTrained`), строка `training=`, `Workforce` — 40% всех людей. Тесты. | | 28.10.2026 | Claude | История голода: `IPopulation.WasStarvingAt`, строка `famine=` в сохранении, тесты (вопрос Саши п. 7). | | 27.10.2026 | Claude | Рабочие по профессиям (`Workforce`, `FreeWorkers`, `GetWorkers`, `SetWorkers`, автозаполнение), Дома в пределе жилья, базовый предел 300 (решение 19.10), добыча в общем расчёте. | diff --git a/Docs/Modules/README.md b/Docs/Modules/README.md index ac48de7..3c8f9f5 100644 --- a/Docs/Modules/README.md +++ b/Docs/Modules/README.md @@ -1,6 +1,6 @@ # Модульная система «Богатырей» -Игра собрана из восьми модулей вокруг общего **Игрового ядра** — как на схеме Димы. Модули общаются только через контракты и события ядра и не управляют внутренним кодом друг друга. Это правило проверяет сам Unity: каждый модуль — отдельная сборка (`.asmdef`), которая видит только ядро, поэтому прямое обращение к соседнему модулю не скомпилируется. +Игра собрана из десяти модулей вокруг общего **Игрового ядра** (регламент 31.10.2026; модуль Разведки — в ветке Саши `feature/intelligence-m1`, на схеме появится с ней). Модули общаются только через контракты и события ядра и не управляют внутренним кодом друг друга. Это правило проверяет сам Unity: каждый модуль — отдельная сборка (`.asmdef`), которая видит только ядро, поэтому прямое обращение к соседнему модулю не скомпилируется. ``` ┌─────────────────────────────────────────┐ @@ -11,28 +11,32 @@ ┌──────────────────────┐ ┌──────────────────────┐ │ ЭКОНОМИКА │ │ ПОСЕЛЕНИЕ │ -│ ресурсы, склады, │ │ здания: Ратуша, │ -│ лимиты, добыча │ │ стройка; │ -│ рабочими │ │ люди: жители, еда, │ -│ │ │ голод, прирост, найм │ +│ ресурсы, склады, │ │ здания, Ратуша, │ +│ лимиты, добыча │ │ участки, стройка │ +│ рабочими │ │ │ └──────────────────────┘ └──────────────────────┘ ┌──────────────────────┐ ┌──────────────────────┐ -│ АРМИЯ │ │ ГЛОБАЛЬНЫЙ МИР │ -│ воины по типам: дома,│ │ карта, походы, замок,│ -│ в гарнизоне, │ │ соседи, лагеря │ -│ в походе │ │ разбойников │ +│ НАСЕЛЕНИЕ │ │ АРМИЯ │ +│ люди, рабочие, еда, │ │ воины по типам: дома,│ +│ голод, прирост, │ │ в гарнизоне, │ +│ жильё │ │ в походе │ └──────────────────────┘ └──────────────────────┘ ┌──────────────────────┐ ┌──────────────────────┐ -│ БОЕВАЯ СИСТЕМА │ │ КРУГ │ -│ сила, контры, стены, │ │ объединение игроков, │ -│ удача ±5%, голод, │ │ роли │ -│ прогноз │ │ │ +│ ГЛОБАЛЬНЫЙ МИР │ │ БОЕВАЯ СИСТЕМА │ +│ карта, походы, замок,│ │ сила, контры, стены, │ +│ соседи, лагеря │ │ удача ±5%, голод, │ +│ разбойников │ │ прогноз │ └──────────────────────┘ └──────────────────────┘ ┌──────────────────────┐ ┌──────────────────────┐ -│ ЧАТ │ │ СОХРАНЕНИЯ │ -│ общий мир и чат │ │ save.json, у каждого │ -│ Круга │ │ модуля своя секция │ +│ КРУГ │ │ ЧАТ │ +│ объединение игроков, │ │ общий мир и чат │ +│ роли │ │ Круга │ └──────────────────────┘ └──────────────────────┘ +┌──────────────────────┐ +│ СОХРАНЕНИЯ │ +│ save.json, у каждого │ +│ модуля своя секция │ +└──────────────────────┘ App/GameModules — точка сборки: регистрирует модули и соединяет их события со старыми системами (GameProgress, Казармы, панели UI) @@ -40,7 +44,7 @@ App/GameModules — точка сборки: регистрирует модул **Не путать:** -- **Поселение** — это **город**: его **здания** (что построено и какого уровня, `Modules/Settlement`) и его **люди** (сколько их, кто работает, сколько едят, кого нанять в войско, `Modules/Settlement/Population`). Люди — отдельная сборка без Unity внутри Поселения, как правила добычи внутри Экономики. Связь: уровень Ратуши задаёт предел жилья. +- **Поселение** — это **здания**: что построено и какого уровня. **Население** — это **люди**: сколько их, кто работает, сколько едят. Связь: уровень Ратуши (Поселение) задаёт предел жилья (Население). - **Армия** хранит, где стоит каждый воин. **Боевая система** только считает бой: кто победил и кто погиб. - **Глобальный мир** решает, куда и когда идёт войско; бой он просит у Боевой системы. - **Производство** (`Bogatyri.Economy.Rules`) — не отдельный модуль, а часть Экономики без Unity: добыча через рабочих. @@ -49,15 +53,16 @@ App/GameModules — точка сборки: регистрирует модул | Кто | Модули | | --- | --- | -| Валдемар (экономика) | Экономика, люди Поселения (`Settlement/Population`) | +| Валдемар (экономика) | Экономика, Население | | Саша (война и карта) | Армия, Боевая система, Глобальный мир, Круг, Чат | -| Общее | Ядро, здания Поселения, Сохранения | +| Общее | Ядро, Поселение, Сохранения | | Модуль | Сборка | Папка | Документ | | --- | --- | --- | --- | | Игровое ядро | `Bogatyri.Core` (без Unity), `Bogatyri.Core.Unity` | `Assets/Scripts/Modules/Core`, `Core.Unity` | [Core.md](Core.md) | | Экономика | `Bogatyri.Economy`, `Bogatyri.Economy.Rules` (без Unity) | `Assets/Scripts/Modules/Economy` | [Economy.md](Economy.md) | -| Поселение | `Bogatyri.Settlement` (здания), `Bogatyri.Settlement.Population` (люди, без Unity) | `Assets/Scripts/Modules/Settlement`, люди — `Settlement/Population` | [Settlement.md](Settlement.md), [Population.md](Population.md) | +| Поселение | `Bogatyri.Settlement` | `Assets/Scripts/Modules/Settlement` | [Settlement.md](Settlement.md) | +| Население | `Bogatyri.Population` (без Unity) | `Assets/Scripts/Modules/Population` | [Population.md](Population.md) | | Армия | `Bogatyri.Army` (без Unity) | `Assets/Scripts/Modules/Army` | [Army.md](Army.md) | | Глобальный мир | `Bogatyri.World`, `Bogatyri.World.Simulation` (без Unity) | `Assets/Scripts/Modules/World` | [World.md](World.md) | | Боевая система | `Bogatyri.Battle` (без Unity) | `Assets/Scripts/Modules/Battle` | [Battle.md](Battle.md) | @@ -90,15 +95,16 @@ EditMode-тесты: `Assets/Scripts/Modules/Tests/ModuleTests.cs`. Запуск Добавить вместе с этапами 1, 2 и 5 (решение команды от 19.10.2026): - [x] лимит жилья: 300 жителей при лимите 300 — прирост стоит, предел вырос — прирост идёт (`PopulationTests`; пока предел растёт от Ратуши, Дома ещё нет); -- [x] нельзя призвать ниже гражданского резерва: 150 свободных, резерв 30 → призвать можно не больше 120 (`PopulationTests`); +- [x] нельзя призвать ниже гражданского резерва: 150 свободных, резерв 30 → призвать можно не больше 120; резерв по уровню Ратуши 10/20/30 (`PopulationTests`); - [x] назначение гарнизона: 30 солдат, 10 в гарнизон → 20 доступны, всего по-прежнему 30 (`ArmyService_Garrison_OneSoldierInOnePlace`); -- [ ] голод и восстановление: еда кончилась → дебаффы, в том числе у защитников; еда вернулась → снятие по правилу. Бой уже проверен (`Battle_Famine_HalvesPower_AndStacksWithFatigue`, `World_FamineAtBattleTime_WeakensMarch`: −50% у нападающих и у защитников, вместе с усталостью 40%), история голода на момент боя — `PopulationTests`; остаются дебаффы производства и правило снятия — экономика; +- [ ] голод и восстановление: еда кончилась → дебаффы, в том числе у защитников; еда вернулась → снятие по правилу. Бой уже проверен (`Battle_Famine_HalvesPower_AndStacksWithFatigue`, `World_FamineAtBattleTime_WeakensMarch`: −50% у нападающих и у защитников, вместе с усталостью 40%), история голода на момент боя — `PopulationTests`; добыча кроме еды при голоде ×0.5 и снятие штрафа сразу после еды — `ProductionTests` (31.10); - [ ] предел синих кристаллов: кристаллы не копятся бесконечно, сверх лимита — по общему правилу. ## Журнал изменений | Дата | Кто | Что | | --- | --- | --- | +| 31.10.2026 | Claude | По регламенту 31.10: откат — Население снова самостоятельный модуль (`Modules/Population`, `Bogatyri.Population`), схема и «Кто отвечает» как у Саши. Работать может любой житель (без «40%»), резерв по уровню Ратуши 10/20/30, голод режет добычу кроме еды вдвое. Обновлены Population, Settlement, Economy, Core. | | 28.10.2026 | Claude | Население стало частью Поселения, как на схеме Димы: `Settlement/Population`, сборка `Bogatyri.Settlement.Population`. Найм воинов из свободных жителей с гражданским резервом, история голода для боя. Обновлены Settlement, Population, Core. | | 28.10.2026 | Claude | Схема модулей обновлена: десять модулей (добавлено Население), «Не путать» (Поселение и Население, Армия и Боевая система), кто за что отвечает. | | 28.10.2026 | Claude | Голод в бою (−50%) и правило усталости (−20%, вместе 40%). Обновлены Battle, World, Core. | diff --git a/Docs/Modules/Settlement.md b/Docs/Modules/Settlement.md index a44f2f9..35cbda2 100644 --- a/Docs/Modules/Settlement.md +++ b/Docs/Modules/Settlement.md @@ -1,12 +1,10 @@ # Поселение -Город: его здания и люди. +Здания и развитие города: Ратуша, участки, свободная стройка на сетке, постройка зданий, восстановление города из сохранения. Люди города — отдельный модуль [Население](Population.md), связь только через контракты ядра. -- **Здания** (этот документ): Ратуша, свободная стройка на сетке, постройка зданий, восстановление города из сохранения. -- **Люди** — жители, рабочие, еда, голод, прирост, жильё, найм воинов: [Поселение: население](Population.md). - -- **Сборки:** `Bogatyri.Settlement` (здания, Unity), `Bogatyri.Settlement.Population` (люди, без Unity). -- **Папка:** `Assets/Scripts/Modules/Settlement`, люди — `Settlement/Population`. +- **Ответственность:** совместная. +- **Сборка:** `Bogatyri.Settlement`. +- **Папка:** `Assets/Scripts/Modules/Settlement`. - **Зависит от:** ядра. Ресурсы на стройку берёт через `IEconomy`. ## Стройка на сетке @@ -84,6 +82,7 @@ | Дата | Кто | Что | | --- | --- | --- | +| 31.10.2026 | Claude | Откат: Население снова самостоятельный модуль (`Modules/Population`), из Поселения убрано. | | 28.10.2026 | Claude | Население стало частью Поселения (`Settlement/Population`, сборка `Bogatyri.Settlement.Population`) — как на схеме Димы. Подробности — [Population.md](Population.md). | | 27.10.2026 | Claude | Свободная стройка на сетке: `BuildGrid`, `BuildGhost`, `BuildingData.footprint`, `BuildManager.BuildAt` / `CanBuildAt` / `FindData`. Участки и площадки «Застройка» прячутся. Новый `BuildModeController` (призрак, «Поставить / Другое / Отмена»), камера не двигается, пока тащат призрак. | | 19.10.2026 | Claude | `ResetState()` для сброса сохранений. Убранные здания помечаются, чтобы запись в том же кадре их не сохранила. |