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:
@@ -40,6 +40,12 @@ namespace ProjectM.Authoring
|
||||
Category = def.Category,
|
||||
Tier = def.Tier,
|
||||
StackMax = def.StackMax,
|
||||
EquipSlot = def.EquipSlot,
|
||||
GrantedAbilityId = def.GrantedAbilityId,
|
||||
Mod0 = ModAt(def, 0),
|
||||
Mod1 = ModAt(def, 1),
|
||||
Mod2 = ModAt(def, 2),
|
||||
Mod3 = ModAt(def, 3),
|
||||
Name = def.DisplayName,
|
||||
};
|
||||
}
|
||||
@@ -48,6 +54,16 @@ namespace ProjectM.Authoring
|
||||
builder.Dispose();
|
||||
AddBlobAsset(ref blob, out _);
|
||||
AddComponent(entity, new ItemDatabase { Value = blob });
|
||||
|
||||
static ItemModSpec ModAt(ItemDefinition def, int i)
|
||||
{
|
||||
if (def.Mods != null && i < def.Mods.Count)
|
||||
{
|
||||
var m = def.Mods[i];
|
||||
return new ItemModSpec { Target = m.Target, Op = m.Op, Value = m.Value };
|
||||
}
|
||||
return new ItemModSpec { Target = 255 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using ProjectM.Simulation;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -27,5 +28,26 @@ namespace ProjectM.Authoring
|
||||
[Min(1)]
|
||||
[Tooltip("Max units that stack in one inventory slot (1 for non-stacking equipment).")]
|
||||
public int StackMax = 999;
|
||||
|
||||
[Header("Equipment (Phase 1)")]
|
||||
[Tooltip("EquipSlotId byte: 0=Weapon, 1=Armor, 2=Trinket, 3=Tool, 255=not equippable.")]
|
||||
public byte EquipSlot = 255;
|
||||
|
||||
[Tooltip("AbilityId granted when equipped in the Weapon slot (0=none): 1=Primary, 2=FastLight, 3=SlowHeavy.")]
|
||||
public byte GrantedAbilityId = 0;
|
||||
|
||||
[Tooltip("Stat modifiers granted while equipped (first 4 used).")]
|
||||
public List<ItemModAuthoring> Mods = new List<ItemModAuthoring>();
|
||||
}
|
||||
|
||||
/// <summary>Designer-facing stat-mod grant on an equippable item; the baker writes the first 4 into ItemDefBlob's inline mod slots.</summary>
|
||||
[System.Serializable]
|
||||
public struct ItemModAuthoring
|
||||
{
|
||||
[Tooltip("StatTarget byte: 0=Damage,1=CooldownTicks,2=Range,3=ProjectileSpeed,4=AutoTargetRange,5=AutoTargetCone,6=MoveSpeed,7=TurnRate,8=MaxHealth.")]
|
||||
public byte Target;
|
||||
[Tooltip("ModOp byte: 0=Flat, 1=PercentAdd, 2=PercentMult.")]
|
||||
public byte Op;
|
||||
public float Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user