-
-
Save tonyN7923/bf1aaf3b8bc8e70169b85da1cd9a95ec to your computer and use it in GitHub Desktop.
Inventory System Plugin - Sample Implementations
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// AddItem | |
FInventoryItemHandle UInventory::AddItem(const FInventoryItemSpecHandle& InSpecHandle, int32 StacksToAdd) | |
{ | |
const FInventoryItemHandle NewHandle = FInventoryItemHandle::GenerateNewHandle(this); | |
check(NewHandle.IsValid()); | |
bool bAddedItem = false; | |
FInventoryItem* Item = nullptr; | |
const TObjectPtr<const UInventoryItemDefinition> Def = InSpecHandle.GetData()->Def; | |
// If stackable, we try finding an existing item to stack it | |
if (Def->bIsStackable) | |
{ | |
// A stackable item should have a max stack count of at least 1 | |
if (Def->MaxStackCount <= 0) | |
{ | |
// If you encounter this, you need to fix your item definition's max stack count. This will return an invalid handle. | |
INVENTORY_SYSTEM_LOG(Error, TEXT("Stackable item def (%s) should have a max stack count of at least 1."), *Def->GetName()); | |
return FInventoryItemHandle(); | |
} | |
Item = InventoryItems.FindItem(Def->Id); | |
// If item is found, add stack | |
if (Item) | |
{ | |
// Force stacks to add to always be at least 1 | |
StacksToAdd = FMath::Max(StacksToAdd, 1); | |
*Item += StacksToAdd; | |
return Item->Handle; | |
} | |
} | |
// If we haven't added item after checking stackable, then we need to create a new inventory item and try adding that | |
if (!bAddedItem) | |
{ | |
FInventoryItem NewItem(NewHandle, *InSpecHandle.GetData()); | |
bAddedItem = InventoryItems.AddItem(NewHandle, NewItem); | |
Item = InventoryItems.FindItem(NewHandle); | |
} | |
if (bAddedItem) | |
{ | |
// We only care about modifying item properties if we are able to add item to inventory. | |
if (Item) | |
{ | |
// Set actual first creation date | |
// The creation date should always be at this point in the execution. The date should not be modified anywhere else. | |
Item->SetFirstCreationDate(FDateTime::UtcNow().ToIso8601()); | |
// Give the next instance id to this item | |
Item->SetInstanceId(++LastItemInstanceId); | |
if (Def->bIsStackable) | |
{ | |
// Force stacks to add to always be at least 1 | |
StacksToAdd = FMath::Max(StacksToAdd, 1); | |
*Item += StacksToAdd; | |
} | |
} | |
AddUsage(Def.Get()); | |
INVENTORY_SYSTEM_LOG(Verbose, TEXT("Added inventory item: %s"), *Item->ToString()); | |
// Broadcast inventory item addition | |
if (OnInventoryItemAddedDelegate.IsBound()) | |
{ | |
OnInventoryItemAddedDelegate.Broadcast(NewHandle); | |
} | |
} | |
else | |
{ | |
// Broadcast addition failure | |
if (OnInventoryItemAddFailedDelegate.IsBound()) | |
{ | |
OnInventoryItemAddFailedDelegate.Broadcast(NewHandle); | |
} | |
} | |
return NewHandle; | |
} | |
// RemoveItem | |
bool UInventory::RemoveItem(const FInventoryItemHandle& InItemHandle, int32 StacksToRemove) | |
{ | |
check(InItemHandle.IsValid()); | |
FInventoryItem* Item = GetInventoryItem(InItemHandle); | |
if (!Item) | |
{ | |
INVENTORY_SYSTEM_LOG(Warning, TEXT("Attempting to remove an invalid item.")); | |
return false; | |
} | |
if (!Item->Spec.Def->bIsRemovable) | |
{ | |
INVENTORY_SYSTEM_LOG(Verbose, TEXT("Remove item failed (Item: %s): Item is not removable."), *Item->ToString()); | |
return false; | |
} | |
if (Item->IsLocked()) | |
{ | |
INVENTORY_SYSTEM_LOG(Verbose, TEXT("Remove item failed (Item: %s): Item is locked."), *Item->ToString()); | |
return false; | |
} | |
if (Item->Spec.Def->bIsStackable) | |
{ | |
// Force stacks to remove to always be at least 1 | |
StacksToRemove = FMath::Max(StacksToRemove, 1); | |
// Change stack count if we're not removing the inventory item entirely | |
if (StacksToRemove < Item->GetStackCount()) | |
{ | |
*Item -= StacksToRemove; | |
return true; | |
} | |
} | |
// Notify removal before we actually remove the item | |
if (Item->Events.OnItemRemoved.IsBound()) | |
{ | |
FInventoryItemRemovalInfo RemovalInfo; | |
RemovalInfo.StackCount = Item->GetStackCount(); | |
Item->Events.OnItemRemoved.Broadcast(RemovalInfo); | |
} | |
INVENTORY_SYSTEM_LOG(Verbose, TEXT("Removing item entirely: %s"), *Item->ToString()); | |
// Remove inventory item entirely | |
return InventoryItems.RemoveItem(InItemHandle); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Wrapper functions for saving and loading inventory data | |
bool UInventorySystemSaveGame::Save(FString InSlotName, int32 InUserIndex) | |
{ | |
PrepareGameSlotValues(InSlotName, InUserIndex); | |
return UGameplayStatics::SaveGameToSlot(this, InSlotName, InUserIndex); | |
} | |
void UInventorySystemSaveGame::SaveAsync(FString InSlotName, int32 InUserIndex, FAsyncSaveGameToSlotDelegate InSavedDelegate) | |
{ | |
PrepareGameSlotValues(InSlotName, InUserIndex); | |
UGameplayStatics::AsyncSaveGameToSlot(this, InSlotName, InUserIndex, InSavedDelegate); | |
} | |
UInventorySystemSaveGame* UInventorySystemSaveGame::Load(bool& bCreatedNew, bool bCreateIfNotExist, FString InSlotName, int32 InUserIndex) | |
{ | |
PrepareGameSlotValues(InSlotName, InUserIndex); | |
bCreatedNew = false; | |
USaveGame* SaveGame = UGameplayStatics::LoadGameFromSlot(InSlotName, InUserIndex); | |
if (!SaveGame) | |
{ | |
if (bCreateIfNotExist) | |
{ | |
SaveGame = UGameplayStatics::CreateSaveGameObject(StaticClass()); | |
bCreatedNew = true; | |
} | |
} | |
return Cast<UInventorySystemSaveGame>(SaveGame); | |
} | |
void UInventorySystemSaveGame::LoadAsync(FString InSlotName, int32 InUserIndex, FAsyncLoadGameFromSlotDelegate InLoadedDelegate) | |
{ | |
PrepareGameSlotValues(InSlotName, InUserIndex); | |
UGameplayStatics::AsyncLoadGameFromSlot(InSlotName, InUserIndex, InLoadedDelegate); | |
} | |
// Helps determine whether to use the save game slot name/user index set on the InventorySystemComponent | |
// or those set in the Developer Settings | |
void UInventorySystemSaveGame::PrepareGameSlotValues(FString& InSlotName, int32& InUserIndex) | |
{ | |
// Return early if slot name is manually provided | |
if (InSlotName.Len() > 0) return; | |
// Slot name is not provided, so we try using slot values from developer settings | |
if (const auto& Settings = GetDefault<UInventorySystemDeveloperSettings>()) | |
{ | |
if (Settings->SlotName.Len() > 0) | |
{ | |
InSlotName = Settings->SlotName; | |
InUserIndex = Settings->UserIndex; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The plugin gives user the ability to add preset items to an inventory upon first-time loading. | |
// An example use-case could be an archive of all characters/items that can be unlocked in a game. | |
void UInventorySubsystem::RegisterComponent(UInventorySystemComponent* InComponent) | |
{ | |
// omitted code... | |
// Load SaveGame for adding preset items | |
InComponent->LoadInventories(); | |
for (const auto& Inventory : InComponent->GetInventories()) | |
{ | |
check(Inventory->IsInitialized()); | |
// Add preset items to inventory | |
if (Inventory->ItemsPreset) | |
{ | |
INVENTORY_SYSTEM_LOG(Verbose, TEXT("Attempting to add preset items for %s"), *Inventory->ToString()); | |
for (const auto& ItemClass : Inventory->ItemsPreset->GetItemClasses()) | |
{ | |
FInventoryItemSpecHandle SpecHandle = InComponent->MakeOutgoingSpec(ItemClass->GetDefaultObject<UObject>()); | |
// Check if item already exists. If so, we skip adding preset item. | |
const bool bItemExists = InComponent->GetCachedSaveGame()->SavedInventoryItems.ContainsByPredicate([SpecHandle](const FInventoryItemSaveData& Data) | |
{ | |
return Data.Id == SpecHandle.GetData()->Def->Id; | |
}); | |
if (bItemExists) continue; | |
FInventoryItemHandle ItemHandle = InComponent->AddItem(SpecHandle); | |
// Calling this BlueprintNativeEvent function allows UInventory blueprint to do any modifications to this added preset item | |
Inventory->ReceivePresetItemAdded(ItemHandle); | |
} | |
} | |
} | |
// Save added preset items | |
InComponent->SaveInventories(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Overloaded += operator (as well as ++, --, -=) for an inventory item handling stacks. | |
FInventoryItem& operator+=(int32 Amount) | |
{ | |
const int32 PreviousStackCount = StackCount; | |
StackCount += Amount; | |
ClampStackCount(); | |
if (StackCount != PreviousStackCount) | |
{ | |
INVENTORY_SYSTEM_LOG(VeryVerbose, TEXT("Added %d stack(s) to inventory item: %s"), StackCount - PreviousStackCount, *ToString()); | |
if (Events.OnItemStackChanged.IsBound()) | |
{ | |
Events.OnItemStackChanged.Broadcast(Handle, StackCount, PreviousStackCount); | |
} | |
} | |
// Broadcast limit reached if adding amount surpasses max stack count | |
if (PreviousStackCount + Amount > Spec.Def->MaxStackCount) | |
{ | |
INVENTORY_SYSTEM_LOG(VeryVerbose, TEXT("Inventory item reached max stack (%d): %s"), Spec.Def->MaxStackCount, *ToString()); | |
if (Events.OnItemStackLimitReached.IsBound()) | |
{ | |
Events.OnItemStackLimitReached.Broadcast(Handle, StackCount); | |
} | |
} | |
return *this; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment