Inventory: per-player items backbone (DR-026 Phase 0)
Data-driven ItemDatabase catalog + per-player replicated InventorySlot ([GhostField] OwnerSendType.All, a StatModifier twin). Harvest reroutes to the firing player's personal inventory (optional ComponentLookup<GhostOwner> in ResourceHarvestSystem; remainder/un-owned -> ledger); the G-key InventoryDepositRequest RPC moves the bag into the shared ledger the build economy spends. Catalog asset (Aether/Ore/Biomass + Stone Pickaxe) wired into the Gameplay subscene; read-only HUD inventory panel. ushort ItemId subsumes ResourceId; byte Category/Tier baked for gear-tier progression. Session-only (no SaveData bump). Play-validated host+client: catalog baked into both worlds, the re-baked player ghost carries InventorySlot with a clean handshake, a server write replicates to the client owner, the deposit RPC round-trips, and the HUD renders catalog names. See DR-026. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,6 +70,9 @@ namespace ProjectM.Client
|
||||
VisualElement _vignette, _downed;
|
||||
float _prevHp, _flash;
|
||||
bool _haveHp;
|
||||
// personal inventory panel (read-only; toggled with I)
|
||||
VisualElement _invPanel, _invList;
|
||||
bool _invOpen;
|
||||
|
||||
EntityQuery _huskQuery;
|
||||
|
||||
@@ -290,6 +293,35 @@ namespace ProjectM.Client
|
||||
_vignette.style.display = DisplayStyle.None;
|
||||
_downed.style.display = DisplayStyle.None;
|
||||
}
|
||||
// ---- Personal inventory (read-only; toggle with I, deposit-all with G via InventoryDepositSendSystem) ----
|
||||
var invKb = UnityEngine.InputSystem.Keyboard.current;
|
||||
if (invKb != null && invKb.iKey.wasPressedThisFrame) _invOpen = !_invOpen;
|
||||
if (_invOpen && found)
|
||||
{
|
||||
EntityManager.CompleteDependencyBeforeRO<InventorySlot>();
|
||||
bool haveItemDb = SystemAPI.TryGetSingleton<ItemDatabase>(out var itemDb);
|
||||
_invPanel.style.display = DisplayStyle.Flex;
|
||||
_invList.Clear();
|
||||
int shown = 0;
|
||||
foreach (var bag in SystemAPI.Query<DynamicBuffer<InventorySlot>>()
|
||||
.WithAll<GhostOwnerIsLocal, PlayerTag>())
|
||||
{
|
||||
for (int i = 0; i < bag.Length; i++)
|
||||
{
|
||||
var slot = bag[i];
|
||||
if (slot.ItemId == 0 || slot.Count <= 0) continue;
|
||||
AddInvRow(ItemName(haveItemDb, itemDb, slot.ItemId), ItemTint(slot.ItemId), slot.Count);
|
||||
shown++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (shown == 0)
|
||||
_invList.Add(HudUi.Text("(empty)", 13, MenuUi.SubCol, TextAnchor.MiddleLeft));
|
||||
}
|
||||
else
|
||||
{
|
||||
_invPanel.style.display = DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- per-frame helpers ----
|
||||
@@ -467,6 +499,7 @@ namespace ProjectM.Client
|
||||
BuildPaletteRow(root);
|
||||
BuildHintBar(root);
|
||||
BuildDowned(root);
|
||||
BuildInventory(root);
|
||||
}
|
||||
|
||||
void BuildVignette(VisualElement root)
|
||||
@@ -724,6 +757,67 @@ namespace ProjectM.Client
|
||||
root.Add(_downed);
|
||||
}
|
||||
|
||||
void BuildInventory(VisualElement root)
|
||||
{
|
||||
_invPanel = HudUi.Panel(PanelDark);
|
||||
_invPanel.style.position = Position.Absolute;
|
||||
_invPanel.style.right = 40; _invPanel.style.bottom = 40;
|
||||
_invPanel.style.minWidth = 224;
|
||||
_invPanel.style.paddingLeft = 14; _invPanel.style.paddingRight = 14;
|
||||
_invPanel.style.paddingTop = 10; _invPanel.style.paddingBottom = 10;
|
||||
_invPanel.style.alignItems = Align.FlexStart;
|
||||
_invPanel.pickingMode = PickingMode.Ignore;
|
||||
|
||||
var header = HudUi.Display("INVENTORY", 16, AetherCyan, TextAnchor.MiddleLeft);
|
||||
header.style.marginBottom = 6;
|
||||
_invPanel.Add(header);
|
||||
|
||||
_invList = new VisualElement();
|
||||
_invList.pickingMode = PickingMode.Ignore;
|
||||
_invPanel.Add(_invList);
|
||||
|
||||
var hint = HudUi.Text("I close - G deposit all at base", 11, MenuUi.SubCol, TextAnchor.MiddleLeft);
|
||||
hint.style.marginTop = 8;
|
||||
_invPanel.Add(hint);
|
||||
|
||||
_invPanel.style.display = DisplayStyle.None;
|
||||
root.Add(_invPanel);
|
||||
}
|
||||
|
||||
void AddInvRow(string name, Color tint, int count)
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.style.flexDirection = FlexDirection.Row;
|
||||
row.style.justifyContent = Justify.SpaceBetween;
|
||||
row.style.minWidth = 196;
|
||||
row.style.marginTop = 2;
|
||||
row.pickingMode = PickingMode.Ignore;
|
||||
row.Add(HudUi.Text(name, 13, tint, TextAnchor.MiddleLeft));
|
||||
row.Add(HudUi.Display("x" + count, 13, Color.white, TextAnchor.MiddleRight));
|
||||
_invList.Add(row);
|
||||
}
|
||||
|
||||
static string ItemName(bool haveDb, ItemDatabase db, ushort id)
|
||||
{
|
||||
if (haveDb && db.Value.IsCreated)
|
||||
{
|
||||
ref var blob = ref db.Value.Value;
|
||||
if (blob.TryGetItem(id, out var def)) return def.Name.ToString();
|
||||
}
|
||||
if (id == ResourceId.Aether) return "Aether";
|
||||
if (id == ResourceId.Ore) return "Ore";
|
||||
if (id == ResourceId.Biomass) return "Biomass";
|
||||
return "Item " + id;
|
||||
}
|
||||
|
||||
static Color ItemTint(ushort id)
|
||||
{
|
||||
if (id == ResourceId.Aether) return AetherCyan;
|
||||
if (id == ResourceId.Ore) return OreAmber;
|
||||
if (id == ResourceId.Biomass) return BioGreen;
|
||||
return new Color(0.85f, 0.85f, 0.9f);
|
||||
}
|
||||
|
||||
static Color ResourceTint(byte resId)
|
||||
=> resId == ResourceId.Aether ? AetherCyan : resId == ResourceId.Biomass ? BioGreen : OreAmber;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user