Constraints

UCrimsonInventoryConstraint is the rule object. The manager component owns a replicated ActiveConstraints array of instanced constraints - add and remove at runtime as the player equips bags, switches loadouts, enters restricted zones, etc. Constraints can also be attached per-item via UCrimsonInventoryItemFragment_Constraint.

Virtual surface

cpp
UCLASS(Abstract, Blueprintable, EditInlineNew, DefaultToInstanced)
class UCrimsonInventoryConstraint : public UObject
{
/** Tag for runtime lookup via GetConstraintsByTag(). */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
FGameplayTag ConstraintTag;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
FText DisplayName;
/** Early veto: can this rule accept the item in principle? */
UFUNCTION(BlueprintNativeEvent)
FCrimsonConstraintCheckResult CanAcceptItem(
const FCrimsonPlacementContext& Context,
const FCrimsonPlacementQuery& Query) const;
/** Position-specific veto: can this rule accept the item at this exact target? */
UFUNCTION(BlueprintNativeEvent)
FCrimsonConstraintCheckResult CanAcceptAt(
const FCrimsonPlacementContext& Context,
const FCrimsonPlacementQuery& Query,
const FCrimsonPlacementTarget& Target) const;
/** Maximum quantity that could be added before this rule would veto. */
UFUNCTION(BlueprintNativeEvent)
int32 CalculateMaxAllowedQuantity(
const FCrimsonPlacementContext& Context,
const FCrimsonPlacementQuery& Query) const;
/** Server-side notification after an item was added; update internal counters here. */
UFUNCTION(BlueprintNativeEvent)
void OnItemAdded(UCrimsonInventoryManagerComponent* Component,
const UCrimsonInventoryItemDefinition* ItemDef,
int32 Quantity, bool bIsNewInstance);
UFUNCTION(BlueprintNativeEvent)
void OnItemRemoved(UCrimsonInventoryManagerComponent* Component,
const UCrimsonInventoryItemDefinition* ItemDef,
int32 Quantity, bool bWasInstanceRemoved);
/** Per-constraint save state (key-value records); mirrors UCrimsonInventoryStateFragment. */
UFUNCTION(BlueprintNativeEvent)
TArray<FCrimsonInventoryStateData> SaveState() const;
UFUNCTION(BlueprintNativeEvent)
void LoadState(const TArray<FCrimsonInventoryStateData>& SavedData);
};
USTRUCT(BlueprintType)
struct FCrimsonConstraintCheckResult
{
bool bPassed = true;
FText FailureReason;
int32 MaxAllowedQuantity = INT32_MAX;
};

Built-in constraints

ClassDescription
UCrimsonInventoryConstraint_SlotSlot-count cap. Reads/updates the tag stack named by MaxSlotsTag (set it to your slots tag; the _FlatSlot layout defaults it to Crimson.Inventory.Constraint.Slots). Stacking-aware: merging into an existing stack does not consume a new slot.
UCrimsonInventoryConstraint_IntTagTag-keyed integer cap. Each added item contributes ValueContributedPerItem to TrackedTag, checked against TrackedTag.Max. Useful for limited-quantity items (max 3 banners, etc.).
UCrimsonInventoryConstraint_FloatTagTag-keyed float cap (weight). When bReadFromFootprintFragment is true (default), reads the per-item contribution from UCrimsonInventoryItemFragment_Footprint::Weight.
UCrimsonInventoryConstraint_TypeFilterItem must match RequiredTags (any) and must not match BlockedTags (any). Implements 'weapons only' pockets, 'no quest items' bags, etc.
UCrimsonInventoryConstraint_GridCellRulePer-cell-region allow/block tags for grid layouts. Gem sockets, 'potions only' belt cells, alchemy slots.
UCrimsonInventoryConstraint_StackLimitAggregates total quantity across the inventory for items matching ItemTagFilter; caps at MaxTotalQuantity. 'Max 5 healing potions of any kind'.
UCrimsonInventoryConstraint_CustomRuleBlueprint-extension base. Game projects subclass and override the virtuals to express any custom rule without C++.

Runtime management API

cpp
// Add a constraint at runtime (e.g. when a bag is equipped)
UCrimsonInventoryConstraint_TypeFilter* AlchemyOnly =
NewObject<UCrimsonInventoryConstraint_TypeFilter>(InventoryComponent);
AlchemyOnly->RequiredTags.AddTag(TAG_Item_Type_Herb);
InventoryComponent->AddConstraint(AlchemyOnly);
// Remove it later (bag unequipped)
InventoryComponent->RemoveConstraint(AlchemyOnly);
// Tag-keyed lookup
TArray<UCrimsonInventoryConstraint*> WeightRules =
InventoryComponent->GetConstraintsByTag(TAG_Inventory_RuleClass_Weight);
// Read all active rules (e.g. for debug UI)
TArray<UCrimsonInventoryConstraint*> Rules = InventoryComponent->GetActiveConstraints();

Simple numeric caps via tag stacks

For simple int/float caps you can skip the constraint object and just mutate the tag-stack containers directly. The slot constraint and float-tag constraint both read these stacks.

cpp
// Read current/max
int32 UsedSlots = InventoryComponent->GetCurrentIntConstraintValue(
TAG_Crimson_Inventory_Constraint_Slots);
int32 MaxSlots = InventoryComponent->GetMaxIntConstraintValue(
TAG_Crimson_Inventory_Constraint_Slots);
float UsedWeight = InventoryComponent->GetCurrentFloatConstraintValue(
TAG_Crimson_Inventory_Constraint_Weight);
float MaxWeight = InventoryComponent->GetMaxFloatConstraintValue(
TAG_Crimson_Inventory_Constraint_Weight);
// Bag Extension: bump the caps at runtime - both replicate automatically
InventoryComponent->ModifyIntConstraintValue(
TAG_Crimson_Inventory_Constraint_Slots, +10);
InventoryComponent->ModifyFloatConstraintValue(
TAG_Crimson_Inventory_Constraint_Weight, +50.f);
Partial add
Call CalculateAddableQuantity(ItemDef, Requested, OutReason) to know how many will actually fit. The async pickup nodes use this internally to split pickups into full/partial/failed.