Expose item identity on runtime instances

This commit is contained in:
Meta Vens 2026-06-14 11:12:48 +01:00
commit d163b3064c
17 changed files with 48 additions and 42 deletions

Binary file not shown.

View file

@ -150,7 +150,7 @@ public class ActionBarGearBinding : ActionBarBinding
private static AetheriaRuntimeCatalogItem FindTypedGearItem(ItemInstance item)
{
var itemId = item?.Data?.ItemId ?? Guid.Empty;
var itemId = item?.ItemId ?? Guid.Empty;
return itemId == Guid.Empty
? null
: ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));

View file

@ -388,7 +388,7 @@ public class ActionGameManager : MonoBehaviour
return new AetheriaRuntimeLoadoutItemCommit
{
ItemDefinitionLegacyId = item.Data?.ItemId.ToString("D") ?? "",
ItemDefinitionLegacyId = item.ItemId == Guid.Empty ? "" : item.ItemId.ToString("D"),
Quality = item is CraftedItemInstance crafted ? crafted.Quality : 1.0,
Durability = item is EquippableItem equippable ? equippable.Durability : 1.0,
Quantity = item is SimpleCommodity commodity ? commodity.Quantity : 1
@ -453,7 +453,7 @@ public class ActionGameManager : MonoBehaviour
DirectionX = entity.Direction.x,
DirectionY = entity.Direction.y,
CorporationLegacyId = entity.Faction?.ID.ToString("D") ?? "",
HullItemDefinitionLegacyId = entity.Hull?.Data?.ItemId.ToString("D") ?? "",
HullItemDefinitionLegacyId = entity.Hull?.ItemId == Guid.Empty ? "" : entity.Hull.ItemId.ToString("D"),
Equipment = ProjectEquippedSlots(entity.Equipment),
CargoBays = ProjectEquippedSlots(entity.CargoBays),
DockingBays = ProjectEquippedSlots(entity.DockingBays),
@ -772,7 +772,7 @@ public class ActionGameManager : MonoBehaviour
AetheriaRuntimeCatalogItem FindTypedActionBarItem(ItemInstance item)
{
var itemId = item?.Data?.ItemId ?? Guid.Empty;
var itemId = item?.ItemId ?? Guid.Empty;
return itemId == Guid.Empty
? null
: RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));

View file

@ -62,7 +62,7 @@ public class EntityInstance : MonoBehaviour
private static AetheriaRuntimeCatalogItem FindTypedHull(ItemInstance hull)
{
var itemId = hull?.Data?.ItemId ?? Guid.Empty;
var itemId = hull?.ItemId ?? Guid.Empty;
return itemId == Guid.Empty
? null
: ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));

View file

@ -148,7 +148,7 @@ public class ShipInstance : EntityInstance
private static AetheriaRuntimeCatalogItem FindTypedItem(ItemInstance item)
{
var itemId = item?.Data?.ItemId ?? Guid.Empty;
var itemId = item?.ItemId ?? Guid.Empty;
return itemId == Guid.Empty
? null
: ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));

View file

