138 lines
5.6 KiB
C#
138 lines
5.6 KiB
C#
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<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; // убирается в этом кадре
|
|
|
|
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));
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|