using System; using System.Collections.Generic; using System.IO; using UnityEngine; namespace Ironspore { [Serializable] public sealed class CargoStack { public string uid = Guid.NewGuid().ToString("N"); public string itemId; public int count; public int x, y; public bool rotated, borrowed; public CargoStack(string id, int amount) { itemId = id; count = amount; } public string Name => InventoryGrid.Slot(itemId) >= 0 ? GameLocalization.Text("equipment." + itemId + ".name") : GameLocalization.ItemName(itemId); } [Serializable] public sealed class RunRecord { public string id; public bool active; public int seed; public int stage; public int branch; public int biome; public int chassis = 1; public int[] equipment = { 0, 0, 0, 0 }; public float armor; public float energy = 100; public bool podUsed; public bool objectiveDone; public bool routeCompleted; public int upgrades; public float elapsed; public float escortArmor = 250; public List cargo = new List(); public List safeCase = new List(); public List installed = new List(); public bool destructionPending; public List searched = new List(); } [Serializable] public sealed class GameSave { public const int CurrentVersion = 2; public int version = CurrentVersion; public List warehouse = new List(); public int alloy; public int cells; public int sorties; public int extractions; public int completed; public List blueprints = new List(); public List unlocked = new List(); public List receipts = new List(); public RunRecord run = new RunRecord(); } public sealed partial class ProfileStore { readonly string path; public GameSave Data { get; private set; } public string Error { get; private set; } public static string SavePath(bool trial) { #if UNITY_EDITOR string root = Path.Combine(Path.GetDirectoryName(Application.dataPath), "UserSettings"); #else string root = Application.persistentDataPath; #endif return Path.Combine(root, "Ironspore", trial ? "range-v2.json" : "profile-v2.json"); } public ProfileStore(string savePath) { path = savePath; Data = Load(path) ?? Load(path + ".bak"); if (Data == null) { if (File.Exists(path) || File.Exists(path + ".bak")) throw new InvalidDataException("No valid equipment snapshot: " + path); Data = new GameSave(); for (int slot = 0; slot < 4; slot++) Data.warehouse.Add(new CargoStack(InventoryGrid.EquipmentId(slot, 0), 1)); } } static GameSave Load(string file) { try { if (!File.Exists(file)) return null; var save = JsonUtility.FromJson(File.ReadAllText(file)); if (save != null && save.version != GameSave.CurrentVersion) throw new NotSupportedException("Unsupported equipment save version: " + save.version); if (!Valid(save)) return null; return save; } catch (ArgumentException) { return null; } } public bool Commit(Func change) { if (Data.run.destructionPending) return false; return Write(change); } bool Write(Func change) { Error = null; var candidate = JsonUtility.FromJson(JsonUtility.ToJson(Data)); if (!change(candidate) || !Valid(candidate)) return false; try { Directory.CreateDirectory(Path.GetDirectoryName(path)); string temp = path + ".tmp"; using (var stream = new FileStream(temp, FileMode.Create, FileAccess.Write, FileShare.None)) { byte[] bytes = System.Text.Encoding.UTF8.GetBytes(JsonUtility.ToJson(candidate, true)); stream.Write(bytes, 0, bytes.Length); stream.Flush(true); } if (File.Exists(path)) File.Replace(temp, path, Load(path) == null ? null : path + ".bak"); else File.Move(temp, path); Data = candidate; Error = null; return true; } catch (Exception ex) { Error = ex.Message; return false; } } public bool Begin(int chassis, int[] equipment, float armor, int seed, bool trial = false, string[] selected = null) { return Commit(s => { if ((selected != null && selected.Length != 4) || s.run.active || equipment == null || equipment.Length != 4 || armor <= 0) return false; s.run = new RunRecord { id = Guid.NewGuid().ToString("N"), active = true, seed = seed, chassis = chassis, equipment = (int[])equipment.Clone(), armor = armor }; for (int slot = 0; slot < 4; slot++) { string id = InventoryGrid.EquipmentId(slot, equipment[slot]); if (id == null) return false; var requested = selected?[slot]; var item = s.warehouse.Find(part => part.itemId == id && (requested == null || requested == part.uid)); if (item == null) { if (requested != null || (!trial && equipment[slot] != 0)) return false; item = new CargoStack(id, 1) { borrowed = true }; } else s.warehouse.Remove(item); s.run.installed.Add(item); } s.sorties++; return true; }); } public bool CompleteRoute() { return Commit(s => { if (!s.run.active || s.run.armor <= 0 || s.run.routeCompleted) return false; s.run.routeCompleted = true; s.run.objectiveDone = true; s.completed++; return true; }); } } }