feat(equipment): persist mech loadouts and recover ejection safe case loot
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ironspore
|
||||
{
|
||||
public sealed partial class ProfileStore
|
||||
{
|
||||
static bool Live(RunRecord run) => run.active && !run.destructionPending && run.armor > 0;
|
||||
static List<CargoStack> Items(RunRecord r, Hold hold) => hold == Hold.Cargo ? r.cargo : r.safeCase;
|
||||
static int Capacity(Hold hold) => hold == Hold.Cargo ? InventoryGrid.CargoSize : InventoryGrid.CaseSize;
|
||||
static bool ValidHold(Hold hold) => hold == Hold.Cargo || hold == Hold.SafeCase;
|
||||
|
||||
static bool Valid(GameSave s)
|
||||
{
|
||||
if (s?.run == null || s.receipts == null || s.blueprints == null || s.unlocked == null ||
|
||||
s.warehouse == null || s.run.cargo == null || s.run.safeCase == null || s.run.installed == null ||
|
||||
s.run.searched == null || s.run.equipment?.Length != 4 || s.version != GameSave.CurrentVersion) return false;
|
||||
var ids = new HashSet<string>();
|
||||
var slots = new HashSet<int>();
|
||||
bool Check(CargoStack i) => i != null && !string.IsNullOrEmpty(i.uid) && ids.Add(i.uid) &&
|
||||
InventoryGrid.Known(i.itemId) && i.count > 0 && i.count <= (InventoryGrid.IsMaterial(i.itemId) ? 10 : 1);
|
||||
foreach (var i in s.warehouse)
|
||||
if (!Check(i) || i.borrowed || InventoryGrid.Slot(i.itemId) < 0) return false;
|
||||
foreach (var hold in new[] { Hold.Cargo, Hold.SafeCase })
|
||||
{
|
||||
var list = Items(s.run, hold);
|
||||
foreach (var i in list)
|
||||
if (!Check(i) || !InventoryGrid.Fits(list, Capacity(hold), i, i.x, i.y, i.rotated)) return false;
|
||||
}
|
||||
foreach (var i in s.run.installed)
|
||||
{
|
||||
if (!Check(i)) return false;
|
||||
int slot = InventoryGrid.Slot(i.itemId);
|
||||
if (slot < 0 || !slots.Add(slot) || InventoryGrid.EquipmentId(slot, s.run.equipment[slot]) != i.itemId) return false;
|
||||
}
|
||||
if (s.run.active)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s.run.id)) return false;
|
||||
for (int slot = 0; slot < 4; slot++)
|
||||
if (!slots.Contains(slot) && s.run.equipment[slot] != -1) return false;
|
||||
}
|
||||
else if (s.run.destructionPending || s.run.installed.Count + s.run.cargo.Count + s.run.safeCase.Count != 0) return false;
|
||||
return new HashSet<string>(s.receipts).Count == s.receipts.Count;
|
||||
}
|
||||
|
||||
public bool Manufacture(string blueprint) => Commit(s =>
|
||||
{
|
||||
var recipe = InventoryGrid.Recipe(blueprint);
|
||||
if (s.run.active || recipe.item == null || !s.unlocked.Contains(blueprint) ||
|
||||
s.alloy < recipe.alloy || s.cells < recipe.cells) return false;
|
||||
s.alloy -= recipe.alloy;
|
||||
s.cells -= recipe.cells;
|
||||
s.warehouse.Add(new CargoStack(recipe.item, 1));
|
||||
return true;
|
||||
});
|
||||
|
||||
public bool AddCargo(string id, int count, string sourceKey = "", string offeredPart = null)
|
||||
{
|
||||
if (!InventoryGrid.Known(id) || count < 1 || count > (InventoryGrid.IsMaterial(id) ? 10 : 1)) return false;
|
||||
return Commit(s =>
|
||||
{
|
||||
var r = s.run;
|
||||
if (!Live(r) || (!string.IsNullOrEmpty(sourceKey) && r.searched.Contains(sourceKey))) return false;
|
||||
int remaining = count;
|
||||
if (InventoryGrid.IsMaterial(id))
|
||||
{
|
||||
foreach (var stack in r.cargo)
|
||||
{
|
||||
if (stack.itemId != id) continue;
|
||||
int added = Math.Min(10 - stack.count, remaining);
|
||||
stack.count += added;
|
||||
remaining -= added;
|
||||
if (remaining == 0) break;
|
||||
}
|
||||
}
|
||||
if (remaining > 0 && !InventoryGrid.AutoPlace(r.cargo, InventoryGrid.CargoSize, new CargoStack(id, remaining))) return false;
|
||||
if (offeredPart != null && (InventoryGrid.Slot(offeredPart) < 0 ||
|
||||
!InventoryGrid.AutoPlace(r.cargo, InventoryGrid.CargoSize, new CargoStack(offeredPart, 1)))) return false;
|
||||
if (!string.IsNullOrEmpty(sourceKey)) r.searched.Add(sourceKey);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// Omitting coordinates requests first-fit placement. Explicit placement also rotates in place.
|
||||
public bool MoveItem(string uid, Hold from, Hold to, int x = -1, int y = -1, bool rotated = false, int quantity = 0)
|
||||
{
|
||||
if (!ValidHold(from) || !ValidHold(to)) return false;
|
||||
return Commit(s =>
|
||||
{
|
||||
if (!Live(s.run)) return false;
|
||||
var source = Items(s.run, from);
|
||||
var item = source.Find(i => i.uid == uid);
|
||||
if (item == null || quantity < 0 || quantity > item.count) return false;
|
||||
if (quantity == 0 || quantity == item.count) source.Remove(item);
|
||||
else
|
||||
{
|
||||
item.count -= quantity;
|
||||
item = new CargoStack(item.itemId, quantity);
|
||||
}
|
||||
var target = Items(s.run, to);
|
||||
return x == -1 && y == -1 ? InventoryGrid.AutoPlace(target, Capacity(to), item)
|
||||
: InventoryGrid.Place(target, Capacity(to), item, x, y, rotated);
|
||||
});
|
||||
}
|
||||
|
||||
public bool SwapItems(string uid, Hold from, string otherUid, Hold to, bool rotated)
|
||||
{
|
||||
if (!ValidHold(from) || !ValidHold(to) || uid == otherUid) return false;
|
||||
return Commit(s =>
|
||||
{
|
||||
if (!Live(s.run)) return false;
|
||||
var source = Items(s.run, from);
|
||||
var target = Items(s.run, to);
|
||||
var first = source.Find(i => i.uid == uid);
|
||||
var second = target.Find(i => i.uid == otherUid);
|
||||
if (first == null || second == null) return false;
|
||||
int x = first.x, y = first.y;
|
||||
source.Remove(first);
|
||||
target.Remove(second);
|
||||
return InventoryGrid.Place(target, Capacity(to), first, second.x, second.y, rotated) &&
|
||||
InventoryGrid.Place(source, Capacity(from), second, x, y, second.rotated);
|
||||
});
|
||||
}
|
||||
|
||||
public bool MergeMaterial(string sourceUid, Hold from, string targetUid, Hold to, int quantity)
|
||||
{
|
||||
if (!ValidHold(from) || !ValidHold(to) || sourceUid == targetUid || quantity < 1) return false;
|
||||
return Commit(s =>
|
||||
{
|
||||
if (!Live(s.run)) return false;
|
||||
var source = Items(s.run, from).Find(i => i.uid == sourceUid);
|
||||
var target = Items(s.run, to).Find(i => i.uid == targetUid);
|
||||
if (source == null || target == null || source.itemId != target.itemId || !InventoryGrid.IsMaterial(source.itemId)
|
||||
|| quantity > source.count || quantity > 10 - target.count) return false;
|
||||
source.count -= quantity;
|
||||
target.count += quantity;
|
||||
if (source.count == 0) Items(s.run, from).Remove(source);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public bool Uninstall(int slot, Hold to, int x, int y, bool rotated)
|
||||
{
|
||||
if (!ValidHold(to)) return false;
|
||||
return Commit(s =>
|
||||
{
|
||||
var r = s.run;
|
||||
if (!Live(r) || slot < 0 || slot > 3) return false;
|
||||
var item = r.installed.Find(i => InventoryGrid.Slot(i.itemId) == slot);
|
||||
if (item == null || !InventoryGrid.Place(Items(r, to), Capacity(to), item, x, y, rotated)) return false;
|
||||
r.installed.Remove(item);
|
||||
r.equipment[slot] = -1;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public bool InstallItem(string uid, Hold from, Func<int[], bool> fitsBudget)
|
||||
{
|
||||
if (!ValidHold(from) || fitsBudget == null) return false;
|
||||
return Commit(s =>
|
||||
{
|
||||
var r = s.run;
|
||||
if (!Live(r)) return false;
|
||||
var source = Items(r, from);
|
||||
var item = source.Find(i => i.uid == uid);
|
||||
if (item == null) return false;
|
||||
int slot = InventoryGrid.Slot(item.itemId);
|
||||
if (slot < 0) return false;
|
||||
r.equipment[slot] = InventoryGrid.Index(slot, item.itemId);
|
||||
if (!fitsBudget(r.equipment)) return false;
|
||||
source.Remove(item);
|
||||
var old = r.installed.Find(i => InventoryGrid.Slot(i.itemId) == slot);
|
||||
if (old != null)
|
||||
{
|
||||
if (!InventoryGrid.AutoPlace(source, Capacity(from), old)) return false;
|
||||
r.installed.Remove(old);
|
||||
}
|
||||
r.installed.Add(item);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void Bank(GameSave s, CargoStack item)
|
||||
{
|
||||
if (item.borrowed) return;
|
||||
if (InventoryGrid.Slot(item.itemId) >= 0) s.warehouse.Add(item);
|
||||
else if (item.itemId == "alloy") s.alloy += item.count;
|
||||
else if (item.itemId == "cells") s.cells += item.count;
|
||||
else if (!s.blueprints.Contains(item.itemId)) s.blueprints.Add(item.itemId);
|
||||
else s.alloy += 2;
|
||||
}
|
||||
|
||||
static List<CargoStack> Manifest(RunRecord r, IReadOnlyCollection<string> selection)
|
||||
{
|
||||
if (!Live(r) || r.podUsed || selection == null || selection.Count == 0 || selection.Count > 4) return null;
|
||||
var ids = new HashSet<string>(selection);
|
||||
var picked = r.cargo.FindAll(i => ids.Contains(i.uid));
|
||||
return picked.Count == selection.Count && InventoryGrid.PodFits(picked) ? picked : null;
|
||||
}
|
||||
|
||||
public bool CanSendCargo(IReadOnlyCollection<string> selection) => Manifest(Data.run, selection) != null;
|
||||
|
||||
public bool SendCargo(IReadOnlyCollection<string> selection) => Commit(s =>
|
||||
{
|
||||
string receipt = s.run.id + ":cargo";
|
||||
var picked = Manifest(s.run, selection);
|
||||
if (picked == null || s.receipts.Contains(receipt)) return false;
|
||||
foreach (var item in picked)
|
||||
{
|
||||
Bank(s, item);
|
||||
s.run.cargo.Remove(item);
|
||||
}
|
||||
s.run.podUsed = true;
|
||||
s.receipts.Add(receipt);
|
||||
return true;
|
||||
});
|
||||
|
||||
public bool MarkDestroyed() => Commit(s =>
|
||||
{
|
||||
if (!s.run.active) return false;
|
||||
s.run.armor = 0;
|
||||
s.run.destructionPending = true;
|
||||
return true;
|
||||
});
|
||||
|
||||
public bool Finish(bool destroyed)
|
||||
{
|
||||
if (destroyed && !Data.run.destructionPending && !MarkDestroyed()) return false;
|
||||
return Write(s =>
|
||||
{
|
||||
var r = s.run;
|
||||
string receipt = r.id + ":return";
|
||||
if (!r.active || r.destructionPending != destroyed || s.receipts.Contains(receipt)) return false;
|
||||
if (destroyed) s.receipts.Add(r.id + ":secure_eject");
|
||||
else
|
||||
{
|
||||
if (r.armor <= 0) return false;
|
||||
foreach (var item in r.cargo) Bank(s, item);
|
||||
foreach (var item in r.installed) Bank(s, item);
|
||||
s.extractions++;
|
||||
}
|
||||
foreach (var item in r.safeCase) Bank(s, item);
|
||||
r.safeCase.Clear();
|
||||
r.cargo.Clear();
|
||||
r.installed.Clear();
|
||||
r.active = false;
|
||||
r.destructionPending = false;
|
||||
s.receipts.Add(receipt);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user