Prefilter loadouts with typed catalog rows
This commit is contained in:
parent
b9377a112a
commit
1806ba221e
4 changed files with 80 additions and 20 deletions
Binary file not shown.
|
|
@ -73,6 +73,9 @@ abstraction has been deleted; item data projection is routed through
|
|||
`Projection` is not durable state; it is a bridge for surviving simulation and
|
||||
UI code. The remaining single-item bridge is named `GetRuntimeItemProjection<T>`,
|
||||
not catalog entry access.
|
||||
Loadout generation now applies typed catalog prefilters for item category,
|
||||
hardpoint type, shape fit, hull type, and behavior kind before hydrating legacy
|
||||
`ItemData` projections for final simulation compatibility.
|
||||
Surviving item catalog Unity DTOs inherit `RuntimeItemProjectionEntry`; the old
|
||||
`RuntimeCatalogEntry` base name no longer appears in live source. Temporary
|
||||
faction/corporation projections and zone body/orbit DTOs keep local GUID fields
|
||||
|
|
|
|||
|
|
@ -93,7 +93,9 @@ public class LoadoutGenerator
|
|||
|
||||
var emptyShape = entity.UnoccupiedSpace;
|
||||
|
||||
var dockingBayData = RandomItem<DockingBayData>(2, item => item.Shape.FitsWithin(emptyShape, out _, out _));
|
||||
var dockingBayData = RandomItem<DockingBayData>(2,
|
||||
item => item.Shape.FitsWithin(emptyShape, out _, out _),
|
||||
item => FitsWithin(item, emptyShape));
|
||||
if (dockingBayData == null) throw new InvalidLoadoutException("No compatible docking bay found for station!");
|
||||
|
||||
dockingBayData.Shape.FitsWithin(emptyShape, out var cargoRotation, out var cargoPosition);
|
||||
|
|
@ -107,8 +109,10 @@ public class LoadoutGenerator
|
|||
OutfitEntity(entity);
|
||||
|
||||
var cargo = entity.CargoBays.First();
|
||||
IEnumerable<EquippableItemData> inventory = RandomItems<EquippableItemData>(16, 1,
|
||||
data => !(data is HullData hull && hull.HullType != HullType.Ship) && !(data is CargoBayData));
|
||||
IEnumerable<EquippableItemData> inventory = RandomItems<EquippableItemData>(16, 1,
|
||||
data => !(data is HullData hull && hull.HullType != HullType.Ship) && !(data is CargoBayData),
|
||||
item => item.Category != "CargoBayData" && item.Category != "DockingBayData" &&
|
||||
(item.Category != "HullData" || item.HullType == nameof(HullType.Ship)));
|
||||
inventory = inventory
|
||||
.OrderByDescending(item=>item.Shape.Coordinates.Length);
|
||||
foreach (var item in inventory)
|
||||
|
|
@ -129,7 +133,11 @@ public class LoadoutGenerator
|
|||
item.HullType == type);
|
||||
}
|
||||
|
||||
public T[] RandomItems<T>(int count, float sizeExponent, Predicate<T> filter = null) where T : EquippableItemData
|
||||
public T[] RandomItems<T>(
|
||||
int count,
|
||||
float sizeExponent,
|
||||
Predicate<T> filter = null,
|
||||
Predicate<AetheriaRuntimeCatalogItem> typedFilter = null) where T : EquippableItemData
|
||||
{
|
||||
return RuntimeCatalog.EquipmentItems
|
||||
.Where(IsTypedCandidate<T>)
|
||||
|
|
@ -138,7 +146,8 @@ public class LoadoutGenerator
|
|||
Guid.TryParse(item.ManufacturerLegacyId, out var manufacturer) &&
|
||||
manufacturer != Guid.Empty &&
|
||||
(Galaxy.IsPrelude || Galaxy.ContainsFaction(manufacturer) &&
|
||||
(Faction == null || Faction.Allegiance.ContainsKey(manufacturer))))
|
||||
(Faction == null || Faction.Allegiance.ContainsKey(manufacturer))) &&
|
||||
(typedFilter?.Invoke(item) ?? true))
|
||||
.Select(ProjectRuntimeItem<T>)
|
||||
.Where(item => item != null)
|
||||
.Where(item =>
|
||||
|
|
@ -198,17 +207,51 @@ public class LoadoutGenerator
|
|||
: 1;
|
||||
}
|
||||
|
||||
public T RandomItem<T>(float sizeExponent, Predicate<T> filter = null) where T : EquippableItemData
|
||||
public T RandomItem<T>(
|
||||
float sizeExponent,
|
||||
Predicate<T> filter = null,
|
||||
Predicate<AetheriaRuntimeCatalogItem> typedFilter = null) where T : EquippableItemData
|
||||
{
|
||||
return RandomItems(1, sizeExponent, filter).FirstOrDefault();
|
||||
return RandomItems(1, sizeExponent, filter, typedFilter).FirstOrDefault();
|
||||
}
|
||||
|
||||
public T RandomItem<T>(HardpointData hardpoint, float sizeExponent, Predicate<T> filter = null) where T : EquippableItemData
|
||||
{
|
||||
return RandomItem<T>(sizeExponent, item => item.HardpointType == hardpoint.Type &&
|
||||
(filter?.Invoke(item) ?? true) &&
|
||||
item.Shape.FitsWithin(hardpoint.Shape, hardpoint.Rotation, out _) &&
|
||||
item.Shape.Coordinates.Length==hardpoint.Shape.Coordinates.Length);
|
||||
return RandomItem<T>(
|
||||
sizeExponent,
|
||||
item => item.HardpointType == hardpoint.Type &&
|
||||
(filter?.Invoke(item) ?? true) &&
|
||||
item.Shape.FitsWithin(hardpoint.Shape, hardpoint.Rotation, out _) &&
|
||||
item.Shape.Coordinates.Length == hardpoint.Shape.Coordinates.Length,
|
||||
item => FitsHardpoint(item, hardpoint));
|
||||
}
|
||||
|
||||
private static bool FitsHardpoint(AetheriaRuntimeCatalogItem item, HardpointData hardpoint)
|
||||
{
|
||||
return string.Equals(item.HardpointType, hardpoint.Type.ToString(), StringComparison.Ordinal) &&
|
||||
item.OccupiedCells == hardpoint.Shape.Coordinates.Length &&
|
||||
FitsWithin(item, hardpoint.Shape, hardpoint.Rotation);
|
||||
}
|
||||
|
||||
private static bool FitsWithin(AetheriaRuntimeCatalogItem item, Shape target)
|
||||
{
|
||||
return ToShape(item.ShapeWidth, item.ShapeHeight, item.ShapeCells).FitsWithin(target, out _, out _);
|
||||
}
|
||||
|
||||
private static bool FitsWithin(AetheriaRuntimeCatalogItem item, Shape target, ItemRotation rotation)
|
||||
{
|
||||
return ToShape(item.ShapeWidth, item.ShapeHeight, item.ShapeCells).FitsWithin(target, rotation, out _);
|
||||
}
|
||||
|
||||
private static Shape ToShape(int width, int height, IReadOnlyList<AetheriaRuntimeShapeCell> cells)
|
||||
{
|
||||
var shape = new Shape(Math.Max(width, 1), Math.Max(height, 1));
|
||||
foreach (var cell in cells)
|
||||
{
|
||||
shape[int2(cell.X, cell.Y)] = true;
|
||||
}
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
private void OutfitEntity(Entity entity)
|
||||
|
|
@ -256,9 +299,10 @@ public class LoadoutGenerator
|
|||
|
||||
var emptyShape = entity.UnoccupiedSpace;
|
||||
|
||||
var cargoData = RandomItem<CargoBayData>(3, item =>
|
||||
!(item is DockingBayData) &&
|
||||
item.Shape.FitsWithin(emptyShape, out _, out _));
|
||||
var cargoData = RandomItem<CargoBayData>(3,
|
||||
item => !(item is DockingBayData) &&
|
||||
item.Shape.FitsWithin(emptyShape, out _, out _),
|
||||
item => item.Category != "DockingBayData" && FitsWithin(item, emptyShape));
|
||||
if (cargoData == null) throw new InvalidLoadoutException("No compatible cargo bay found for entity!");
|
||||
|
||||
cargoData.Shape.FitsWithin(emptyShape, out var cargoRotation, out var cargoPosition);
|
||||
|
|
@ -269,9 +313,11 @@ public class LoadoutGenerator
|
|||
|
||||
emptyShape = entity.UnoccupiedSpace;
|
||||
|
||||
var capacitorData = RandomItem<GearData>(2, item =>
|
||||
item.Behaviors.Any(b => b is CapacitorData) &&
|
||||
item.Shape.FitsWithin(emptyShape, out _, out _));
|
||||
var capacitorData = RandomItem<GearData>(2,
|
||||
item => item.Behaviors.Any(b => b is CapacitorData) &&
|
||||
item.Shape.FitsWithin(emptyShape, out _, out _),
|
||||
item => item.BehaviorKinds.Contains(nameof(CapacitorData), StringComparer.Ordinal) &&
|
||||
FitsWithin(item, emptyShape));
|
||||
if (capacitorData == null) throw new InvalidLoadoutException("No compatible capacitor found for entity!");
|
||||
|
||||
capacitorData.Shape.FitsWithin(emptyShape, out var capacitorRotation, out var capacitorPosition);
|
||||
|
|
|
|||
|
|
@ -122,7 +122,9 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
commands, not in-client catalog hydration shortcuts. `LoadoutGenerator` also
|
||||
receives the typed runtime catalog and uses it to own
|
||||
item candidate selection before hydrating selected legacy item DTOs by ID for
|
||||
exact fitting checks and behavior construction. The unused `TradeMenuDebug`
|
||||
remaining behavior construction and legacy simulation checks. Hardpoint type,
|
||||
shape fit, station bay fit, hull/category, and behavior-kind prefilters now
|
||||
run against typed catalog rows before any DTO projection is hydrated. The unused `TradeMenuDebug`
|
||||
script has been deleted instead of preserving an old uGUI debug path that
|
||||
turned typed trade rows back into legacy `ItemData` objects. That hydration
|
||||
now comes from typed state, not `AetherDB.msgpack`.
|
||||
|
|
@ -391,7 +393,9 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
marked with project-owned `LegacyPayloadKeyAttribute`, not MessagePack
|
||||
metadata. Item properties
|
||||
manufacturer display is a typed snapshot consumer; loadout generation is the
|
||||
remaining runtime single-ID item projection bridge. The old trade debug UI
|
||||
remaining runtime single-ID item projection bridge, but typed catalog rows own
|
||||
the loadout candidate prefilters for shape, category, hardpoint type, and
|
||||
behavior kind before that bridge is invoked. The old trade debug UI
|
||||
and console `give` command have been deleted. No live caller can enumerate
|
||||
legacy catalog entries or request catalog-shaped single entries through
|
||||
`ItemManager`.
|
||||
|
|
@ -558,6 +562,10 @@ First Aetheria surfaces to publish:
|
|||
- Done: move `LoadoutGenerator` item candidate selection to the typed
|
||||
runtime catalog; legacy item DTO hydration remains only for exact fitting
|
||||
checks and behavior construction.
|
||||
- Done: move loadout hardpoint fit, station bay fit, hull/category, and
|
||||
behavior-kind prefilters ahead of DTO hydration so typed catalog rows
|
||||
reject incompatible candidates before `GetRuntimeItemProjection<T>` is
|
||||
called.
|
||||
- Done: delete unused `TradeMenuDebug`; the old debug uGUI trade path no
|
||||
longer hydrates typed trade rows back into legacy `ItemData` objects.
|
||||
- Done: delete the console `give` command instead of preserving a debug
|
||||
|
|
@ -802,4 +810,7 @@ temporary `AetheriaRuntimeItemCatalog` DTO materializer. The typed state spine
|
|||
and migration quarantine exist; the next cuts should replace surviving
|
||||
projection consumers with typed item facets, typed behavior factories, and Eve
|
||||
surfaces until `GetRuntimeItemProjection<T>` has no caller and the old DTO
|
||||
bridge can be deleted.
|
||||
bridge can be deleted. Loadout generation should keep moving eligibility tests
|
||||
onto `AetheriaRuntimeCatalogItem` rows before projection; any predicate that
|
||||
still needs `ItemData` must earn that dependency by using behavior objects or
|
||||
simulation-only methods that typed facets do not yet expose.
|
||||
|
|
|
|||
Loading…
Reference in a new issue