From d2845591c5edfac6ae75aa4b2da8da87d111c555 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 14 Jun 2026 19:25:46 +0100 Subject: [PATCH] Keep loadout templates as typed state --- .voidbot/state/aetheria.cc | Bin 85434 -> 85711 bytes Assets/Scripts/Gameplay/ActionGameManager.cs | 108 +++++++++++++++---- Assets/Scripts/UI/Menu/InventoryPanel.cs | 41 ++++--- docs/aetheria-perfect-machine-map.md | 4 + 4 files changed, 117 insertions(+), 36 deletions(-) diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index a9beac4df038e2d5876e185ea1b1c0f34c4bc688..157ea75576a35abc4a1cc88f84396dbe9a3f1c93 100644 GIT binary patch delta 1225 zcmZ`(ziSjh6lU^669a+}O_9Q9BLs5CT_iv7)XQChF<|gaViQD|+nr>`?9MDRvllN- z5KFPpEw&H~3l)+kHVN2B3b7Kgj9_o)zu?TIf#*34s^2&2^KK#`6w1Z3}i0t0%AX5p_*K@J!#)~H-J^Ld)WNPwqi8A9b#c9En z)RuuM80PsdHLPITKC=sQLogNjXUYAo>r1r7Y{(8meozBd4B~S(Pz}j%paNmI$3P3} zYeyPgd}Fzkd*8`q%`#kyIqItotN|4^AbG3+xX^~8 zb{dNuO+MWXSilzl$vz`Jj~lQgGAd-?_*g0C({}6>G^w?YRgDCicvH4JrX(V`=P?`U z7~GqO7T3JVU2fXNcZLa#8V)~)@Aocjp+Verfj#0n?gV{ySj?-93KB!}Hu0CF?i$0v zVOgYexUWaS1WGZ3FW8_mXnThLxBE)Mk-AqxbI>D?qCW{N-J>KNlhKrcu&Ih(pCf~f z!|hp#<2CpzTA-vISPZfW*4=>N1aM+;JL%w2nqft_8K78fFTih@o&dSDgGbkKfS2Z) z%;7k(G-!Dmk7=ki<~5*TOaWR6tJFgIoay2=6_z8_B3spA_^o`4JAbD!OX7)~rJlzt z(HS2?D5I0E=21UM!l;4FiI#UlrdV(ML9c7I`-43Ow_ zg$xhuq-!4!BHFBx+5G+(OYWHHM~!^!K;zjZb1FKh6WM{2nHBXsn?ClhNKtnJCgRpPmRz zV+)B<4d6-(j0-NHjUwhwpaqsVV#JlS&Uo&Gk|5|GSqv%xgN_L)WDT@mO$9&5Ip>p+ zHkhD6$-F~9r zjkG}SHy|OK9x90>UsD+`h4KOR!+g5CMDBYTGt(py4HA(dfW!?4VI=deu8eR^aEbuZ zlxd6Y2Om$ie%wUVke_nTGV6lZLdK|(KGOZixiVbyffaD?NN!#EG^Xh~WgPzs?s-jG zgw&yRpJ-mo>8}e{2iMeeY?L g5MBpglqRz$q!U-8$GuXvaku|!^yO$#n;KjB15C-De*gdg diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index 74e970bdd..0add36451 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -166,21 +166,11 @@ public class ActionGameManager : MonoBehaviour try { var loadouts = AetheriaRuntimeCatalogStore.ReadLoadoutTemplates(stateFilePath); - LoadoutBlueprints.Clear(); - foreach (var loadout in loadouts) - { - var blueprint = CreateRuntimeBlueprint(loadout.RootEntity); - if (blueprint == null) - continue; + LoadoutTemplates.Clear(); + LoadoutTemplates.AddRange(loadouts); - if (string.IsNullOrWhiteSpace(blueprint.Name)) - blueprint.Name = loadout.Name; - - LoadoutBlueprints.Add(blueprint); - } - - if (LoadoutBlueprints.Count > 0) - Debug.Log($"Loaded {LoadoutBlueprints.Count} Aetheria Verse loadout templates from typed state."); + if (LoadoutTemplates.Count > 0) + Debug.Log($"Loaded {LoadoutTemplates.Count} Aetheria Verse loadout templates from typed state."); } catch (FileNotFoundException) { @@ -191,6 +181,14 @@ public class ActionGameManager : MonoBehaviour } } + public RuntimeEntityBlueprint CreateRuntimeBlueprint(AetheriaRuntimeLoadoutTemplateSnapshot template) + { + var blueprint = CreateRuntimeBlueprint(template.RootEntity); + if (blueprint != null && string.IsNullOrWhiteSpace(blueprint.Name)) + blueprint.Name = template.Name; + return blueprint; + } + private RuntimeEntityBlueprint CreateRuntimeBlueprint(AetheriaRuntimeEntityLoadoutSnapshot entity) { var hull = CreateEquippableLoadoutItem(entity.Hull); @@ -407,7 +405,7 @@ public class ActionGameManager : MonoBehaviour public ItemManager ItemManager { get; private set; } public Zone Zone { get; private set; } - public List LoadoutBlueprints { get; } = new List(); + public List LoadoutTemplates { get; } = new List(); private readonly (float2 direction, string name)[] _directions = { (float2(0, 1), "Front"), @@ -1281,13 +1279,14 @@ public class ActionGameManager : MonoBehaviour public void QueueRuntimeLoadoutTemplateCommit(RuntimeEntityBlueprint blueprint) { - LoadoutBlueprints.RemoveAll(loadout => loadout.Name == blueprint.Name); - LoadoutBlueprints.Add(blueprint); + var loadout = ProjectLoadoutTemplate(blueprint); + LoadoutTemplates.RemoveAll(template => template.Name == loadout.Name); + LoadoutTemplates.Add(CreateRuntimeLoadoutTemplateSnapshot(loadout)); try { var commit = AetheriaRuntimeStateCommitLog.QueueLoadoutTemplate( RuntimeStateFilePath, - ProjectLoadoutTemplate(blueprint)); + loadout); Debug.Log($"Queued Aetheria Verse loadout commit: {commit.Path}"); } catch (Exception ex) @@ -1296,6 +1295,78 @@ public class ActionGameManager : MonoBehaviour } } + private static AetheriaRuntimeLoadoutTemplateSnapshot CreateRuntimeLoadoutTemplateSnapshot( + AetheriaRuntimeLoadoutTemplateCommit template) + { + var timestamp = DateTime.UtcNow.ToString("O"); + return new AetheriaRuntimeLoadoutTemplateSnapshot( + template.Name ?? "", + template.OwnerPlayerKey ?? "", + CreateRuntimeEntityLoadoutSnapshot(template.RootEntity), + timestamp, + timestamp); + } + + private static AetheriaRuntimeEntityLoadoutSnapshot CreateRuntimeEntityLoadoutSnapshot( + AetheriaRuntimeEntityLoadoutCommit entity) + { + entity ??= new AetheriaRuntimeEntityLoadoutCommit(); + return new AetheriaRuntimeEntityLoadoutSnapshot( + entity.Name ?? "", + entity.Kind ?? "", + ReferenceKey("aetheria.corporation", entity.CorporationLegacyId ?? ""), + CreateRuntimeLoadoutItemSnapshot(entity.Hull), + CreateRuntimeLoadoutItemSlotSnapshots(entity.Equipment), + CreateRuntimeLoadoutItemSlotSnapshots(entity.CargoBays), + CreateRuntimeLoadoutItemSlotSnapshots(entity.DockingBays), + CreateRuntimeCargoBayLoadoutSnapshots(entity.CargoContents), + CreateRuntimeCargoBayLoadoutSnapshots(entity.DockingBayContents), + (entity.DockingBayAssignments ?? Array.Empty()).ToArray(), + (entity.WeaponGroups ?? Array.Empty>()) + .Select(group => (IReadOnlyList)group.ToArray()) + .ToArray(), + (entity.Children ?? Array.Empty()) + .Select(CreateRuntimeEntityLoadoutSnapshot) + .ToArray()); + } + + private static AetheriaRuntimeLoadoutItemSnapshot CreateRuntimeLoadoutItemSnapshot( + AetheriaRuntimeLoadoutItemCommit item) + { + item ??= new AetheriaRuntimeLoadoutItemCommit(); + return new AetheriaRuntimeLoadoutItemSnapshot( + ReferenceKey("aetheria.item_definition", item.ItemDefinitionLegacyId ?? ""), + item.Quality, + item.Durability, + item.Quantity, + item.Enabled); + } + + private static IReadOnlyList CreateRuntimeLoadoutItemSlotSnapshots( + IReadOnlyList slots) + { + return (slots ?? Array.Empty()) + .Select(slot => new AetheriaRuntimeLoadoutItemSlotSnapshot( + slot.X, + slot.Y, + CreateRuntimeLoadoutItemSnapshot(slot.Item))) + .ToArray(); + } + + private static IReadOnlyList CreateRuntimeCargoBayLoadoutSnapshots( + IReadOnlyList cargoBays) + { + return (cargoBays ?? Array.Empty()) + .Select(bay => new AetheriaRuntimeCargoBayLoadoutSnapshot( + CreateRuntimeLoadoutItemSlotSnapshots(bay.Items))) + .ToArray(); + } + + private static string ReferenceKey(string prefix, string legacyId) + { + return string.IsNullOrWhiteSpace(legacyId) ? "" : $"{prefix}:{legacyId}"; + } + private void OnApplicationQuit() => QueueRunCheckpoint("application-quit"); private void QueueRunCheckpoint(string reason) @@ -1852,7 +1923,6 @@ public class ActionGameManager : MonoBehaviour Zone, loadoutGenerator.GenerateShipLoadout(data => string.IsNullOrEmpty(Settings.StartingHullName) || data.Name==Settings.StartingHullName ), true); - // RuntimeEntityBlueprintProjector.InstantiateFromBlueprint(ItemManager, Zone, LoadoutBlueprints.First(x => x.Name == StarterShipTemplate), true); ((Ship) ship).IsPlayerShip = true; ship.Position = float3.zero; ship.Zone = Zone; diff --git a/Assets/Scripts/UI/Menu/InventoryPanel.cs b/Assets/Scripts/UI/Menu/InventoryPanel.cs index 9cf9f12aa..27dbdd75c 100644 --- a/Assets/Scripts/UI/Menu/InventoryPanel.cs +++ b/Assets/Scripts/UI/Menu/InventoryPanel.cs @@ -176,25 +176,10 @@ public class InventoryPanel : MonoBehaviour, IPointerClickHandler GameManager.QueueRuntimeLoadoutTemplateCommit(RuntimeEntityBlueprintProjector.CaptureBlueprint(_displayedEntity)); }); - if (GameManager.LoadoutBlueprints.Any()) + if (GameManager.LoadoutTemplates.Any()) { ContextMenu.AddDropdown("Restore Loadout", - GameManager.LoadoutBlueprints.Select(blueprint => - ( - $"{blueprint.Name} - {blueprint.Price(GameManager.ItemManager):n0}", () => - { - var entity = RuntimeEntityBlueprintProjector.InstantiateFromBlueprint(GameManager.ItemManager, GameManager.Zone, blueprint, true); - entity.SetParent(GameManager.DockedEntity); - GameManager.Credits -= blueprint.Price(GameManager.ItemManager); - GameManager.CurrentEntity = entity; - if(entity is Ship ship) - { - ship.IsPlayerShip = true; - GameManager.DockingBay.DockedShip = ship; - } - Display(entity); - }, blueprint.Price(GameManager.ItemManager) < GameManager.Credits - ))); + GameManager.LoadoutTemplates.Select(LoadoutOption)); } ContextMenu.Show(); @@ -202,6 +187,28 @@ public class InventoryPanel : MonoBehaviour, IPointerClickHandler } } + private (string text, Action action, bool enabled) LoadoutOption(AetheriaRuntimeLoadoutTemplateSnapshot template) + { + var blueprint = GameManager.CreateRuntimeBlueprint(template); + if (blueprint == null) + return ($"{template.Name} - unavailable", () => { }, false); + + var price = blueprint.Price(GameManager.ItemManager); + return ($"{template.Name} - {price:n0}", () => + { + var entity = RuntimeEntityBlueprintProjector.InstantiateFromBlueprint(GameManager.ItemManager, GameManager.Zone, blueprint, true); + entity.SetParent(GameManager.DockedEntity); + GameManager.Credits -= price; + GameManager.CurrentEntity = entity; + if(entity is Ship ship) + { + ship.IsPlayerShip = true; + GameManager.DockingBay.DockedShip = ship; + } + Display(entity); + }, price < GameManager.Credits); + } + private void Update() { if (_displayedEntity != null && TemperatureDisplay) diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index ead150138..bd0ddd1ca 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -1058,6 +1058,10 @@ First Aetheria surfaces to publish: - Done: remove public `GasGiantData`/`SunData` handles from runtime wrappers; wave and light calculations now use copied wrapper fields after construction. + - Done: move saved loadout template ownership off `RuntimeEntityBlueprint`; + `ActionGameManager` keeps typed `AetheriaRuntimeLoadoutTemplateSnapshot` + documents and projects blueprints only for pricing, instantiation, and + commit boundaries. - Done: delete the legacy `Behaviors` lists from `ConsumableItemData` and `EquippableItemData`; item DTOs can no longer carry behavior config state. - Done: move runtime blueprint price aggregation and conductivity restore