UCrimsonEquipmentManagerComponent

UPawnComponent that manages all equipped items on a pawn. Owns the replicated FCrimsonEquipmentList, handles ability grants, actor spawning, and save/restore. Add this alongside UCrimsonEquipmentSlotsComponent.

Authority Only
EquipItem, UnequipItem, SwapEquippedItems, MoveEquippedItemToSlot, GatherSaveData, and RestoreFromSaveData are all BlueprintAuthorityOnly. Call them server-side only. Clients receive state via the replicated FCrimsonEquipmentList.

Core Equip API

FunctionDescription
EquipItem(Definition, SourceObject, SpecificSlotIndex)Equips a piece of equipment. Creates the instance, grants ability sets, spawns visual actors, fires OnEquipped. Returns the new instance or nullptr on failure (slot full, CanEquipItem returned false).
UnequipItem(ItemInstance, bForce)Unequips a specific instance. Fires OnUnequipped, destroys actors, revokes ability sets. Pass bForce=true to bypass CanUnequipItem (used during shutdown).
UnequipItemInSlot(SlotTag, SlotIndex)Convenience - unequips whatever is in the given slot/index. No-op if empty.
SwapEquippedItems(ItemA, ItemB)Swaps the slot assignments of two already-equipped items - a pure metadata swap (SlotTag / index), with no ability revoke/re-grant and no lifecycle hooks.
MoveEquippedItemToSlot(Item, NewSlotTag, NewSlotIndex)Moves an item to a new slot without changing any gameplay state (no ability revoke/re-grant). Pure organizational reorder.

Replace Ordering (equipping into an occupied slot)

When EquipItem targets a slot that is already occupied, the incoming item is equipped first and the occupant removed second, so shared resources (skill trees, hotbar contexts) never briefly have zero owners:

StepWhat Happens
1Incoming item equipped: ability sets granted, visual actors spawned, OnEquipped fired.
2OnBeforeSwapUnequip(Incoming, Outgoing) called - override to increment any shared ref-counts.
3Outgoing item unequipped: OnUnequipped fired, actors destroyed, ability sets revoked.

Query API

FunctionReturnsDescription
GetEquipmentInstanceForSlot(SlotTag, SlotIndex)UCrimsonEquipmentInstance*Returns the instance in the given slot/index, or nullptr if empty.
GetFirstInstanceOfType(InstanceType)UCrimsonEquipmentInstance*Returns the first equipped instance of the given class, or nullptr.
GetEquipmentInstancesOfType(InstanceType)TArray<UCrimsonEquipmentInstance*>Returns all equipped instances of the given class.
GetEquipmentList()const FCrimsonEquipmentList&Direct access to the replicated entries array for iteration.

Override Points

BlueprintNativeEventDescription
CanEquipItem(Definition, SourceObject)Return false to block an equip (e.g. "in combat", "insufficient level"). Called authority-only.
CanUnequipItem(ItemInstance)Return false to block an unequip. Bypassed when bForce=true.
OnBeforeSwapUnequip(Incoming, Outgoing)Called when equipping into an occupied slot, after the incoming item is live, before the occupant is removed. Use to manage shared ref-counted resources.

Delegate

DelegateWhen Fired
OnEquipmentChangedAny time an item is equipped or unequipped on authority. Bind in your UI to refresh the equipment display.

Message Broadcast

The plugin ships a FCrimsonEquipmentChangedMessage struct (Instance, SlotTag, bEquipped) and the Crimson.Equipment.Message.Changed tag string as a decoupling convention, but it does not broadcast the message itself - the live notification is the parameterless OnEquipmentChanged delegate above. For a fully decoupled GameplayMessageSubsystem path, broadcast the message yourself from an OnEquipmentChanged handler; the listener side then looks like this:

cpp
#include "CrimsonEquipmentMessagePayloads.h"
// In your UI widget or subsystem - no reference to UCrimsonEquipmentManagerComponent needed
ListenHandle = UGameplayMessageSubsystem::Get(this).RegisterListener<FCrimsonEquipmentChangedMessage>(
FGameplayTag::RequestGameplayTag(TEXT("Crimson.Equipment.Message.Changed")),
this, &UMyEquipmentUI::OnEquipmentChanged);