diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index 5ff6f3078..eb32b0086 100644 Binary files a/.voidbot/state/aetheria.cc and b/.voidbot/state/aetheria.cc differ diff --git a/Assets/Scripts/ServerShared/EntitySerializer.cs b/Assets/Scripts/ServerShared/EntitySerializer.cs index 2401da934..a9679f7c9 100644 --- a/Assets/Scripts/ServerShared/EntitySerializer.cs +++ b/Assets/Scripts/ServerShared/EntitySerializer.cs @@ -90,7 +90,7 @@ public static class EntitySerializer private static void Restore(ItemManager itemManager, Zone zone, EntityPack pack, Entity entity, bool instantiate = false) { entity.Name = pack.Name; - entity.Faction = itemManager.GetCatalogEntry(pack.Faction); + entity.Faction = zone?.Galaxy?.ResolveFaction(pack.Faction); entity.Children = pack.Children.Select(c => { var child = Unpack(itemManager, zone, c, instantiate); diff --git a/Assets/Scripts/ServerShared/Galaxy.cs b/Assets/Scripts/ServerShared/Galaxy.cs index 62012b925..ab249f924 100644 --- a/Assets/Scripts/ServerShared/Galaxy.cs +++ b/Assets/Scripts/ServerShared/Galaxy.cs @@ -467,6 +467,22 @@ public class Galaxy return _containedFactions.Contains(factionID); } + public Faction ResolveFaction(Guid factionID) + { + if (factionID == Guid.Empty) + { + return null; + } + + var faction = Factions.FirstOrDefault(f => f.ID == factionID); + if (faction != null) + { + return faction; + } + + return _allFactions.FirstOrDefault(f => f.ID == factionID); + } + class DijkstraVertex { public float Cost; diff --git a/Assets/Scripts/ServerShared/LoadoutGenerator.cs b/Assets/Scripts/ServerShared/LoadoutGenerator.cs index 667cf6426..3d01e4e61 100644 --- a/Assets/Scripts/ServerShared/LoadoutGenerator.cs +++ b/Assets/Scripts/ServerShared/LoadoutGenerator.cs @@ -137,13 +137,26 @@ public class LoadoutGenerator .WeightedRandomElements(ref Random, item => Faction == null ? 1 : (item.Manufacturer == Faction.ID ? 1 : Faction.Allegiance.ContainsKey(item.Manufacturer) ? Faction.Allegiance[item.Manufacturer] : 0.0f / // Prioritize items from allied manufacturers - (Galaxy?.ContainsFaction(item.Manufacturer) ?? false ? Zone?.Distance[Galaxy.HomeZones[ItemManager.GetCatalogEntry(item.Manufacturer)]] ?? 1 : 1)) * // Penalize distance to manufacturer headquarters + ManufacturerDistancePenalty(item.Manufacturer)) * // Penalize distance to manufacturer headquarters pow(item.Shape.Coordinates.Length, sizeExponent) / // Prioritize larger items pow(item.Price, PriceExponent), // Penalize item price to a controllable degree count ); } + private float ManufacturerDistancePenalty(Guid manufacturer) + { + if (Galaxy == null || Zone == null || !Galaxy.ContainsFaction(manufacturer)) + { + return 1; + } + + var faction = Galaxy.ResolveFaction(manufacturer); + return faction != null && Galaxy.HomeZones.TryGetValue(faction, out var homeZone) + ? Zone.Distance[homeZone] + : 1; + } + public T RandomItem(float sizeExponent, Predicate filter = null) where T : EquippableItemData { return RandomItems(1, sizeExponent, filter).FirstOrDefault(); diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 8881ed3ba..341b2da57 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -85,13 +85,16 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. deserialization only. UI code no longer reaches into `LegacyCatalogBoundary.GetCatalog` directly. - `ItemManager` no longer exposes the raw `ILegacyCatalogReader` as public - gameplay/UI API. Item, faction, loadout, trade, and entity restore consumers + gameplay/UI API. Item, loadout item selection, trade, and give-command consumers now go through narrow `ItemManager.GetCatalogEntry` and `GetCatalogEntries` methods. This does not remove the legacy reader under the hood; it prevents old catalog authority from leaking into every caller that only needs a domain lookup. The item properties UI no longer uses `ItemManager` for manufacturer display; it resolves the manufacturer through - the package-owned `ActionGameManager.RuntimeCatalog` typed snapshot. + the package-owned `ActionGameManager.RuntimeCatalog` typed snapshot. Entity + restore and loadout manufacturer-distance weighting no longer use `ItemManager` + for faction lookup; they resolve factions through the `Galaxy` typed + corporation projection. - `Galaxy` generation no longer accepts `ILegacyCatalogReader` or `ItemManager`. Sector and tutorial generation receive the package-owned typed runtime catalog. `Galaxy` projects typed corporation v2 records into temporary legacy @@ -186,6 +189,9 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. - Galaxy generation no longer reads legacy `Faction` or `NameFile` entries through `ItemManager`; it requires the typed runtime catalog opened from `GameData/aetheria-world.cc`. +- Entity restore and loadout manufacturer-distance weighting no longer read + legacy `Faction` entries through `ItemManager`; they use `Galaxy.ResolveFaction` + over the typed corporation projection. - MessagePack is no longer used as a runtime object-cloning shortcut for `EntitySettings`, and UI/player-settings startup no longer registers the old MessagePack resolver. Resolver registration is confined to legacy catalog @@ -234,8 +240,9 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. `ActionGameManager` has a private boot helper that calls it only to construct `ItemManager`. Runtime code receives narrow `ItemManager` catalog methods where gameplay only needs lookups. `ItemManager` still receives - `ILegacyCatalogReader` until typed runtime catalog boot replaces it; `Galaxy` - has been moved off the legacy reader and now consumes `ItemManager`. + `ILegacyCatalogReader` until typed runtime catalog boot replaces it; `Galaxy`, + entity restore, and faction-distance loadout weighting have been moved off + legacy faction catalog reads. The embedded Unity state package owns the read-only runtime catalog model contract and the read-only known-schema `.cc` catalog opener. `ActionGameManager` now exposes that typed package snapshot at boot. The SDK-style @@ -243,9 +250,8 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. contract for full .NET smokes and Eve surface reads. Neither writes state or owns simulation. The legacy cache no longer has a public mutation surface, but it still materializes `ItemManager`. Item properties manufacturer display is - a typed snapshot consumer; trade debug, galaxy generation, loadout generation, - entity restore, and the give console command still use `ItemManager` legacy - domain materialization. + a typed snapshot consumer; trade debug, loadout item selection, and the give + console command still use `ItemManager` legacy item-domain materialization. - Inputs: Unity gameplay code, legacy catalog files, typed state documents, and CultMesh server state. - Outputs: `Aetheria.State` emits `.cc` state and CultMesh documents. Legacy @@ -397,6 +403,9 @@ First Aetheria surfaces to publish: - Done: move `Galaxy` faction selection and name generation to the typed runtime catalog; legacy `Faction`/`NameFile` catalog entries no longer decide generated sector factions or zone names. + - Done: move entity faction restore and loadout manufacturer-distance + weighting to `Galaxy.ResolveFaction`, so legacy `Faction` catalog entries + no longer decide runtime faction references after generation. - Replace `ActionGameManager` cache bootstrap with the new state runtime. - Convert domain references from GUID/base-class patterns to typed record refs.