Equipment: weapon-granted abilities + gear mods (DR-027 Phase 1)

Equipment slots reusing the AbilityRef/StatModifier machinery: EquipmentSlot [GhostField] buffer (index=slot), server-only event-driven EquipSystem (RPC). Weapon -> AbilityRef.Id swaps the attack (prefab + base stats, prediction-correct); gear -> StatModifiers tagged a reserved per-slot EquipSourceId, stripped target-agnostically via RemoveBySourceId. Item mods are INLINE on ItemDefBlob (a nested BlobArray reads empty under the by-value TryGetItem copy). Atomic equip-over swap (no item loss); DefaultAbility restores the unarmed ability on weapon-unequip. Client keys + build-safe hooks; HUD equipment panel + click-to-equip. 4 catalog weapon/gear items wired + re-baked.

Play-validated host+client: weapon equip swaps AbilityRef on both worlds, gear folds into EffectiveCharacterStats, unequip reverses + restores DefaultAbility, all replicated to the owner. See DR-027.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 11:09:25 -07:00
parent 510c12a980
commit 43f355c06b
28 changed files with 709 additions and 6 deletions
@@ -60,6 +60,8 @@ namespace ProjectM.Authoring
// Data-driven stat refs (replace M2's inlined PlayerMoveStats / AbilityStats values).
AddComponent(entity, new CharacterStatsRef { Id = characterId });
AddComponent(entity, new AbilityRef { Id = abilityId });
// Unarmed/base ability restored on weapon-unequip (AbilityRef.Id mutates when a weapon is equipped).
AddComponent(entity, new DefaultAbility { Id = abilityId });
// Effective stats: zeroed at bake, recomputed every predicted tick by StatRecomputeSystem.
AddComponent(entity, new EffectiveAbilityStats());
@@ -69,6 +71,10 @@ namespace ProjectM.Authoring
AddBuffer<StatModifier>(entity);
// Empty replicated personal inventory (server-authoritative; harvest yield + deposit RPC land here).
AddBuffer<InventorySlot>(entity);
// Equipment loadout: one replicated row per slot in FIXED order (buffer index = EquipSlotId), empty.
var equip = AddBuffer<EquipmentSlot>(entity);
for (int s = 0; s < EquipSlotId.Count; s++)
equip.Add(new EquipmentSlot { ItemId = 0 });
// Server-only expiry tracker for timed buffs (paired with a StatModifier by SourceId; not replicated).
AddBuffer<TimedModifier>(entity);