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.
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user