🪖 Equipment¶
Adds support for fixed-slot equipment systems — weapons, armor, and accessories — built alongside the Inventory system.
✅ Stored in a dedicated
EquipmentContainer(not anInventoryContainer)
✅ Integrates cleanly with Inventory via equip/unequip transactions
✅ Enforces category/tag filters viaEquipmentFilter
✅ Supports socket-based visual attachment
Purpose¶
Provides a structured way to equip single items into named slots (Weapon, Helmet, Accessory1, etc.) with:
- strict filter validation
- safe transactional equip/unequip
- optional socket-based equipment visuals
Equipment is stored in a dedicated EquipmentContainer owned by the CharacterEquipment component.
⚠️ Equipment operations are not authority-gated by default.
They are intended to be invoked from authoritative gameplay code (e.g. server-side inventory flows).
Contents¶
| File | Purpose |
|---|---|
CharacterEquipment |
Defines slot layout and manages equip/unequip transactions (local, not service-backed). |
EquipmentFilter |
Authorable ScriptableObject defining slot acceptance rules. |
EquipmentSlot |
Struct storing one equipped item with category/tag restrictions. |
EquipmentVisualAttacher |
Spawns/destroys equipped visuals under named sockets. |
🚀 Quick Start¶
- Add
CharacterEquipmentto your character prefab. - Configure the
layout(slot IDs + filters). - (Optional) Add
EquipmentVisualAttacherand assign the socket root (your rig).
🎮 CharacterEquipment¶
Defines an ordered set of named slots (Weapon, Helmet, Chest, etc.).
Uses its own EquipmentContainer instance — this container is not managed by SceneInventoryService.
Equipping from Inventory¶
var result = characterEquipment.TryEquipFromInventoryResult(
characterInventory, // Inventory source
inventoryIndex: 0,
slotId: "Weapon"
);
if (!result.Success)
Debug.LogWarning($"Equip failed: {result.Code} {result.Message}");
Unequipping Back to Inventory¶
var result = characterEquipment.TryUnequipToInventoryResult(
characterInventory,
"Helmet"
);
Events¶
| Event | Description |
|---|---|
OnEquipped(string slotId, ItemStack item) |
Fired after a successful equip. |
OnUnequipped(string slotId, ItemStack item) |
Fired after a successful unequip. |
Behaviour Highlights¶
- Incoming items are cloned with quantity = 1 (enforced).
- Swapped-out items (if any) are added back to inventory via
TryAddAllResult. - Equip/unequip operations are wrapped in:
EquipmentContainer.DeferEvents()InventoryContainer.DeferEvents()CompTxn(transaction scope)- Any failure triggers a clean rollback of both equipment and inventory.
- Duplicate slot IDs warn in-editor; normalized duplicates cause the last entry to win.
🎯 EquipmentFilter¶
Authorable filter defining what items may be equipped into a slot.
| Field | Description |
|---|---|
requiredCategory |
Required category (normalized via trim + lowercase). |
requiredTags |
All tags must be present. |
forbiddenTags |
None of these tags may be present. |
Runtime behaviour:
- Categories and tags are normalized on OnEnable / OnValidate.
- Required and forbidden tags are stored in
HashSet<string>for fast lookup. - Empty or whitespace values collapse to
null(fast-path).
Usage:
if (filter.Matches(itemDef))
Debug.Log("Fits!");
🧩 EquipmentSlot¶
Simple runtime struct used internally by EquipmentContainer:
public struct EquipmentSlot
{
public string slotId;
public EquipmentFilter filter;
public ItemStack stack;
}
- Exactly one item per slot
- Quantity is always normalized to 1
- Slot IDs are normalized internally (
SlotKeys.Normalize)
🎨 EquipmentVisualAttacher¶
Presentation-only helper that spawns equipped item visuals based on the socket declared in ItemDefinition.
Features¶
- Listens to
OnEquipped/OnUnequippedevents - Tracks visuals per slotId (one visual per slot)
- Uses
ItemDefinition.equipSocketto find a child transform by name - Optionally creates missing sockets under the configured root
- Can preserve authored prefab transforms or apply defaults
- Caches sockets and visuals for fast updates
- Automatically rebuilds the socket cache and resyncs visuals when the rig hierarchy changes (
OnTransformChildrenChanged)
Example:
itemDef.equipVisualPrefab = swordPrefab;
itemDef.equipSocket = "RightHand";
Attach to your character; it auto-resolves CharacterEquipment on Awake if not assigned.
⚠️ Notes & Gotchas¶
EquipmentContaineris separate from Inventory — no stacking logic- Equipment slots always store quantity = 1
- Filter failures return
InvOpCode.FilterMismatch - Transactions ensure full rollback on failure
- Visuals are safely added/removed per slot
- Duplicate
slotIds warn in-editor; normalized duplicates cause last to win
📎 Related¶
- Character → binding inventory to characters
- Data → categories/tags used by filters
- Containers →
EquipmentContainerinternal logic - UI → equipment/inventory bridges and debug views