From 1fadd56464e4ee49afbaa8a2fff9a4307bd28850 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 16 Jun 2026 04:27:07 +0100 Subject: [PATCH] Route loot pickup through checkpoints --- .voidbot/state/aetheria.cc | Bin 144850 -> 145232 bytes Aetheria.State.Verify/Program.cs | 56 +++++++++++++++++++ Assets/Scripts/Gameplay/ActionGameManager.cs | 17 ++++++ Assets/Scripts/Gameplay/ShieldManager.cs | 3 +- docs/aetheria-perfect-machine-map.md | 9 ++- 5 files changed, 83 insertions(+), 2 deletions(-) diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index fe58beeda4f0dcb655209abccdb7b7f68fe4d535..fc4964d6ff00da048bca4adcaaa8a466c0b3290c 100644 GIT binary patch delta 1292 zcmZ8hO^6&t6n1)(WwRi}OcY|&yj8>6z(^`{sp9*|{e#Hdhv;G!Qd-K8b-!&Y5OXfRMnY=%9kGVg@x(qB;@+rjQz*K9aq4 zqe^90XfPp`&!fZ;<6aXH01c6zk0levf0uH?qRp?DLWVqef(YV(h3taDSSq8zwunj; z8M|5{m|>{1*MGWm&Yzc2#EfJ5R6+D0bVEi%&_*iD%~2>2Y%^(_0}8`TI^eua((JR} z?p=c}GokHBv}MRG)}g*?|9ykPiQfEmJ3)mJVOAl54v8@4#3Bw2qB4r5u#F0_+Az)` zKy8#v`-Hku@u1X1+Gfp4CZH2aQR{ZXEtV5?5VVALViJWwITB%G(4kE1TVaEVn6``q zE0I|u+Wu+#oLa@hXC8CraY8Y2B&2e9F-L=-5-UGKkju~8u%M$z9D>=I#!PhbiGM14 z*SDCavdcFb)Hs zi$PH~O99(_^kFUtN{NN#<1s8_NLHCt*%v?T%c|er)-)k8u##YcVA2@3&X0|m81yd_ zK|`q)v8`|1a_+^q6}C4QGBWdMw*U04z3S5X`0VT=QmF!6#5?ppG4`38hX5lvoKF@- z7%&SAG%>)Lz~27nei=D_{3N#Rhlc+TT@iG_u#Z0&A7}TLVb|Feepin!U$14?UmDF< zTa|l8`*vh={J_+)`QZe`#ExTt96OeTF4?+~w(9{#=ASN9s=GhLRTiM2*w=j~-?&(L zw)e&>H)ryjuU6{$m$!R&?|xy?s2E3?jL=)e)b(hI@an7z2C3BIePyGTUO@sf6M(5DeXOEeO%wOQn_@sI6MVL zwlT7AaE2PqMsbGnziZy-!`egTwHnPgAN1s~UR<%f{A?Xr|7*(oao2eN?loGiv;P9U C@7q5B delta 981 zcmZWoOK2295Va>U#)uL@jekod2X%Li@fSY_BZ7+SKcd8g_RiMsw%gM^cK7bO9td{@gU@|-o%49K@E6NJa`cV!Oua@*6b#WAPhA9y5`lZS5NRYsf>bAcrMh71}^qsJx|@8szFBw-MXf*OPhZOD=+sY)QQG2TRRS8l+Z5G(bOL z6lVM4v+vogcrNpnj2Yxa;7TYAxO#|ru#P*M0>5IaNGGaFkOJ#-$xRBlY6YYX7SN#b z5efn&QRIa|-^qFkY9%2Nq^M$Brv*G8O5qC%0E0zg9m%1r>p;sEBPbrAg^DE!xjGyp z6$f-)5WC4dcBrh->ci9MTz0{jXjN=5U-2p0X&6T0GP_NZGm%I)Xjzpm@IX+~67g{3 zm+ISL*OYnI2`Rzn#t6#nYgG`%E+J>cynGQs*A!9!Hwbx@ z=;BKiwxIvcK|Jq(8ezefbcIR_G)ekbp3{l(i&v%+k&4nu$wQ+5PjcM*l`Xop+*`T~ zgtI6MnI~NNC2fvk!G4-{`tp71!G~?G^$dLm)B!hizs3q+w5e|cN z8S5~7c;BS*k_eklHN}&Oz79g}L{3QI>JS61}*&w6SP9d$YRtDON>3 z8KqWXBaN|j?Bw=%;xd~T-zc+HeVazs4{aNyZ%bywhV`Ql950dqwZ`i*yW2H>=Fru9 zjcbP8droWA6TRzLIJHiL&6SJoTJJ?0+B~^)UGa&Or>9_IrR9vjq`XUQsJV-cD{1fL J%r-VO`WN9aX>tGn diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 3bee98f0e..9f837560f 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -19,6 +19,7 @@ RequireHullConductivityCommitAuthority(root); RequireInventoryEntityRenameCommitAuthority(root); RequireWeaponGroupCommitAuthority(root); RequireInventoryDoubleClickTransferCommitAuthority(root); +RequireLootPickupCommitAuthority(root); RequireTradePurchaseCommitAuthority(root); RequireInventoryLoadoutRestoreCommitAuthority(root); RequireDockedCurrentShipCommitAuthority(root); @@ -1106,6 +1107,61 @@ static void RequireTradePurchaseCommitAuthority(string root) } } +static void RequireLootPickupCommitAuthority(string root) +{ + var actionGameManagerPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "ActionGameManager.cs"); + var actionGameManager = File.Exists(actionGameManagerPath) + ? File.ReadAllText(actionGameManagerPath) + : throw new InvalidOperationException("Cannot verify loot pickup authority; ActionGameManager.cs is missing."); + + var requiredSymbols = new[] + { + "CommitLootPickup", + "QueueRunCheckpoint(\"loot-pickup\")", + ".TryStore(item)" + }; + + var missingSymbols = requiredSymbols + .Where(symbol => !actionGameManager.Contains(symbol, StringComparison.Ordinal)) + .ToArray(); + + if (missingSymbols.Length > 0) + { + throw new InvalidOperationException( + "Loot pickup no longer has a gameplay-owned checkpoint commit primitive: " + + string.Join(", ", missingSymbols)); + } + + var shieldManagerPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "ShieldManager.cs"); + var shieldManager = File.Exists(shieldManagerPath) + ? File.ReadAllText(shieldManagerPath) + : throw new InvalidOperationException("Cannot verify loot pickup authority; ShieldManager.cs is missing."); + + var forbiddenSymbols = new[] + { + ".TryStore(", + ".CargoBays.Any(" + }; + + var hits = File.ReadLines(shieldManagerPath) + .Select((line, index) => new { LineNumber = index + 1, Line = line }) + .Where(line => forbiddenSymbols.Any(symbol => line.Line.Contains(symbol, StringComparison.Ordinal))) + .Select(line => $"{Path.GetRelativePath(root, shieldManagerPath)}:{line.LineNumber}: {line.Line.Trim()}") + .ToArray(); + + if (hits.Length > 0) + { + throw new InvalidOperationException( + "ShieldManager still owns direct loot cargo mutation: " + + string.Join("; ", hits)); + } + + if (!shieldManager.Contains("ActionGameManager.Instance.CommitLootPickup", StringComparison.Ordinal)) + { + throw new InvalidOperationException("ShieldManager no longer routes loot pickup through ActionGameManager."); + } +} + static void RequireInventoryLoadoutRestoreCommitAuthority(string root) { var actionGameManagerPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "ActionGameManager.cs"); diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index c065e065f..7acac87c9 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -1570,6 +1570,23 @@ public class ActionGameManager : MonoBehaviour return true; } + public bool CommitLootPickup(Entity entity, ItemInstance item) + { + if (entity == null || item == null) + return false; + + foreach (var cargoBay in entity.CargoBays) + { + if (!cargoBay.TryStore(item)) + continue; + + QueueRunCheckpoint("loot-pickup"); + return true; + } + + return false; + } + public bool CommitCargoItemTransfer( EquippedCargoBay origin, EquippedCargoBay destination, diff --git a/Assets/Scripts/Gameplay/ShieldManager.cs b/Assets/Scripts/Gameplay/ShieldManager.cs index fc24c8b12..447b4b5b5 100644 --- a/Assets/Scripts/Gameplay/ShieldManager.cs +++ b/Assets/Scripts/Gameplay/ShieldManager.cs @@ -25,7 +25,8 @@ public class ShieldManager : MonoBehaviour var mine = other.collider.GetComponent(); if (itemPickup) { - if (Entity.CargoBays.Any(c => c.TryStore(itemPickup.Item))) + if (ActionGameManager.Instance != null && + ActionGameManager.Instance.CommitLootPickup(Entity, itemPickup.Item)) { // TODO: Pickup notification! Destroy(itemPickup.gameObject); diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 551c58a34..3be2d7a57 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -1409,6 +1409,11 @@ First Aetheria surfaces to publish: `ActionGameManager.CommitDockedCurrentShip`. `InventoryPanel` may request a selected docked player ship and update the button color, but gameplay owns `CurrentEntity`, `DockingBay.DockedShip`, and the typed checkpoint. + - Done: route loot pickup through `ActionGameManager.CommitLootPickup`. + `ShieldManager` may detect the collision and destroy the world pickup + after a successful commit, but it no longer stores items into cargo bays + directly; gameplay owns the cargo mutation and queues the typed run + checkpoint. - Replace the next concrete uGUI screen with an Eve-owned surface. - Move the staged packages into the Eve repo once its worktree is clean, then import them back into Aetheria from Eve instead of carrying a local copy. @@ -1742,7 +1747,9 @@ credit changes, cargo transfer, ship creation, and checkpoint. Runtime loadout restore follows the same rule: UI requests restoration, gameplay owns instantiation, credits, dock assignment, current entity, and checkpoint. Docked current-ship selection follows the same rule: UI requests selection, gameplay -owns `CurrentEntity`, `DockingBay.DockedShip`, and checkpoint. Any +owns `CurrentEntity`, `DockingBay.DockedShip`, and checkpoint. Loot pickup +follows the same rule: collision code reports the pickup opportunity, gameplay +owns cargo storage and checkpoint. Any predicate that still needs legacy DTO objects must earn that dependency by using behavior objects or simulation-only methods that typed facets do not yet expose.