@ -263,7 +263,7 @@ public abstract class Entity
Hull = hull;
var typedHull = itemManager.GetRuntimeItem(hull);
if (typedHull == null)
throw new InvalidOperationException($"Unable to construct entity: missing typed hull row for {hull?.Data?.ItemId}");
throw new InvalidOperationException($"Unable to construct entity: missing typed hull row for {hull?.ItemId}");
Name = typedHull.Name;
MapEntity();
WeaponGroups = new (List<Weapon> weapons, List<EquippedItem> items)[itemManager.GameplaySettings.WeaponGroupCount];
@ -316,7 +316,7 @@ public abstract class Entity
public ConsumableItemEffect FindActiveConsumable(Guid itemId)
{
return _activeConsumables.FirstOrDefault(ac => ac.Item?.Data?.ItemId == itemId);
return _activeConsumables.FirstOrDefault(ac => ac.Item?.ItemId == itemId);
}
public bool CanActivateConsumable(AetheriaRuntimeCatalogItem item)
@ -350,7 +350,7 @@ public abstract class Entity
{
var typedHull = ItemManager.GetRuntimeItem(Hull);
if (typedHull == null)
throw new InvalidOperationException($"Unable to map entity {Name}: missing typed hull row for {Hull?.Data?.ItemId}");
throw new InvalidOperationException($"Unable to map entity {Name}: missing typed hull row for {Hull?.ItemId}");
var hullShape = GetHullShape(typedHull);
EquippedHull = new EquippedItem(ItemManager, Hull, int2.zero, this);
@ -391,7 +391,7 @@ public abstract class Entity
public void GenerateWeaponGroups()
{
foreach (var group in Weapons
.GroupBy(w => w.Item.EquippableItem.Data.ItemId)
.GroupBy(w => w.Item.EquippableItem.ItemId)
.OrderBy(wg=>wg.Average(w=>w.Range))
.Select((weapons, index) => (weapons, index)))
{
@ -1718,9 +1718,9 @@ public class EquippedCargoBay : EquippedItem
}
Cargo[item] = cargoCoord;
if(!ItemsOfType.ContainsKey(item.Data.ItemId))
ItemsOfType[item.Data.ItemId] = new List<ItemInstance>();
ItemsOfType[item.Data.ItemId].Add(item);
if(!ItemsOfType.ContainsKey(item.ItemId))
ItemsOfType[item.ItemId] = new List<ItemInstance>();
ItemsOfType[item.ItemId].Add(item);
}
else if (Occupancy[cargoCoord.x, cargoCoord.y] is SimpleCommodity cargoCommodity && cargoCommodity.Data == item.Data)
{
@ -1762,9 +1762,9 @@ public class EquippedCargoBay : EquippedItem
}
Cargo[item] = cargoCoord;
if(!ItemsOfType.ContainsKey(item.Data.ItemId))
ItemsOfType[item.Data.ItemId] = new List<ItemInstance>();
ItemsOfType[item.Data.ItemId].Add(item);
if(!ItemsOfType.ContainsKey(item.ItemId))
ItemsOfType[item.ItemId] = new List<ItemInstance>();
ItemsOfType[item.ItemId].Add(item);
Mass += ItemManager.GetMass(item);
ThermalMass += ItemManager.GetThermalMass(item);
@ -1786,9 +1786,9 @@ public class EquippedCargoBay : EquippedItem
Occupancy[v.x, v.y] = null;
Cargo.Remove(item);
ItemsOfType[item.Data.ItemId].Remove(item);
if (!ItemsOfType[item.Data.ItemId].Any())
ItemsOfType.Remove(item.Data.ItemId);
ItemsOfType[item.ItemId].Remove(item);
if (!ItemsOfType[item.ItemId].Any())
ItemsOfType.Remove(item.ItemId);
Mass -= ItemManager.GetMass(item);
ThermalMass -= ItemManager.GetThermalMass(item);
@ -1814,9 +1814,9 @@ public class EquippedCargoBay : EquippedItem
Occupancy[v.x, v.y] = null;
Cargo.Remove(item);
ItemsOfType[item.Data.ItemId].Remove(item);
if (!ItemsOfType[item.Data.ItemId].Any())
ItemsOfType.Remove(item.Data.ItemId);
ItemsOfType[item.ItemId].Remove(item);
if (!ItemsOfType[item.ItemId].Any())
ItemsOfType.Remove(item.ItemId);
Mass -= ItemManager.GetMass(item);
ThermalMass -= ItemManager.GetThermalMass(item);

View file

@ -14,6 +14,7 @@ public abstract class ItemInstance
{
public RuntimeItemReference Data;
public ItemRotation Rotation;
public Guid ItemId => Data?.ItemId ?? Guid.Empty;
}
public abstract class CraftedItemInstance : ItemInstance

View file

@ -91,8 +91,7 @@ public class ItemManager
public AetheriaRuntimeCatalogItem GetRuntimeItem(ItemInstance item)
{
var itemId = item?.Data?.ItemId ?? Guid.Empty;
return GetRuntimeItem(itemId);
return GetRuntimeItem(item?.ItemId ?? Guid.Empty);
}
public AetheriaRuntimeCatalogItem GetRuntimeItem(Guid itemId)
@ -363,7 +362,7 @@ public class ItemManager
var durability = pow(item.Durability / maxDurability, durabilityExponent * stat.DurabilityExponentMultiplier);
var result = lerp(stat.Min, stat.Max, quality * durability);
if (float.IsNaN(result))
throw new InvalidOperationException($"Performance Stat on {typedItem?.Name ?? item.Data?.ItemId.ToString() ?? "unknown item"} evaluating as NaN: input data is invalid! Durability: {item.Durability} / {maxDurability}");
throw new InvalidOperationException($"Performance Stat on {typedItem?.Name ?? item.ItemId.ToString()} evaluating as NaN: input data is invalid! Durability: {item.Durability} / {maxDurability}");
return result;
}
@ -413,7 +412,7 @@ public class ItemManager
var typedItem = GetRuntimeItem(item);
if (typedItem == null)
{
_logger($"Attempted to instantiate missing item id {item?.Data?.ItemId}");
_logger($"Attempted to instantiate missing item id {item?.ItemId}");
return null;
}

