Skip to content

🪖 Equipment

Adds support for fixed-slot equipment systems — weapons, armor, and accessories — built alongside the Inventory system.

✅ Stored in a dedicated EquipmentContainer (not an InventoryContainer)
✅ Integrates cleanly with Inventory via equip/unequip transactions
✅ Enforces category/tag filters via EquipmentFilter
✅ 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

  1. Add CharacterEquipment to your character prefab.
  2. Configure the layout (slot IDs + filters).
  3. (Optional) Add EquipmentVisualAttacher and 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 / OnUnequipped events
  • Tracks visuals per slotId (one visual per slot)
  • Uses ItemDefinition.equipSocket to 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

  • EquipmentContainer is 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

  • Character → binding inventory to characters
  • Data → categories/tags used by filters
  • ContainersEquipmentContainer internal logic
  • UI → equipment/inventory bridges and debug views