Placement Strategies
Each inventory chooses a strategy that decides how items occupy and merge in slots. The strategy is selected in the Inspector and applies to every add and move.
A strategy is read-only: it answers "where may this item go and how much fits?" by producing placement candidates. It never adds, removes, or mutates anything — the transfer engine performs all mutation. See Transfer Pipeline.
Strategy Comparison
| Slot 1 | Slot 2 | Slot 3 | Behavior | |
|---|---|---|---|---|
| Unique | Sword | Shield | Potion | One item = one slot |
| Stackable | Potion x5 | Potion x3 | Shield | Identical items are automatically stacked |
| Separable Stacks | Squad x10 | Squad x20 | --- | Stacks are independent, merge on request |
When to Use What
flowchart TD
Q1{"Should items stack?"}
Q1 -->|No| UNI["Unique"]
Q1 -->|Yes| Q2{"Do stacks merge automatically?"}
Q2 -->|Yes| STK["Stackable"]
Q2 -->|No| SEP["Separable Stacks"]
Unique
Each item occupies exactly one slot; stacking is not supported. When several instances are transferred, each one takes a separate slot.
flowchart TD
A["Item"] --> B{"Slot free?"}
B -->|Yes| C["Place"]
B -->|No| D["Look for another free slot"]
D --> E{"Found?"}
E -->|Yes| C
E -->|No| F["Reject"]
Typical use: equipment inventory, collection of unique artifacts.
Stackable
Identical items are automatically combined into one stack. When adding, the strategy first offers a merge candidate for an existing stack of the same item, then a create candidate for a free slot.
flowchart TD
A["Item"] --> B{"Same item already in a slot?"}
B -->|Yes| C["Merge stacks"]
B -->|No| D{"Free slot?"}
D -->|Yes| E["Create stack"]
D -->|No| F["Reject"]
Typical use: consumables (potions, arrows), resources.
Separable Stacks
Items can stack but are not merged automatically. You can keep several stacks of the same item in different slots. A merge happens only on an explicit drop onto the same item, when merging is allowed.
flowchart TD
A["Item"] --> B{"Empty slot?"}
B -->|Yes| C["Create a new stack"]
B -->|No| D{"Same item + merge allowed?"}
D -->|Yes| E["Merge"]
D -->|No| F["Reject"]
Typical use: Heroes of Might & Magic style (squads with independent stacks).
Slot management (fixed vs dynamic)
Strategy decides placement; slot management decides whether the slot set is fixed or can grow and shrink. It is a separate setting on the inventory.
FixedSlotManagementSettings— a fixed number of slots.DynamicSlotManagementSettings— creates new slots as needed (up to a limit), keeps a minimum number of free slots, and removes excess empties on removal.
Dynamic slots work with any of the three strategies. The strategy only returns a
NewDynamicSlot candidate when growth is allowed; the engine drives the actual
slot creation and removal through IDynamicSlotLifecycle.
Inspector Configuration
| Parameter | Values | Description |
|---|---|---|
| Inventory Strategy | UniqueItemStrategy / StackableItemStrategy / SeparableStacksStrategy |
Placement strategy, selected through [SerializeReference] |
| Slot Management | FixedSlotManagementSettings / DynamicSlotManagementSettings |
Slot lifecycle mode, selected through [SerializeReference] |
| Max Slots | number | Maximum slots (Dynamic only) |
| Max Free Slots | number | Empty slots to keep available (Dynamic only) |
| Drag Amount | All / HalfDown / HalfUp / One / Custom |
How many items to drag from a stack |
Custom Strategy
A custom strategy is read-only: it produces candidates, it does not mutate.
- Inherit from
InventoryStrategyBase. - Mark it
[Serializable]so it appears in theUniversalInventorystrategy picker. - Override the candidate methods:
[Serializable]
public class MyCustomStrategy : InventoryStrategyBase
{
// Validate a directly chosen slot and return its candidate (kind + capacity).
public override bool TryGetCandidate(
IPlacementGeometry geometry,
InventoryAcceptanceRequest request,
BaseSlot targetBaseSlot,
out PlacementCandidate candidate)
{
// Resolve the anchor, decide merge vs create, compute capacity.
// Helpers like TryCreatePlacementCandidate(...) and PassesRules(...) are available.
}
// Enumerate candidates for automatic placement (area drop / auto-transfer).
public override PlacementCandidateSource GetCandidates(
IPlacementGeometry geometry,
InventoryAcceptanceRequest request)
{
// Return a lazy, re-enumerable source of candidates.
}
// How many items the inventory can accept right now (read-only count).
public override int GetAcceptableCount(
IPlacementGeometry geometry,
InventoryAcceptanceRequest request)
{
// Your counting logic.
}
}
Helpers on the base class
PassesRules(slot, item, count) validates slot rules, and
TryCreatePlacementCandidate(...) builds a validated create candidate against the
topology. Geometry (IPlacementGeometry) gives you anchor resolution, bounds and
occupancy checks, and covered slots — your strategy never needs to know it is a grid.
Strategies do not mutate
There are no TryAdd/TryRemove/TryAddToSlot methods anymore. If you are looking
for where items are actually placed, that is the transfer engine, not the strategy.
Custom Slot Management
To create your own slot lifecycle mode:
- Inherit from
SlotManagementSettingsBase. - Mark it
[Serializable]so it appears in the slot-management picker. - Override the hooks you need:
| Hook | Purpose |
|---|---|
CanCreateNewSlot(...) |
May the inventory grow right now? |
GetPotentialNewSlots(...) |
How many more slots could be created |
EnsureFreeSlots(...) |
Pre-create the configured number of free slots |
CanRemoveAnotherSlot(...) |
May an excess empty slot be removed? |
HandleSlotEmptied(...) |
React when a slot becomes empty (e.g. trim) |
Key Classes
| Concept | Class | Description |
|---|---|---|
| Strategy contract | IStrategy |
Read-only: candidates, capacity, max stack size |
| Base class | InventoryStrategyBase |
Shared candidate helpers and rule checks |
| Shared stack base | StackBasedInventoryStrategyBase |
Stack-size and per-item override support for stacking strategies |
| Unique | UniqueItemStrategy |
One item = one slot |
| Stackable | StackableItemStrategy |
Automatic stack merging |
| Separable | SeparableStacksStrategy |
Independent stacks with optional merging |
| Candidate | PlacementCandidate, PlacementCandidateSource |
Topology-neutral merge/create/new-slot intent |
| Ordering | PlacementCandidateOrderer |
Orders candidates for automatic placement only |
| Slot management base | SlotManagementSettingsBase |
Base for fixed, dynamic, and custom slot lifecycle |
| Slot management modes | FixedSlotManagementSettings, DynamicSlotManagementSettings |
Fixed vs growing slot sets |
| Dynamic lifecycle | IDynamicSlotLifecycle |
Engine-driven slot creation/removal |