Hi,
first of all thank you for your help and sorry for my late answer! I actually was kind of blind and didn’t noticed that it was taking diffent code paths. It’s supposed to first call TryAddToUsedSlot and if that fails to call TryAddToEmptySlot, but never both.
It seems to be due to saving data beyond play sessions, because it’s predictably alternating between calling the two functions mentioned above from session to session.
I actually create the UStorageSlot statically in the contructor and initialize its Count in BeginPlay(...). This is the code…
UInventoryComponent::UInventoryComponent()
{
...
ShotgunShells = CreateDefaultSubobject<USlotStorage>("ShotgunShells");
}
void UInventoryComponent::BeginPlay()
{
Super::BeginPlay();
...
ShotgunShells->Init(this, InventoryLayout.ShotgunShells);
}
void USlotStorage::Init(UInventoryComponent* AInventoryComponent, TArray<int> SlotCapacities)
{
InventoryComponent = AInventoryComponent;
for (int i = 0; i < SlotCapacities.Num(); ++i)
{
FSlot Slot;
Slot.Index = i;
Slot.Class = nullptr;
Slot.Count = 0;
Slot.Max = SlotCapacities[i];
Slots.Add(Slot);
}
}
FStorageInfo
USTRUCT(BlueprintType)
struct FStorageInfo
{
GENERATED_BODY()
FStorageInfo() {};
FStorageInfo(EStorageName StorageName, TObjectPtr<UTexture2D> Icon = nullptr, bool Consumable = true, int Count = 0);
void Update(int ACount, int AIndex, int AMax, bool ASuccess);
// When Pickup is added to an unknown Slot
UPROPERTY() EStorageName StorageName;
UPROPERTY() TObjectPtr<UTexture2D> Icon;
UPROPERTY() bool Consumable = true;
UPROPERTY() int Count = 0;
UPROPERTY() int Index = 0;
UPROPERTY() int Max = 0;
UPROPERTY() bool Success = false;
};
FSlot
USTRUCT()
struct FSlot
{
GENERATED_BODY()
int Index;
TSubclassOf<APickup> Class;
int Count;
int Max;
};
Add(…)
void USlotStorage::Add(APickup* Pickup, FStorageInfo& StorageInfo)
{
UE_LOG(LogTemp, Warning, TEXT("%s -> %d"), *FString(__FUNCTION__), StorageInfo.Count);
TryAddToUsedSlot(Pickup, StorageInfo);
if(!StorageInfo.Success)
{
TryAddToEmptySlot(Pickup, StorageInfo);
}
if(StorageInfo.Success)
{
Pickup->Destroy();
}
}
TryAddToUsedSlot(…)
void USlotStorage::TryAddToUsedSlot(APickup* Pickup, FStorageInfo& StorageInfo)
{
for (FSlot& Slot : Slots)
{
// If there is already an item of that type in a Slot that is not full: Add the item
if(Slot.Class == Pickup->GetClass())
{
if(Slot.Count < Slot.Max)
{
UE_LOG(LogTemp, Warning, TEXT("%s -> %d - %d"), *FString(__FUNCTION__), Slot.Count, StorageInfo.Count);
AddToSlot(Slot, StorageInfo);
return;
}
}
}
}
TryAddToEmptySlot(…)
void USlotStorage::TryAddToEmptySlot(APickup* Pickup, FStorageInfo& StorageInfo)
{
for (FSlot& Slot : Slots)
{
if(Slot.Count == 0)
{
Slot.Class = Pickup->GetClass();
UE_LOG(LogTemp, Warning, TEXT("%s -> %d - %d"), *FString(__FUNCTION__), Slot.Count, StorageInfo.Count);
AddToSlot(Slot, StorageInfo);
return;
}
}
}
GetStorage(…)
UStorage* UInventoryComponent::GetStorage(EStorageName StorageName)
{
switch (StorageName)
{
case EStorageName::ShotgunShells:
return ShotgunShells;
// ...
class THETHIRDPERSONGAME_API UInventoryComponent : public UActorComponent
{
...
private:
UPROPERTY() TObjectPtr<USlotStorage> ShotgunShells;