View file

@ -337,7 +337,7 @@ public class LoadoutGenerator
var previousItem = itemRow == null
? null
: Guid.TryParse(itemRow.LegacyId, out var previousItemId)
? entity.Equipment.FirstOrDefault(item => item.EquippableItem.Data.ItemId == previousItemId)
? entity.Equipment.FirstOrDefault(item => item.EquippableItem.ItemId == previousItemId)
: null;
itemRow ??= RandomCatalogItem(RuntimeItemCandidateKind.Gear, hardpoint, 2);
if (itemRow == null) ItemManager.Log($"No compatible item found for entity {hardpoint.Type} hardpoint!");

View file

@ -144,7 +144,7 @@ public class SchematicDisplay : MonoBehaviour
private static AetheriaRuntimeCatalogItem FindTypedWeapon(ItemInstance item)
{
var itemId = item?.Data?.ItemId ?? Guid.Empty;
var itemId = item?.ItemId ?? Guid.Empty;
var typedItem = itemId == Guid.Empty
? null
: ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));
@ -288,7 +288,7 @@ public class SchematicDisplay : MonoBehaviour
private static AetheriaRuntimeCatalogItem FindTypedItem(ItemInstance item)
{
var itemId = item?.Data?.ItemId ?? Guid.Empty;
var itemId = item?.ItemId ?? Guid.Empty;
return itemId == Guid.Empty
? null
: ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));

View file

@ -219,7 +219,7 @@ public class InventoryMenu : MonoBehaviour
private static AetheriaRuntimeCatalogItem FindTypedInventoryItem(ItemInstance item)
{
var itemId = item?.Data?.ItemId ?? Guid.Empty;
var itemId = item?.ItemId ?? Guid.Empty;
return itemId == Guid.Empty
? null
: ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));

View file

@ -925,7 +925,7 @@ public class InventoryPanel : MonoBehaviour, IPointerClickHandler
private static AetheriaRuntimeCatalogItem FindTypedInventoryItem(ItemInstance item)
{
var itemId = item?.Data?.ItemId ?? Guid.Empty;
var itemId = item?.ItemId ?? Guid.Empty;
return itemId == Guid.Empty
? null
: ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));

View file

@ -123,7 +123,7 @@ public class SectorRenderer : MonoBehaviour, IBeginDragHandler, IDragHandler, IS
private static AetheriaRuntimeCatalogItem FindTypedHull(RuntimeEntityBlueprint entity)
{
var itemId = entity?.Hull?.Data?.ItemId ?? Guid.Empty;
var itemId = entity?.Hull?.ItemId ?? Guid.Empty;
return itemId == Guid.Empty
? null
: ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));

View file

