using System.Collections.Generic; using System.Text; using UnityEngine; // Слой приложения: сообщает модулям, какие здания стоят в городе. // - рабочие места (Лес, Еда, Камень, Металл, Золото) и их уровни -> модуль добычи (места и эффективность); // - Дома (здание "Население") -> модуль Населения (предел жилья); // - Склады и Амбары -> лимиты склада в Экономике. // Модули сами здания не ищут: они без Unity и живут и на карте мира, где города нет. // // Проверяет город раз в полсекунды и применяет изменения, только когда картина устоялась // (два одинаковых замера подряд) -- чтобы не поймать город посреди восстановления из сохранения. public class CitySync : MonoBehaviour { const float Interval = 0.5f; public static CitySync Instance { get; private set; } float next; string pending; // замер, который ждёт подтверждения string applied; // последний применённый замер [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] static void Create() { if (Instance != null) return; var go = new GameObject("CitySync"); DontDestroyOnLoad(go); Instance = go.AddComponent(); } void OnDestroy() { if (Instance == this) Instance = null; } void Update() { if (Time.unscaledTime < next) return; next = Time.unscaledTime + Interval; if (BuildManager.Instance == null) return; // город не открыт -- модули помнят последнее var city = Scan(); if (city.Signature == applied) { pending = null; return; } if (city.Signature != pending) { pending = city.Signature; return; } // ждём подтверждения Apply(city); } // Применить сразу (после улучшения здания в окне -- чтобы окно показало новые места) public void SyncNow() { if (BuildManager.Instance == null) return; Apply(Scan()); } class City { public readonly List> Workplaces = new List>(); public int HouseLevels, WarehouseLevels, GranaryLevels; public string Signature; } static City Scan() { var city = new City(); var sig = new List(); foreach (var placed in FindObjectsByType(FindObjectsSortMode.None)) { if (string.IsNullOrEmpty(placed.buildingName)) continue; // убирается в этом кадре var rc = placed.GetComponent(); if (rc != null) { if (WorkJobs.TryFromResource(rc.resourceType, out WorkJob job)) { city.Workplaces.Add(new KeyValuePair(job, rc.level)); sig.Add("w" + (int)job + ":" + rc.level); } else if (rc.IsHouse) { city.HouseLevels += Mathf.Max(1, rc.level); sig.Add("h:" + rc.level); } } var sb = placed.GetComponent(); if (sb != null) { if (sb.kind == StorageBuilding.Kind.Warehouse) city.WarehouseLevels += Mathf.Max(1, sb.level); else city.GranaryLevels += Mathf.Max(1, sb.level); sig.Add("s" + (int)sb.kind + ":" + sb.level); } } sig.Sort(System.StringComparer.Ordinal); var text = new StringBuilder(); foreach (var s in sig) text.Append(s).Append(';'); city.Signature = text.ToString(); return city; } void Apply(City city) { applied = city.Signature; pending = null; var config = EconomyConfig.Load(); // Лимиты склада var rm = ResourceManager.Instance; if (rm != null) { ResourceType[] stored = { ResourceType.Wood, ResourceType.Stone, ResourceType.Iron, ResourceType.Gold, ResourceType.Food, ResourceType.Gems }; foreach (var t in stored) rm.SetCapacity(t, config.GetCapacity(t, city.WarehouseLevels, city.GranaryLevels)); } var production = GameModules.Production; var population = GameModules.Population; if (production == null || population == null) return; // Места для рабочих: где мест стало больше (построили, улучшили) -- ставим туда свободных var before = new int[WorkJobs.Count]; for (int j = 0; j < WorkJobs.Count; j++) before[j] = production.Slots((WorkJob)j); production.SetBuildings(city.Workplaces); population.SetHouseLevels(city.HouseLevels); population.ClampJobs(); if (!population.JobsInitialized) { population.AutoFillAll(); // первый запуск после этапа 3 или новая игра } else { for (int j = 0; j < WorkJobs.Count; j++) if (production.Slots((WorkJob)j) > before[j]) population.AutoFill((WorkJob)j); } } }