From 6dae25698019bc54a747afa41450eb22e81a6bb7 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 21 Jun 2026 20:55:22 +0100 Subject: [PATCH] Save loadouts from observed entity state --- Aetheria.State.Verify/Program.cs | 20 +++++- Assets/Scripts/Gameplay/ActionGameManager.cs | 70 +++++++++++++++++++- Assets/Scripts/UI/Menu/InventoryPanel.cs | 2 +- 3 files changed, 86 insertions(+), 6 deletions(-) diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 04c11ced7..c49106abc 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -10586,8 +10586,9 @@ static void RequireInventoryLoadoutSaveRequestAuthority(string root) "AetheriaRuntimeLoadoutTemplateCommands.cs"); var eveBridgePath = Path.Combine(root, "Aetheria.State", "AetheriaEveCommandBridge.cs"); var runtimeStateMapperPath = Path.Combine(root, "Aetheria.State", "AetheriaRuntimeStateMapper.cs"); + var inventoryPanelPath = Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "InventoryPanel.cs"); - var requiredFiles = new[] { eveCommandDocumentPath, loadoutCommandsPath, eveBridgePath, runtimeStateMapperPath }; + var requiredFiles = new[] { eveCommandDocumentPath, loadoutCommandsPath, eveBridgePath, runtimeStateMapperPath, inventoryPanelPath }; var missingFiles = requiredFiles.Where(path => !File.Exists(path)).ToArray(); if (missingFiles.Length > 0) { @@ -10600,10 +10601,15 @@ static void RequireInventoryLoadoutSaveRequestAuthority(string root) var loadoutCommands = File.ReadAllText(loadoutCommandsPath); var eveBridge = File.ReadAllText(eveBridgePath); var runtimeStateMapper = File.ReadAllText(runtimeStateMapperPath); + var inventoryPanel = File.ReadAllText(inventoryPanelPath); var requiredActionSymbols = new[] { - "RequestLoadoutTemplateSave", + "RequestLoadoutTemplateSave(Entity entity)", + "ProjectLoadoutTemplate(Entity entity)", + "ProjectEntityLoadout(Entity entity)", + "ProjectSlots(IEnumerable slots)", + "ProjectCargoBays(IEnumerable bays)", "SendRuntimeLoadoutTemplateCommand(loadout", "private static void SendRuntimeLoadoutTemplateCommand(", "AetheriaRuntimeEveCommands.TrySendLoadoutTemplateCommand", @@ -10622,7 +10628,8 @@ static void RequireInventoryLoadoutSaveRequestAuthority(string root) { "AetheriaRuntimeStateCommitLog.QueueLoadoutTemplate", "TrySendRuntimeLoadoutTemplateCommand(", - "private static bool TrySendRuntimeLoadoutTemplateCommand(" + "private static bool TrySendRuntimeLoadoutTemplateCommand(", + "RequestLoadoutTemplateSave(EntityConstructionBlueprint blueprint)" }; var forbiddenActionHits = forbiddenActionSymbols .Where(symbol => actionGameManager.Contains(symbol, StringComparison.Ordinal)) @@ -10634,6 +10641,13 @@ static void RequireInventoryLoadoutSaveRequestAuthority(string root) string.Join(", ", forbiddenActionHits)); } + if (!inventoryPanel.Contains("GameManager.RequestLoadoutTemplateSave(_displayedEntity)", StringComparison.Ordinal) || + inventoryPanel.Contains("EntityConstructionBlueprintProjector.CaptureBlueprint(_displayedEntity)", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "InventoryPanel still captures a Unity EntityConstructionBlueprint before submitting a typed loadout-template save."); + } + var requiredBridgeSymbols = new[] { "AetheriaRuntimeEveCommandKind.SaveLoadoutTemplate", diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index f11b42685..0b8dafa38 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -617,6 +617,43 @@ public class ActionGameManager : MonoBehaviour }; } + private static AetheriaRuntimeLoadoutTemplateCommit ProjectLoadoutTemplate(Entity entity) + { + return new AetheriaRuntimeLoadoutTemplateCommit + { + Name = entity?.Name ?? "", + OwnerPlayerKey = $"global:aetheria.player_settings.v1", + RootEntity = ProjectEntityLoadout(entity) + }; + } + + private static AetheriaRuntimeEntityLoadoutCommit ProjectEntityLoadout(Entity entity) + { + return new AetheriaRuntimeEntityLoadoutCommit + { + Name = entity?.Name ?? "", + Kind = entity is Ship ? "ship" : entity is OrbitalEntity ? "orbital" : "entity", + FactionKey = entity?.Faction?.FactionKey ?? "", + Hull = ProjectLoadoutItem(entity?.Hull), + Equipment = ProjectSlots(entity?.Equipment), + CargoBays = ProjectSlots(entity?.CargoBays), + DockingBays = ProjectSlots(entity?.DockingBays), + CargoContents = ProjectCargoBays(entity?.CargoBays), + DockingBayContents = ProjectCargoBays(entity?.DockingBays), + DockingBayAssignments = entity?.DockingBays? + .Select(dockingBay => entity.Children.IndexOf(dockingBay.DockedShip)) + .ToArray() ?? Array.Empty(), + WeaponGroups = entity?.WeaponGroups? + .Select(group => (IReadOnlyList)group.items + .Select(item => entity.Equipment.IndexOf(item)) + .ToArray()) + .ToArray() ?? Array.Empty>(), + Children = entity?.Children? + .Select(ProjectEntityLoadout) + .ToArray() ?? Array.Empty() + }; + } + private static AetheriaRuntimeLoadoutItemSlotCommit[] ProjectSlots((int2 position, EquippableItem item)[] slots) { return slots? @@ -629,6 +666,18 @@ public class ActionGameManager : MonoBehaviour .ToArray() ?? Array.Empty(); } + private static AetheriaRuntimeLoadoutItemSlotCommit[] ProjectSlots(IEnumerable slots) + { + return slots? + .Select(slot => new AetheriaRuntimeLoadoutItemSlotCommit + { + X = slot.Position.x, + Y = slot.Position.y, + Item = ProjectLoadoutItem(slot.EquippableItem) + }) + .ToArray() ?? Array.Empty(); + } + private static AetheriaRuntimeCargoBayLoadoutCommit[] ProjectCargoBays((int2 position, ItemInstance item)[][] bays) { return bays? @@ -646,6 +695,23 @@ public class ActionGameManager : MonoBehaviour .ToArray() ?? Array.Empty(); } + private static AetheriaRuntimeCargoBayLoadoutCommit[] ProjectCargoBays(IEnumerable bays) + { + return bays? + .Select(bay => new AetheriaRuntimeCargoBayLoadoutCommit + { + Items = bay.Cargo? + .Select(slot => new AetheriaRuntimeLoadoutItemSlotCommit + { + X = slot.Value.x, + Y = slot.Value.y, + Item = ProjectLoadoutItem(slot.Key) + }) + .ToArray() ?? Array.Empty() + }) + .ToArray() ?? Array.Empty(); + } + private static AetheriaRuntimeLoadoutItemCommit ProjectLoadoutItem(ItemInstance item) { if (item == null) @@ -960,9 +1026,9 @@ public class ActionGameManager : MonoBehaviour return ObservedGalaxy?.Factions == null || faction == null ? -1 : Array.IndexOf(ObservedGalaxy.Factions, faction); } - public void RequestLoadoutTemplateSave(EntityConstructionBlueprint blueprint) + public void RequestLoadoutTemplateSave(Entity entity) { - var loadout = ProjectLoadoutTemplate(blueprint); + var loadout = ProjectLoadoutTemplate(entity); SendRuntimeLoadoutTemplateCommand(loadout, "unity-inventory", "loadout template save"); } diff --git a/Assets/Scripts/UI/Menu/InventoryPanel.cs b/Assets/Scripts/UI/Menu/InventoryPanel.cs index 1b4fdc2dc..786cce99d 100644 --- a/Assets/Scripts/UI/Menu/InventoryPanel.cs +++ b/Assets/Scripts/UI/Menu/InventoryPanel.cs @@ -249,7 +249,7 @@ public class InventoryPanel : MonoBehaviour, IPointerClickHandler { _dropdownCommands[AetheriaRuntimeInventoryDropdownSurfaceBuilder.SaveLoadout] = () => { - GameManager.RequestLoadoutTemplateSave(EntityConstructionBlueprintProjector.CaptureBlueprint(_displayedEntity)); + GameManager.RequestLoadoutTemplateSave(_displayedEntity); }; }