@ -293,7 +293,7 @@ public class TradeMenu : MonoBehaviour
x => () =>
{
if (x.IsHull)
return GameManager.DockedEntity.Children.Count(s => s.Hull.Data.ItemId == x.LegacyId && s is Ship {IsPlayerShip: true}).ToString();
return GameManager.DockedEntity.Children.Count(s => s.Hull.ItemId == x.LegacyId && s is Ship {IsPlayerShip: true}).ToString();
if(x.Item is SimpleCommodity)
return (_targetCargo.ItemsOfType.ContainsKey(x.LegacyId) ? _targetCargo.ItemsOfType[x.LegacyId].Cast<SimpleCommodity>().Sum(s=>s.Quantity) : 0).ToString();
return (_targetCargo.ItemsOfType.ContainsKey(x.LegacyId) ? _targetCargo.ItemsOfType[x.LegacyId].Count : 0).ToString();
@ -301,7 +301,7 @@ public class TradeMenu : MonoBehaviour
x =>
{
if (x.IsHull)
return GameManager.DockedEntity.Children.Count(s => s.Hull.Data.ItemId == x.LegacyId && s is Ship {IsPlayerShip: true});
return GameManager.DockedEntity.Children.Count(s => s.Hull.ItemId == x.LegacyId && s is Ship {IsPlayerShip: true});
if(x.Item is SimpleCommodity)
return _targetCargo.ItemsOfType.ContainsKey(x.LegacyId) ? _targetCargo.ItemsOfType[x.LegacyId].Cast<SimpleCommodity>().Sum(s=>s.Quantity) : 0;
return _targetCargo.ItemsOfType.ContainsKey(x.LegacyId) ? _targetCargo.ItemsOfType[x.LegacyId].Count : 0;
@ -393,12 +393,13 @@ public class TradeMenu : MonoBehaviour
private static AetheriaRuntimeCatalogItem FindTypedTradeItem(ItemInstance item)
{
if (item?.Data == null || item.Data.ItemId == Guid.Empty)
var itemId = item?.ItemId ?? Guid.Empty;
if (itemId == Guid.Empty)
{
return null;
}
return ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(item.Data.ItemId.ToString("D"));
return ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));
}
private static double GetTypedBehaviorNumber(TradeRow row, BehaviorFilter behaviorFilter, AetheriaRuntimeBehaviorFieldMetadata field)
@ -468,7 +469,7 @@ public class TradeMenu : MonoBehaviour
public AetheriaRuntimeCatalogItem TypedItem { get; }
public Guid LegacyId => Item?.Data?.ItemId ?? Guid.Empty;
public Guid LegacyId => Item?.ItemId ?? Guid.Empty;
public string Name => !string.IsNullOrWhiteSpace(TypedItem?.Name) ? TypedItem.Name : "Unknown Item";

View file

@ -417,7 +417,7 @@ public class PropertiesPanel : MonoBehaviour
private static AetheriaRuntimeCatalogItem FindTypedPropertyItem(ItemInstance item)
{
var itemId = item?.Data?.ItemId ?? Guid.Empty;
var itemId = item?.ItemId ?? Guid.Empty;
return itemId == Guid.Empty
? null
: ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));

View file

@ -611,7 +611,7 @@ public class ZoneRenderer : MonoBehaviour
private static AetheriaRuntimeCatalogItem FindTypedZoneItem(ItemInstance item)
{
var itemId = item?.Data?.ItemId ?? Guid.Empty;
var itemId = item?.ItemId ?? Guid.Empty;
return itemId == Guid.Empty
? null
: ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(itemId.ToString("D"));

View file

@ -545,9 +545,10 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
escape `ItemManager`;
gameplay stat modifiers target live behavior instances and package-owned
behavior metadata instead of rebuilding config DTOs for lookup.
`RuntimeItemReference.ItemId` is the only item reference value. The reader
interface for this bridge is named `IRuntimeItemCatalogReader` and exposes
only typed catalog row lookup. Behavior
`ItemInstance.ItemId` is the runtime identity surface; `RuntimeItemReference`
remains only the backing compatibility reference until native typed item
instances replace it. The reader interface for this bridge is named
`IRuntimeItemCatalogReader` and exposes only typed catalog row lookup. Behavior
type selection now uses an explicit runtime catalog map instead of
`UnionAttribute` reflection, and the temporary behavior config constructor
map is keyed by typed behavior payload kind rather than legacy union key.
@ -1052,6 +1053,10 @@ First Aetheria surfaces to publish:
- Done: demote `RuntimeItemReference.Value` to an item-definition id, then
delete `RuntimeItemReference.Projection`; item references no longer carry
hydrated `ItemData` DTOs.
- Done: add `ItemInstance.ItemId` as the direct runtime identity surface and
move live runtime/UI/catalog lookup call sites off `.Data.ItemId`. The old
`Data` field is now compatibility backing state rather than the identity
API callers use.
- Done: delete the dead `ItemManager.GetRuntimeItemProjection<T>` bridge
after loadout generation stopped hydrating selected typed rows into
`EquippableItemData` merely to instantiate equipment.