254 lines
12 KiB
C#
254 lines
12 KiB
C#
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;
|
|
});
|
|
}
|
|
}
|
|
}
|