Use typed catalog rows for equip acceptance

This commit is contained in:
Meta Vens 2026-06-14 04:56:01 +01:00
commit f85033b6c6
3 changed files with 163 additions and 54 deletions

View file

@ -489,15 +489,15 @@ public abstract class Entity
Equipment.Remove(item);
_orderedEquipment = Equipment.OrderBy(x => x.SortPosition).ToArray();
var hullData = ItemManager.GetData(Hull) as HullData;
var itemData = ItemManager.GetData(item.EquippableItem);
foreach (var i in hullData.Shape.Coordinates)
var itemShape = GetItemShape(item.EquippableItem);
var itemCellCount = Math.Max(itemShape.Coordinates.Length, 1);
foreach (var i in GetHullShape().Coordinates)
if (GearOccupancy[i.x, i.y] == item)
{
ThermalMass[i.x, i.y] -= ItemManager.GetThermalMass(item.EquippableItem) / itemData.Shape.Coordinates.Length;
ThermalMass[i.x, i.y] -= ItemManager.GetThermalMass(item.EquippableItem) / itemCellCount;
GearOccupancy[i.x, i.y] = null;
}
Mass -= itemData.Mass;
Mass -= ItemManager.GetMass(item.EquippableItem);
foreach (var b in item.Behaviors)
{
if (b is Weapon weapon)
@ -519,44 +519,136 @@ public abstract class Entity
return item.EquippableItem;
}
// Check whether the given item will fit when its origin is placed at the given coordinate
private bool ItemFits(EquippableItemData itemData, HullData hullData, EquippableItem item, int2 hullCoord)
private Shape GetHullShape()
{
return GetHullShape(ItemManager.GetRuntimeItem(Hull));
}
private static Shape GetHullShape(AetheriaRuntimeCatalogItem typedHull)
{
return ToShape(typedHull?.ShapeWidth ?? 1, typedHull?.ShapeHeight ?? 1, typedHull?.ShapeCells);
}
private static Shape GetHullInteriorShape(AetheriaRuntimeCatalogItem typedHull)
{
return ToShape(typedHull?.InteriorShapeWidth ?? 1, typedHull?.InteriorShapeHeight ?? 1, typedHull?.InteriorShapeCells);
}
private Shape GetItemShape(EquippableItem item)
{
return GetItemShape(ItemManager.GetRuntimeItem(item));
}
private static Shape GetItemShape(AetheriaRuntimeCatalogItem typedItem)
{
return ToShape(typedItem?.ShapeWidth ?? 1, typedItem?.ShapeHeight ?? 1, typedItem?.ShapeCells);
}
private static AetheriaRuntimeHardpoint GetHardpointAt(AetheriaRuntimeCatalogItem typedHull, int2 hullCoord)
{
if (typedHull == null) return null;
foreach (var hardpoint in typedHull.Hardpoints)
{
var hardpointShape = ToShape(hardpoint.ShapeWidth, hardpoint.ShapeHeight, hardpoint.ShapeCells);
var localCoord = hullCoord - new int2(hardpoint.PositionX, hardpoint.PositionY);
if (hardpointShape[localCoord])
return hardpoint;
}
return null;
}
private static HardpointType GetHardpointType(AetheriaRuntimeCatalogItem typedItem, HardpointType fallback)
{
return typedItem != null && Enum.TryParse(typedItem.HardpointType, out HardpointType hardpointType)
? hardpointType
: fallback;
}
private static HardpointType GetHardpointType(AetheriaRuntimeHardpoint hardpoint, HardpointType fallback)
{
return hardpoint != null && Enum.TryParse(hardpoint.Type, out HardpointType hardpointType)
? hardpointType
: fallback;
}
private static ItemRotation GetRotation(AetheriaRuntimeHardpoint hardpoint)
{
return hardpoint != null && Enum.TryParse(hardpoint.Rotation, out ItemRotation rotation)
? rotation
: ItemRotation.None;
}
private static bool IsCargoBay(AetheriaRuntimeCatalogItem typedItem)
{
return typedItem != null &&
(string.Equals(typedItem.Category, "CargoBayData", StringComparison.Ordinal) ||
string.Equals(typedItem.Category, "DockingBayData", StringComparison.Ordinal));
}
private static bool IsDockingBay(AetheriaRuntimeCatalogItem typedItem)
{
return typedItem != null && string.Equals(typedItem.Category, "DockingBayData", StringComparison.Ordinal);
}
private static Shape ToShape(int width, int height, IReadOnlyList<AetheriaRuntimeShapeCell> cells)
{
var shape = new Shape(Math.Max(width, 1), Math.Max(height, 1));
if (cells == null) return shape;
foreach (var cell in cells)
shape[new int2(cell.X, cell.Y)] = true;
return shape;
}
// Check whether the given item will fit when its origin is placed at the given coordinate
private bool ItemFits(AetheriaRuntimeCatalogItem typedItem, AetheriaRuntimeCatalogItem typedHull, EquippableItem item, int2 hullCoord)
{
var hullShape = GetHullShape(typedHull);
var itemShape = GetItemShape(typedItem);
var itemHardpointType = GetHardpointType(typedItem, HardpointType.Tool);
if (itemShape.Coordinates.Length == 0) return false;
// If the given coordinate isn't even in the ship it obviously won't fit
if (!hullData.Shape[hullCoord]) return false;
if (!hullShape[hullCoord]) return false;
// Items without specific hardpoints on the ship can be freely rotated and placed anywhere
if (itemData.HardpointType == HardpointType.Tool)
if (itemHardpointType == HardpointType.Tool)
{
var hullInterior = GetHullInteriorShape(typedHull);
// Check every cell of the item's shape
foreach (var i in itemData.Shape.Coordinates)
foreach (var i in itemShape.Coordinates)
{
// If there is any gear already occupying that space, it won't fit
// If there's a hardpoint there, it won't fit
// Thermal items have their own layer and do not collide with gear
var itemCoord = hullCoord + itemData.Shape.Rotate(i, item.Rotation);
if (!hullData.InteriorCells[itemCoord] ||
itemData.HardpointType == HardpointType.Tool && Hardpoints[itemCoord.x, itemCoord.y] != null ||
var itemCoord = hullCoord + itemShape.Rotate(i, item.Rotation);
if (!hullInterior[itemCoord] ||
GetHardpointAt(typedHull, itemCoord) != null ||
GearOccupancy[itemCoord.x, itemCoord.y] != null)
return false;
}
}
else
{
var hardpoint = Hardpoints[hullCoord.x, hullCoord.y];
var hardpoint = GetHardpointAt(typedHull, hullCoord);
// If there's no hardpoint there, it won't fit
if (hardpoint == null) return false;
// If the hardpoint type doesn't match the item, it won't fit
if (hardpoint.Type != itemData.HardpointType) return false;
if (GetHardpointType(hardpoint, HardpointType.Hull) != itemHardpointType) return false;
// Items placed in hardpoints are automatically aligned to hardpoint rotation
item.Rotation = hardpoint.Rotation;
item.Rotation = GetRotation(hardpoint);
// Inset the shapes of both item and hardpoint
var itemShapeInset = hullData.Shape.Inset(itemData.Shape, hullCoord, item.Rotation);
var hardpointShapeInset = hullData.Shape.Inset(hardpoint.Shape, hardpoint.Position);
var itemShapeInset = hullShape.Inset(itemShape, hullCoord, item.Rotation);
var hardpointShapeInset = hullShape.Inset(
ToShape(hardpoint.ShapeWidth, hardpoint.ShapeHeight, hardpoint.ShapeCells),
new int2(hardpoint.PositionX, hardpoint.PositionY));
// Check every cell of the hardpoint shape for existing items
foreach(var v in hardpointShapeInset.Coordinates)
@ -583,9 +675,9 @@ public abstract class Entity
// Don't allow equipping while deployed
if (_active) return false;
var itemData = ItemManager.GetData(item);
var hullData = ItemManager.GetData(Hull) as HullData;
return ItemFits(itemData, hullData, item, hullCoord);
var typedItem = ItemManager.GetRuntimeItem(item);
var typedHull = ItemManager.GetRuntimeItem(Hull);
return typedItem != null && typedHull != null && ItemFits(typedItem, typedHull, item, hullCoord);
}
public bool TryFindSpace(EquippableItem item, out int2 hullCoord)
@ -596,16 +688,23 @@ public abstract class Entity
hullCoord = int2.zero;
return false;
}
var itemData = ItemManager.GetData(item);
var hullData = ItemManager.GetData(Hull) as HullData;
var typedItem = ItemManager.GetRuntimeItem(item);
var typedHull = ItemManager.GetRuntimeItem(Hull);
if (typedItem == null || typedHull == null)
{
hullCoord = int2.zero;
return false;
}
var itemHardpointType = GetHardpointType(typedItem, HardpointType.Tool);
// Tools and thermal equipment can be installed anywhere on the ship
// Search the whole ship for somewhere the item will fit
if (itemData.HardpointType == HardpointType.Tool)
if (itemHardpointType == HardpointType.Tool)
{
foreach (var hullCoord2 in hullData.InteriorCells.Coordinates)
foreach (var hullCoord2 in GetHullInteriorShape(typedHull).Coordinates)
{
if (ItemFits(itemData, hullData, item, hullCoord2))
if (ItemFits(typedItem, typedHull, item, hullCoord2))
{
hullCoord = hullCoord2;
return true;
@ -617,14 +716,15 @@ public abstract class Entity
// Search the ship for an empty hardpoint that matches the type and shape of the item
else
{
foreach (var hardpoint in hullData.Hardpoints)
foreach (var hardpoint in typedHull.Hardpoints)
{
if(hardpoint.Type == itemData.HardpointType)
if(GetHardpointType(hardpoint, HardpointType.Hull) == itemHardpointType)
{
foreach (var hardpointCoord in hardpoint.Shape.Coordinates)
var hardpointShape = ToShape(hardpoint.ShapeWidth, hardpoint.ShapeHeight, hardpoint.ShapeCells);
foreach (var hardpointCoord in hardpointShape.Coordinates)
{
var hullCoord2 = hardpoint.Position + hardpointCoord;
if (ItemFits(itemData, hullData, item, hullCoord2))
var hullCoord2 = new int2(hardpoint.PositionX, hardpoint.PositionY) + hardpointCoord;
if (ItemFits(typedItem, typedHull, item, hullCoord2))
{
hullCoord = hullCoord2;
return true;
@ -647,17 +747,20 @@ public abstract class Entity
// Don't allow equipping while deployed
if (_active) return false;
var itemData = ItemManager.GetData(item);
var hullData = ItemManager.GetData(Hull) as HullData;
var typedItem = ItemManager.GetRuntimeItem(item);
var typedHull = ItemManager.GetRuntimeItem(Hull);
if (typedItem == null || typedHull == null) return false;
var itemHardpointType = GetHardpointType(typedItem, HardpointType.Tool);
var itemShape = GetItemShape(typedItem);
if (!ItemFits(itemData, hullData, item, hullCoord)) return false;
if (!ItemFits(typedItem, typedHull, item, hullCoord)) return false;
EquippedItem equippedItem;
if (itemData.HardpointType == HardpointType.Tool)
if (itemHardpointType == HardpointType.Tool)
{
if(itemData is CargoBayData)
if(IsCargoBay(typedItem))
{
if (itemData is DockingBayData)
if (IsDockingBay(typedItem))
{
equippedItem = new EquippedDockingBay(ItemManager, item, hullCoord, this, $"{Name} Docking Bay {DockingBays.Count + 1}");
DockingBays.Add((EquippedDockingBay) equippedItem);
@ -701,15 +804,15 @@ public abstract class Entity
// equippedItem.OnOnline += () => ItemOnline.OnNext(equippedItem);
// equippedItem.OnOffline += () => ItemOffline.OnNext(equippedItem);
foreach (var i in itemData.Shape.Coordinates)
foreach (var i in itemShape.Coordinates)
{
var occupiedCoord = hullCoord + itemData.Shape.Rotate(i, item.Rotation);
var occupiedCoord = hullCoord + itemShape.Rotate(i, item.Rotation);
// TODO: Track thermal mass of cargo bay contents as reactive property
ThermalMass[occupiedCoord.x, occupiedCoord.y] += ItemManager.GetThermalMass(item) / itemData.Shape.Coordinates.Length;
ThermalMass[occupiedCoord.x, occupiedCoord.y] += ItemManager.GetThermalMass(item) / itemShape.Coordinates.Length;
GearOccupancy[occupiedCoord.x, occupiedCoord.y] = equippedItem;
}
Mass += itemData.Mass;
Mass += ItemManager.GetMass(item);
_orderedEquipment = Equipment.OrderBy(x => x.SortPosition).ToArray();
return true;
}