From 26e9fd7e806d506c8acae26e541b8027fdef7faf Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 14 Jun 2026 04:13:52 +0100 Subject: [PATCH] Use typed behavior payloads in trade rows --- .voidbot/state/aetheria.cc | Bin 61423 -> 62625 bytes Assets/Scripts/UI/Menu/TradeMenu.cs | 87 +++++++++++++++++++++------ docs/aetheria-perfect-machine-map.md | 18 +++--- 3 files changed, 80 insertions(+), 25 deletions(-) diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index 5a38d51818ee3f5701801546df6827d1914963b8..3bfe95ce4c4e2c79c07de608ff0e529ce6898cb7 100644 GIT binary patch delta 1633 zcma)7O=w(I6gHV!n}Db#KPW0^5pXYmn--y)D7dQKyYSriGeZUf!oYiT?$7zo_kHKQ z`}b;}{aw5D<>>hQ!ph=&uy|s5=i`w{_tWTyRb=@@9{FeQuuE&Uj4sU;(IbbR8Qp93 zoG?oUWx&)dL3N}j^9jqrL z6N*q1d=U!5FRB|T;QRh9)`StdR1JHK3z+-ATix4AlT!UeIof=Z== zqD~gY0i8;Ggv1v?Fcu-m;v|=*Yl#dQM8p6tK%&)Kh#FMFWB^+lpGX=rTSPK}rzx~a z%sjPVc$}8$3X)1;K;+u?ZPDN;B&=@v@zl6`eEqPy{`GQK zqj$G`pHvE;P2(mi;JIAqlI~lp)X%%?fc~o-)Y2**s=Dx5@c!qTweHMN6`F?o^z!`3 z8}9eZ6R!`N(Tkn8?*D2Rk92>(++-b28T!Uo{E2(lJ?p;bQ%=2nf2PP?*KHW2D-BWj z0~vPbZa>$!a#kHU1qMT)-}vs=E7R_y+vD@-^f$euhDHNK997jUCR_sk`q=|_p>feY zw!7e_zWMg}Qepfiv0)S|inj6Nt{&sOtLK!|E2q^-2uH+ zv=R|sy$oB5!kLu@I@x!WX{<8ed7R|E<>pRKJG(J6acrd=-is%9u3OSZ15NjCZagva z^o;?uTf?AwKi++GY^;CM{Z@PONbzrJ!9A!=yQ6#GRnvZ9sr&|{{YWwYKlt;#DspVj P@9E9_^}|>{w6yadzp7jD delta 910 zcmZ`&y-yTD6c>&tkZ6JdjWLo}7_C_DF5H1&q46Y;NIVr93X-|qcej)5%&xPu2Wvy3 zm9Y?=L@NstzhW#jXD@{|7RHW94A%;c@ju}0a(59D%lF&&=KVhQt?y+1G&3JwclT!V zli6`=?2`GLIc>gnKkDBH3fbb~ldd7-XYRF;Ts}p5d(L$?KQ%7)ghjLd?f&qMJGP60 zX>P;JLJ3@CdZfxd4-}I%G-sD!O1r28^Pu8K(h03sw#T^G>KeI<$rsLmYZTyMl}oO< zFGFC`fdwjJGrBo64boo*n<*8;9JPo@%%ccUF&!yPd*|57A=rWruM!UfkHzNJ(xBPc z>|F(9whL9o1VYtU|4`m1I7NIwA#*~|aexjS8xnnT+qJJLkW^&`&#E&^ZamW|1;0Rg&)bU?sj&rjU0 zASpz$o_29rOSw_OD08a*XH04SCR7x6YG}P9cU)iH#%lQM$jQdq_sjh!-ea9RNUkO5 zQ&WC0Y_2@JV7#rS$xWOx1KZ~Z=>)QqV>zpsFD$;)tVY8_joIyiuEyiZV{46i^LSrh z>rTdV=11mC>uL&x^lHBR{@lKiVy<;HN6p(Ezco}S77Xk@?^F|wjor<@_Q-gCaStJJ BOyB?j diff --git a/Assets/Scripts/UI/Menu/TradeMenu.cs b/Assets/Scripts/UI/Menu/TradeMenu.cs index 014525caf..8e042e269 100644 --- a/Assets/Scripts/UI/Menu/TradeMenu.cs +++ b/Assets/Scripts/UI/Menu/TradeMenu.cs @@ -259,33 +259,30 @@ public class TradeMenu : MonoBehaviour if (fieldType == typeof(float)) columns.Add((field.Name, 1, x => { - var behavior = x.LegacyEquippableData.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b)); - return () => ActionGameManager.RuntimePlayerSettings.Format((float) field.GetValue(behavior)); + var value = GetTypedBehaviorNumber(x, type, field); + return () => ActionGameManager.RuntimePlayerSettings.Format((float)value); }, x => { - var behavior = x.LegacyEquippableData.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b)); - return (float) field.GetValue(behavior); + return (float)GetTypedBehaviorNumber(x, type, field); })); else if (fieldType == typeof(int)) columns.Add((field.Name, 1, x => { - var behavior = x.LegacyEquippableData.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b)); - return () => ((int) field.GetValue(behavior)).ToString(); + var value = GetTypedBehaviorNumber(x, type, field); + return () => ((int)value).ToString(); }, x => { - var behavior = x.LegacyEquippableData.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b)); - return (int) field.GetValue(behavior); + return (int)GetTypedBehaviorNumber(x, type, field); })); else if (fieldType == typeof(PerformanceStat)) { columns.Add((field.Name, 1, x => { - var behavior = x.LegacyEquippableData.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b)); - return () => ActionGameManager.RuntimePlayerSettings.Format(((PerformanceStat) field.GetValue(behavior)).Max); + var value = GetTypedBehaviorNumber(x, type, field); + return () => ActionGameManager.RuntimePlayerSettings.Format((float)value); }, x => { - var behavior = x.LegacyEquippableData.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b)); - return ((PerformanceStat) field.GetValue(behavior)).Max; + return (float)GetTypedBehaviorNumber(x, type, field); })); } } @@ -404,10 +401,70 @@ public class TradeMenu : MonoBehaviour return ActionGameManager.RuntimeCatalog?.FindItemByLegacyId(item.Data.ItemId.ToString("D")); } + private static double GetTypedBehaviorNumber(TradeRow row, Type behaviorType, FieldInfo field) + { + var key = field.GetCustomAttribute()?.Key; + if (key == null) + { + return 0; + } + + var payload = FindTypedBehaviorPayload(row.TypedItem, behaviorType); + var payloadField = payload?.Fields.FirstOrDefault(candidate => candidate.Key == key.Value); + if (payloadField == null) + { + return 0; + } + + if (field.FieldType == typeof(PerformanceStat)) + { + return payloadField.Value.Children.Count > 1 + ? payloadField.Value.Children[1].NumberValue + : 0; + } + + return payloadField.Value.NumberValue; + } + + private static AetheriaRuntimeBehaviorPayload FindTypedBehaviorPayload(AetheriaRuntimeCatalogItem typedItem, Type behaviorType) + { + if (typedItem == null) + { + return null; + } + + return typedItem.BehaviorPayloads.FirstOrDefault(payload => TypedBehaviorMatches(payload, behaviorType)); + } + + private static bool TypedBehaviorMatches(AetheriaRuntimeBehaviorPayload payload, Type behaviorType) + { + if (string.Equals(payload.Kind, behaviorType.Name, StringComparison.Ordinal)) + { + return true; + } + + var payloadType = ResolveBehaviorType(payload.Kind); + return payloadType != null && behaviorType.IsAssignableFrom(payloadType); + } + + private static Type ResolveBehaviorType(string kind) + { + return string.IsNullOrWhiteSpace(kind) + ? null + : BehaviorTypesByName.TryGetValue(kind, out var type) + ? type + : null; + } + + private static readonly IReadOnlyDictionary BehaviorTypesByName = typeof(BehaviorData) + .GetAllChildClasses() + .Where(type => !string.IsNullOrWhiteSpace(type.Name)) + .GroupBy(type => type.Name) + .ToDictionary(group => group.Key, group => group.First(), StringComparer.Ordinal); + private sealed class TradeRow { private readonly ItemManager _itemManager; - private ItemData _legacyData; public TradeRow(ItemInstance item, AetheriaRuntimeCatalogItem typedItem, ItemManager itemManager) { @@ -420,10 +477,6 @@ public class TradeMenu : MonoBehaviour public AetheriaRuntimeCatalogItem TypedItem { get; } - public ItemData LegacyData => _legacyData ?? (_legacyData = _itemManager.GetData(Item)); - - public EquippableItemData LegacyEquippableData => LegacyData as EquippableItemData; - public Guid LegacyId => Item?.Data?.ItemId ?? Guid.Empty; public string Name => !string.IsNullOrWhiteSpace(TypedItem?.Name) ? TypedItem.Name : "Unknown Item"; diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 4a3e5fc2f..465c5c027 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -155,12 +155,12 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. now comes from typed state, not `AetherDB.msgpack`. The surviving trade menu wraps rows in a typed `TradeRow`: name, mass, price, size, hardpoint type, commodity subtype, hull ownership, and behavior-kind filters read typed - catalog rows before any legacy projection is hydrated. Legacy `ItemData` - hydration is lazy and bounded to behavior reflection columns. Trade buy - decisions for crafted price, ship-hull classification, simple commodity base - price, and simple commodity stack size now read typed catalog rows; the - existing inventory transfer and ship-construction paths still own the actual - runtime mutation. + catalog rows before any legacy projection is hydrated. Dynamic behavior + columns read typed behavior payload fields by legacy payload key instead of + hydrating `BehaviorData` DTOs. Trade buy decisions for crafted price, + ship-hull classification, simple commodity base price, and simple commodity + stack size also read typed catalog rows; the existing inventory transfer and + ship-construction paths still own the actual runtime mutation. The sector properties UI also resolves station/turret/ship counts only through typed hull classifications from the runtime catalog; missing typed hull rows no longer fall back to legacy `HullData`. Loot pickup @@ -643,11 +643,13 @@ First Aetheria surfaces to publish: typed catalog row prefilters before row-level `ItemData` hydration. - Done: move trade menu row presentation for name, mass, price, size, hardpoint type, commodity subtype, hull-owned counts, and behavior-kind - filtering onto typed `TradeRow` fields; legacy `ItemData` hydration is - lazy and bounded to behavior reflection columns. + filtering onto typed `TradeRow` fields. - Done: move trade menu buy price, ship-hull classification, and simple commodity stack-size decisions onto typed catalog rows; inventory transfer and ship construction remain the runtime mutation owners. + - Done: move TradeMenu dynamic behavior columns onto typed behavior payload + fields; the menu no longer hydrates `ItemData` or `BehaviorData` for row + display, filtering, sorting, or buy decisions. - Done: move action-bar consumable drop binding onto typed catalog rows; legacy `ConsumableItemData` resolution is lazy and bounded to activation and active-duration fill until typed consumable effect execution exists.