using System; using System.Collections.Generic; using UnityEngine; // Сохранение 3D-поселения: секция "settlement3d" (05.11, Валдемар). 2D-город сохраняется отдельно ("settlement"), // поэтому 3D-сцена его не трогает. Здания: id, тип, клетка, поворот, уровень, стройка или улучшение, которое идёт; // Ратуша: уровень и её улучшение; расчищенные деревья и валуны. // Регистрирует App/GameModules. Пока сцена City3D открыта, данные берутся у неё (Live), иначе -- последние известные. public sealed class City3DSave : ISaveParticipant { [Serializable] public class Entry { public string id; public string type; // тип 3D-сцены: "Ратуша", "Дом", "Ферма"... public int x, y, rot; public int level = 1; public int timerKind; // 0 -- нет, 1 -- постройка, 2 -- улучшение (BuildTimer.Kind) public long timerStartMs, timerEndMs; public int timerTargetLevel; } [Serializable] public class Data { public int nextId = 1; public List buildings = new List(); public List cleared = new List(); // Здания 2D-города, которым при переносе не нашлось места (зона закрыта, всё занято): ждут и ставятся, // как только место появится. Не теряются (05.11) public List pendingFrom2D = new List(); } public interface ILive { Data CaptureState(); void OnSaveLoaded(); // загрузили другое сохранение или сбросили -- сцена должна перестроиться } public static readonly City3DSave Instance = new City3DSave(); public static Data Cached { get; private set; } // Сохранение уже прочитано (или новая игра): по Cached можно считать здания вне 3D-сцены (Кристальная шахта на карте мира) public static bool Loaded { get; private set; } public static ILive Live; public string SaveKey => "settlement3d"; public string Capture() { if (Live != null) Cached = Live.CaptureState(); return Cached != null ? JsonUtility.ToJson(Cached) : null; } public void Restore(string json) { Loaded = true; Cached = string.IsNullOrEmpty(json) ? null : JsonUtility.FromJson(json); if (Cached != null) { if (Cached.buildings == null) Cached.buildings = new List(); if (Cached.cleared == null) Cached.cleared = new List(); if (Cached.pendingFrom2D == null) Cached.pendingFrom2D = new List(); } Live?.OnSaveLoaded(); } public void ResetState() { Loaded = true; Cached = null; Live?.OnSaveLoaded(); } // Запись таймера здания в Entry и обратно public static void WriteTimer(Entry e, GameObject view) { var t = BuildTimer.Of(view); bool on = t != null && BuildTimer.All.Contains(t); e.timerKind = on ? (int)t.kind : 0; e.timerStartMs = on ? t.startMs : 0; e.timerEndMs = on ? t.endMs : 0; e.timerTargetLevel = on ? t.targetLevel : 0; } // Таймер из файла -- только осмысленный: постройка у 1 уровня, улучшение до уровня выше текущего public static void ReadTimer(Entry e, GameObject view) { bool build = e.timerKind == (int)BuildTimer.Kind.Build && e.level <= 1; bool upgrade = e.timerKind == (int)BuildTimer.Kind.Upgrade && e.timerTargetLevel > Mathf.Max(1, e.level); if ((build || upgrade) && e.timerEndMs > 0) BuildTimer.Begin(view, (BuildTimer.Kind)e.timerKind, Mathf.Max(1, e.timerTargetLevel), e.timerStartMs, e.timerEndMs); } }