133 lines
6.0 KiB
C#
133 lines
6.0 KiB
C#
using System.Collections.Generic;
|
|
using System.Text;
|
|
using UnityEngine;
|
|
|
|
// Слой приложения: сообщает модулям, какие здания стоят в городе.
|
|
// - рабочие места (Лес, Еда, Камень, Металл, Золото) и их уровни -> модуль добычи (места и эффективность);
|
|
// - Дома (здание "Население") -> модуль Населения (предел жилья);
|
|
// - Склады и Амбары -> лимиты склада в Экономике и защита ресурсов от грабежа (App/ResourceProtection).
|
|
// Здания, которые ещё строятся (BuildTimer), не считаются: они пока не работают.
|
|
// Модули сами здания не ищут: они без 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<CitySync>();
|
|
}
|
|
|
|
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<KeyValuePair<WorkJob, int>> Workplaces = new List<KeyValuePair<WorkJob, int>>();
|
|
public int HouseLevels, WarehouseLevels, GranaryLevels;
|
|
public string Signature;
|
|
}
|
|
|
|
static City Scan()
|
|
{
|
|
var city = new City();
|
|
var sig = new List<string>();
|
|
foreach (var placed in FindObjectsByType<PlacedBuildingId>(FindObjectsSortMode.None))
|
|
{
|
|
if (string.IsNullOrEmpty(placed.buildingName)) continue; // убирается в этом кадре
|
|
if (BuildTimer.IsConstructing(placed)) continue; // ещё строится -- не работает
|
|
|
|
var rc = placed.GetComponent<ResourceCollector>();
|
|
if (rc != null)
|
|
{
|
|
if (WorkJobs.TryFromResource(rc.resourceType, out WorkJob job))
|
|
{
|
|
city.Workplaces.Add(new KeyValuePair<WorkJob, int>(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<StorageBuilding>();
|
|
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));
|
|
}
|
|
ResourceProtection.SetLevels(city.WarehouseLevels, city.GranaryLevels); // защита от грабежа
|
|
|
|
var production = GameModules.Production;
|
|
var population = GameModules.Population;
|
|
if (production == null || population == null) return;
|
|
|
|
// Места для рабочих. Новые места (построили, улучшили) остаются пустыми, пока игрок сам
|
|
// не поставит рабочих (регламент 31.10). Мест стало меньше -- лишних рабочих снимаем.
|
|
production.SetBuildings(city.Workplaces);
|
|
population.SetHouseLevels(city.HouseLevels);
|
|
population.ClampJobs();
|
|
|
|
// Единственное автозаполнение -- стартовая расстановка, один раз: новая игра или первая загрузка старого сохранения
|
|
if (!population.JobsInitialized) population.AutoFillAll();
|
|
}
|
|
}
|