114 lines
4.8 KiB
C#
114 lines
4.8 KiB
C#
using System;
|
|||
|
|
using System.Collections.Generic;
|
||
|
|
|
||
|
|
namespace Ironspore
|
||
|
|
{
|
||
|
|
public enum Hold { Cargo, SafeCase }
|
||
|
|
|
||
|
|
// Stable content IDs also resolve the existing four gameplay slot indices.
|
||
|
|
public static class InventoryGrid
|
||
|
|
{
|
||
|
|
public const int CargoSize = 4, CaseSize = 2, PodSize = 2;
|
||
|
|
static readonly string[][] Slots = {
|
||
|
|
new[] { "rapid", "coil", "siege" }, new[] { "guided", "pulse" },
|
||
|
|
new[] { "efficient", "vector" }, new[] { "composite_guard", "reactive_guard" }
|
||
|
|
};
|
||
|
|
|
||
|
|
public static string EquipmentId(int slot, int index) =>
|
||
|
|
slot >= 0 && slot < Slots.Length && index >= 0 && index < Slots[slot].Length ? Slots[slot][index] : null;
|
||
|
|
|
||
|
|
public static int Slot(string id) => Array.FindIndex(Slots, group => Array.IndexOf(group, id) >= 0);
|
||
|
|
public static int Index(int slot, string id) => Array.IndexOf(Slots[slot], id);
|
||
|
|
public static bool IsMaterial(string id) => id == "alloy" || id == "cells";
|
||
|
|
public static bool IsBlueprint(string id) => id != null && id.StartsWith("blueprint_", StringComparison.Ordinal) && id.Length > 10;
|
||
|
|
public static bool Known(string id) => IsMaterial(id) || IsBlueprint(id) || Slot(id) >= 0;
|
||
|
|
|
||
|
|
public static (string item, int alloy, int cells) Recipe(string blueprint)
|
||
|
|
{
|
||
|
|
switch (blueprint)
|
||
|
|
{
|
||
|
|
case "blueprint_coil_capacitor": return ("coil", 4, 2);
|
||
|
|
case "blueprint_boost_efficient": return ("vector", 3, 2);
|
||
|
|
case "blueprint_reactive_guard": return ("reactive_guard", 4, 1);
|
||
|
|
default: return (null, 0, 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static void Size(string id, bool rotated, out int width, out int height)
|
||
|
|
{
|
||
|
|
width = 1;
|
||
|
|
height = 1;
|
||
|
|
switch (id)
|
||
|
|
{
|
||
|
|
case "rapid": height = 3; break;
|
||
|
|
case "coil": width = 2; height = 3; break;
|
||
|
|
case "siege": width = 2; height = 4; break;
|
||
|
|
case "pulse": case "reactive_guard": case "vector": width = 2; height = 2; break;
|
||
|
|
case "guided": case "efficient": case "composite_guard": height = 2; break;
|
||
|
|
}
|
||
|
|
if (rotated) (width, height) = (height, width);
|
||
|
|
}
|
||
|
|
|
||
|
|
public static int Area(CargoStack item)
|
||
|
|
{
|
||
|
|
Size(item.itemId, false, out int w, out int h);
|
||
|
|
return w * h;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static bool Fits(List<CargoStack> items, int size, CargoStack item, int x, int y, bool rotated)
|
||
|
|
{
|
||
|
|
Size(item.itemId, rotated, out int w, out int h);
|
||
|
|
if (x < 0 || y < 0 || x > size - w || y > size - h) return false;
|
||
|
|
foreach (var other in items)
|
||
|
|
{
|
||
|
|
if (other == null) return false;
|
||
|
|
if (ReferenceEquals(item, other)) continue;
|
||
|
|
Size(other.itemId, other.rotated, out int ow, out int oh);
|
||
|
|
if (x < other.x + ow && x + w > other.x && y < other.y + oh && y + h > other.y)
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static bool Place(List<CargoStack> items, int size, CargoStack item, int x, int y, bool rotated)
|
||
|
|
{
|
||
|
|
if (items.Contains(item) || !Fits(items, size, item, x, y, rotated)) return false;
|
||
|
|
item.x = x;
|
||
|
|
item.y = y;
|
||
|
|
item.rotated = rotated;
|
||
|
|
items.Add(item);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static bool AutoPlace(List<CargoStack> items, int size, CargoStack item)
|
||
|
|
{
|
||
|
|
for (int turn = 0; turn < 2; turn++)
|
||
|
|
for (int y = 0; y < size; y++)
|
||
|
|
for (int x = 0; x < size; x++)
|
||
|
|
if (Place(items, size, item, x, y, turn == 1)) return true;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// The pod is small. Try alternate placements so a greedy choice cannot reject a valid manifest.
|
||
|
|
public static bool PodFits(List<CargoStack> selection)
|
||
|
|
{
|
||
|
|
if (selection.Count == 0 || selection.Count > PodSize * PodSize) return false;
|
||
|
|
return Pack(0, new List<CargoStack>());
|
||
|
|
bool Pack(int index, List<CargoStack> placed)
|
||
|
|
{
|
||
|
|
if (index == selection.Count) return true;
|
||
|
|
var next = new CargoStack(selection[index].itemId, selection[index].count) { uid = selection[index].uid };
|
||
|
|
for (int turn = 0; turn < 2; turn++)
|
||
|
|
for (int y = 0; y < PodSize; y++)
|
||
|
|
for (int x = 0; x < PodSize; x++)
|
||
|
|
{
|
||
|
|
if (!Place(placed, PodSize, next, x, y, turn == 1)) continue;
|
||
|
|
if (Pack(index + 1, placed)) return true;
|
||
|
|
placed.RemoveAt(placed.Count - 1);
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|