// Copyright ExByte Studios. Item Interaction Ecosystem. #pragma once #include "CoreMinimal.h" #include "GameplayTagContainer.h" #include "ItemEnums.h" #include "ItemRuntimeProperties.h" #include "ItemLockData.h" #include "ItemInstanceData.generated.h" /** * Runtime representation of a single item instance (section 4.3). * * Deliberately tiny: it never copies the static FItemDefinitionRow - it only * stores the stable ItemId plus this instance's mutable state. Resolve the * definition through UItemDatabaseSubsystem when you need icons/meshes/flags. */ USTRUCT(BlueprintType) struct ITEMCORE_API FItemInstanceData { GENERATED_BODY() /** Globally unique handle for this concrete stack/instance. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item") FGuid InstanceId; /** Stable design-time id (e.g. "ammo_9mm"). Never a localized string. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item") FName ItemId; UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item") int32 Quantity = 1; UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item") FGameplayTagContainer StateTags; UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item") FItemRuntimeProperties RuntimeProperties; UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item") FGuid CurrentContainerId; UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item") int32 SlotIndex = INDEX_NONE; UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item") EItemLocationType LocationType = EItemLocationType::None; /** Bumped when the item schema changes; drives save migration (section 26.2). */ UPROPERTY(EditAnywhere, BlueprintReadOnly, SaveGame, Category = "Item") int32 ItemDataVersion = 1; /** Transient server-side lock; not saved (section 20). */ UPROPERTY(BlueprintReadOnly, Transient, Category = "Item") FItemLockData Lock; FItemInstanceData() = default; explicit FItemInstanceData(FName InItemId, int32 InQuantity = 1) : InstanceId(FGuid::NewGuid()) , ItemId(InItemId) , Quantity(InQuantity) { } bool IsValid() const { return !ItemId.IsNone() && InstanceId.IsValid() && Quantity > 0; } void EnsureInstanceId() { if (!InstanceId.IsValid()) { InstanceId = FGuid::NewGuid(); } } /** * Two instances can merge into one stack only when they are the same item, * carry identical per-instance state and neither is locked. MaxStack is * enforced by the inventory layer which has access to the definition. */ bool CanStackWith(const FItemInstanceData& Other) const { return ItemId == Other.ItemId && !Lock.bLocked && !Other.Lock.bLocked && StateTags == Other.StateTags && RuntimeProperties == Other.RuntimeProperties; } bool operator==(const FItemInstanceData& Other) const { return InstanceId == Other.InstanceId; } bool operator!=(const FItemInstanceData& Other) const { return InstanceId != Other.InstanceId; } };