Concept: Slots and Displacement

A slot is a pair: a gameplay tag and an index. The layout says which pairs exist; the entries say which are occupied. Everything else follows from that.

Resolving an index

When Equip Item runs with Specific Slot Index = -1, the manager reads the definition's slot tag, asks the slots component for that slot's definition, collects the indices already used by entries with the same tag, and takes the first free one below the count.

SituationResult
A free index existsThe lowest free index is used.
Every index is occupiedIndex 0 is targeted, and its occupant is displaced.
The tag is not in the layoutThe equip is refused: an error is logged and Equip Item returns null.
The pawn has no slots component at allA warning is logged, validation is skipped, and index 0 is used - so each equip of that tag replaces the last.

Passing an explicit index skips the search entirely and targets that index, displacing whatever is there. It is not bounds-checked against the layout - an index beyond the slot count simply becomes an occupied position that the auto-search will never choose.

Why the incoming item is equipped first

Equipping into an occupied slot could reasonably remove the old item and then add the new one. This plugin does it the other way round, and the ordering is load-bearing:

StepWhat happens
1The incoming item is fully equipped - abilities granted, actors spawned, OnEquipped run.
2OnBeforeSwapUnequip(Incoming, Outgoing) is called, with both instances alive.
3The outgoing item is force-unequipped - OnUnequipped run, actors destroyed, grants revoked.

Anything both items own - a shared ability, a hotbar context, a weapon-type skill tree - therefore never drops to zero owners during the change. Removing first would release it and re-acquire it a moment later, and every listener would see a spurious teardown.

Because step 3 passes force, Can Unequip Item does not get a vote on a displaced item. An item that must never leave a slot has to refuse the incoming equip, not the outgoing unequip.

Swap and move change nothing but labels

Swap Equipped Items exchanges the slot tag and index of two entries. Move Equipped Item To Slot writes a new tag and index onto one. Neither grants, revokes, spawns, destroys, or runs a lifecycle hook - the items were already equipped and still are.

The consequence is a real trap: on clients, the list's PostReplicatedChange is deliberately empty, so a swap produces no changed message and no source-changed message. Only the look-changed message fires. UI that tracks slot positions has to listen for that and re-read, or it will keep drawing the old arrangement.

The layout is duplicated, not replicated

UCrimsonEquipmentSlotsComponent holds no replicated state. Both machines build the same layout from the same Blueprint defaults, so replicating it would spend bandwidth on data both sides already have.

That works exactly as long as the layout is authored. The moment you call Set Slot Layout at runtime, or override Find Slot Definition with a rule that reads unreplicated state, the two machines can disagree - and the disagreement shows up as a client drawing slots the server refuses to fill. Drive any dynamic layout from state that already replicates.