feat(equipment): persist mech loadouts and recover ejection safe case loot

This commit is contained in:
2026-11-12 18:02:26 +08:00
parent 0857e291d5
commit e1d38c5a6d
23 changed files with 1371 additions and 204 deletions
+61 -35
View File
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.InputSystem;
@@ -53,6 +52,7 @@ namespace Ironspore
get; private set;
}
public int SelectedChassis = 1;
public readonly string[] SelectedInstances = new string[4];
public int[] InitialEquipment = { 0, 0, 0, 0 };
LocalizedMessage noticeMessage;
public string Notice => noticeMessage?.ToString() ?? "";
@@ -76,7 +76,7 @@ namespace Ironspore
Active = this;
GameLocalization.Initialize(localization);
Application.targetFrameRate = 120;
mainStore = new ProfileStore(Path.Combine(Application.persistentDataPath, "Ironspore", "profile.json"));
mainStore = new ProfileStore(ProfileStore.SavePath(false));
Store = mainStore;
Input = Instantiate(inputTemplate);
Input.Enable();
@@ -90,14 +90,16 @@ namespace Ironspore
World.BuildHangar();
Hud.ShowHangar();
SetMenu(true);
if (Run.active && (Run.destructionPending || Run.armor <= 0))
EndSortie(true);
if (startInRange)
StartTrial();
}
public EquipmentDefinition Part(int slot, int index)
{
var list = Array.FindAll(equipment, x => x.slot == slot);
return list[Mathf.Clamp(index, 0, list.Length - 1)];
string id = InventoryGrid.EquipmentId(slot, index);
return Array.Find(equipment, x => x.id == id);
}
public int PartCount(int slot) => Array.FindAll(equipment, x => x.slot == slot).Length;
public bool Fits(int frame, int[] parts)
@@ -106,6 +108,8 @@ namespace Ironspore
for (int i = 0; i < 4; i++)
{
var p = Part(i, parts[i]);
if (parts[i] == -1) continue;
if (!p) return false;
mass += p.mass;
power += p.power;
}
@@ -113,10 +117,13 @@ namespace Ironspore
}
public bool StartingPartsUnlocked()
{
var unlocked = mainStore.Data.unlocked;
return (InitialEquipment[0] != 1 || unlocked.Contains("blueprint_coil_capacitor"))
&& (InitialEquipment[2] != 1 || unlocked.Contains("blueprint_boost_efficient"))
&& (InitialEquipment[3] != 1 || unlocked.Contains("blueprint_reactive_guard"));
for (int slot = 0; slot < 4; slot++)
{
if (InitialEquipment[slot] == 0 && SelectedInstances[slot] == null) continue;
string id = InventoryGrid.EquipmentId(slot, InitialEquipment[slot]);
if (!mainStore.Data.warehouse.Exists(i => i.itemId == id && (SelectedInstances[slot] == null || i.uid == SelectedInstances[slot]))) return false;
}
return true;
}
public void BeginSortie()
@@ -138,7 +145,7 @@ namespace Ironspore
}
Trial = false;
Store = mainStore;
if (!Store.Begin(SelectedChassis, InitialEquipment, chassis[SelectedChassis].armor, Environment.TickCount))
if (!Store.Begin(SelectedChassis, InitialEquipment, chassis[SelectedChassis].armor, Environment.TickCount, selected: SelectedInstances))
{
NotifySaveFailure();
return;
@@ -147,17 +154,23 @@ namespace Ironspore
}
public void StartTrial()
{
var range = new ProfileStore(ProfileStore.SavePath(true));
if ((range.Data.run.active && !range.Finish(true)) ||
!range.Begin(SelectedChassis, InitialEquipment, chassis[SelectedChassis].armor, 1739, trial: true))
{
Notify("notice.saveFailed");
return;
}
Trial = true;
Store = new ProfileStore(Path.Combine(Application.persistentDataPath, "Ironspore", "range.json"));
Store.Begin(SelectedChassis, InitialEquipment, chassis[SelectedChassis].armor, 1739);
Store = range;
EnterField();
}
public void Resume()
{
Trial = false;
Store = mainStore;
if (Run.active)
EnterField();
if (Run.active && (Run.destructionPending || Run.armor <= 0)) EndSortie(true);
else if (Run.active) EnterField();
}
void EnterField()
{
@@ -211,6 +224,11 @@ namespace Ironspore
noticeMessage = null;
if (!InField || !Pilot || ending)
return;
if (Pilot.Health <= 0 || Run.destructionPending)
{
EndSortie(true);
return;
}
if (Action("Pause").WasPressedThisFrame())
{
if (MenuOpen)
@@ -239,11 +257,6 @@ namespace Ironspore
if (!MenuOpen && Action("Interact").WasPressedThisFrame() && World.Focused)
World.Focused.Activate();
TickCargo();
if (Pilot.Health <= 0)
{
EndSortie(true);
return;
}
autosaveTimer += Time.deltaTime;
if (autosaveTimer >= 10)
{
@@ -281,10 +294,12 @@ namespace Ironspore
{
if (SelectedCargo.Contains(uid))
SelectedCargo.Remove(uid);
else if (SelectedCargo.Count < 2)
SelectedCargo.Add(uid);
else
Notify("notice.twoSlots");
{
var next = new List<string>(SelectedCargo) { uid };
if (Store.CanSendCargo(next)) SelectedCargo.Add(uid);
else Notify("notice.twoSlots");
}
Hud.ShowCargo();
}
public void SnapshotVitals()
@@ -296,7 +311,9 @@ namespace Ironspore
}
public void Search(WorldInteractable target)
{
if (!Store.AddCargo(target.itemId, target.amount, target.key))
int index = target.offersPart ? Mathf.Clamp(target.partIndex, 0, PartCount(target.partSlot) - 1) : -1;
string offered = target.offersPart ? Part(target.partSlot, index).id : null;
if (!Store.AddCargo(target.itemId, target.amount, target.key, offered))
{
NotifySaveFailure("notice.holdFull");
return;
@@ -308,22 +325,27 @@ namespace Ironspore
if (target.offersPart)
{
SetMenu(true);
Hud.ShowPart(target.partSlot, target.partIndex);
Hud.ShowPart(target.partSlot, index);
}
}
public void Install(int slot, int index)
{
var next = (int[])Run.equipment.Clone();
next[slot] = index;
if (!Fits(Run.chassis, next))
{
Notify("notice.partBudget");
return;
}
Store.Commit(s => { s.run.equipment = next; return true; });
Pilot.ApplyLoadout();
var item = Run.cargo.FindLast(i => i.itemId == InventoryGrid.EquipmentId(slot, index));
if (item == null || !InstallItem(item, Hold.Cargo)) return;
CloseMenu();
Notify("notice.refit", GameLocalization.Message("equipment." + Part(slot, index).id + ".name"));
}
public bool InstallItem(CargoStack item, Hold from)
{
if (!Store.InstallItem(item.uid, from, parts => Fits(Run.chassis, parts)))
{
NotifySaveFailure("notice.installFailed");
return false;
}
SelectedCargo.Remove(item.uid);
Pilot.ApplyLoadout();
Notify("notice.refit", GameLocalization.Message("equipment." + item.itemId + ".name"));
return true;
}
public void CompleteObjective()
{
@@ -398,15 +420,19 @@ namespace Ironspore
return;
SnapshotVitals();
bool complete = Run.routeCompleted;
int lost = Run.cargo.Count;
int lost = Run.cargo.Count + Run.installed.Count;
int protectedCount = Run.safeCase.FindAll(i => !i.borrowed).Count;
if (!Store.Finish(destroyed))
{
NotifySaveFailure();
return;
}
ending = true;
reportMessage = GameLocalization.Message(Trial ? "report.range" : destroyed ? "report.destroyed" : complete ? "report.complete" : "report.extracted", lost);
reportMessage = GameLocalization.Message(Trial ? "report.range" : destroyed ? "report.destroyed" : complete ? "report.complete" : "report.extracted", lost, protectedCount);
InField = false;
Paused = false;
Time.timeScale = 1;
Array.Clear(SelectedInstances, 0, SelectedInstances.Length);
Trial = false;
Store = mainStore;
Pilot = null;