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 CargoStack(string id, int amount) { itemId = id; count = amount; } public string 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 searched = new List(); } [Serializable] public sealed class GameSave { public int version = 1; 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 class ProfileStore { readonly string path; public GameSave Data { get; private set; } public string Error { get; private set; } public ProfileStore(string savePath) { path = savePath; Data = Load(path) ?? Load(path + ".bak") ?? new GameSave(); } static GameSave Load(string file) { try { if (!File.Exists(file)) return null; var save = JsonUtility.FromJson(File.ReadAllText(file)); if (save == null || save.version != 1 || save.run == null || save.receipts == null || save.blueprints == null || save.unlocked == null) return null; if (save.run.cargo == null || save.run.equipment == null || save.run.equipment.Length != 4) return null; return save; } catch (Exception) { return null; } } public bool Commit(Func change) { var candidate = JsonUtility.FromJson(JsonUtility.ToJson(Data)); if (!change(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, 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) { return Commit(s => { s.run = new RunRecord { id = Guid.NewGuid().ToString("N"), active = true, seed = seed, chassis = chassis, equipment = (int[])equipment.Clone(), armor = armor }; s.sorties++; return true; }); } public bool AddCargo(string id, int count, string sourceKey = "") { if (count <= 0) return false; return Commit(s => { if (!s.run.active) return false; if (sourceKey.Length > 0 && s.run.searched.Contains(sourceKey)) return false; foreach (var stack in s.run.cargo) { if (id.StartsWith("blueprint_") || stack.itemId != id || stack.count + count > 10) continue; stack.count += count; if (sourceKey.Length > 0) s.run.searched.Add(sourceKey); return true; } if (s.run.cargo.Count >= 4 || (id.StartsWith("blueprint_") ? count != 1 : count > 10)) return false; s.run.cargo.Add(new CargoStack(id, count)); if (sourceKey.Length > 0) s.run.searched.Add(sourceKey); return true; }); } static void Bank(GameSave s, CargoStack stack) { if (stack.itemId == "alloy") s.alloy += stack.count; else if (stack.itemId == "cells") s.cells += stack.count; else if (!s.blueprints.Contains(stack.itemId)) s.blueprints.Add(stack.itemId); else s.alloy += 2; } public bool SendCargo(IReadOnlyCollection selection) { if (selection == null || selection.Count == 0 || selection.Count > 2) return false; var ids = new HashSet(selection); if (ids.Count != selection.Count) return false; return Commit(s => { string receipt = s.run.id + ":cargo"; if (!s.run.active || s.run.armor <= 0 || s.run.podUsed || s.receipts.Contains(receipt)) return false; var picked = s.run.cargo.FindAll(x => ids.Contains(x.uid)); if (picked.Count != ids.Count) return false; foreach (var stack in picked) { Bank(s, stack); s.run.cargo.Remove(stack); } s.run.podUsed = true; s.receipts.Add(receipt); return true; }); } public bool Finish(bool destroyed) { return Commit(s => { string receipt = s.run.id + ":return"; if (!s.run.active || s.receipts.Contains(receipt)) return false; if (!destroyed) { foreach (var stack in s.run.cargo) Bank(s, stack); s.extractions++; } s.run.cargo.Clear(); s.run.active = false; s.receipts.Add(receipt); 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; }); } } }