Files
unity/Assets/Game/Scripts/Save/ProfileStore.cs
T
RCiBot 92623a1bc8 feat: establish mech extraction prototype with bilingual UI
Track Unity source, scenes, editable assets, licenses, tools and QA evidence. Exclude generated caches, Windows builds and raw runtime logs.
2026-11-12 12:28:40 +08:00

230 lines
7.5 KiB
C#

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<CargoStack> cargo = new List<CargoStack>();
public List<string> searched = new List<string>();
}
[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<string> blueprints = new List<string>();
public List<string> unlocked = new List<string>();
public List<string> receipts = new List<string>();
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<GameSave>(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<GameSave, bool> change)
{
var candidate = JsonUtility.FromJson<GameSave>(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<string> selection)
{
if (selection == null || selection.Count == 0 || selection.Count > 2)
return false;
var ids = new HashSet<string>(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;
});
}
}
}