Created
June 27, 2017 20:01
-
-
Save Ruhrpottpatriot/04a4060ce0633cfc66a828f575d7d347 to your computer and use it in GitHub Desktop.
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
// Fill out your copyright notice in the Description page of Project Settings. | |
#include "Fantasy.h" | |
#include "InventoryComponent.h" | |
#include "UnrealNetwork.h" | |
// Sets default values for this component's properties | |
UInventoryComponent::UInventoryComponent() | |
{ | |
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features | |
// off to improve performance if you don't need them. | |
PrimaryComponentTick.bCanEverTick = true; | |
bReplicates = true; | |
} | |
void UInventoryComponent::BeginPlay() | |
{ | |
Super::BeginPlay(); | |
} | |
bool UInventoryComponent::IsFreeSlot(int32 index) | |
{ | |
bool isFree = this->Size > index; | |
if (index < this->Items.Num()) | |
{ | |
isFree = this->Items[index].Amount == 0; | |
} | |
return isFree; | |
} | |
bool UInventoryComponent::TryAdd_Implementation(const FName id, const int32 amount, const bool newStack, int32& amountRemaining) | |
{ | |
if (this->GetOwnerRole() != ROLE_Authority) | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Remote tried to access authority function")); | |
amountRemaining = amount; | |
return false; | |
} | |
if (newStack) | |
{ | |
int freeSlot = this->GetFreeSlotIndex(); | |
if (freeSlot == INDEX_NONE) | |
{ | |
UE_LOG(FantasyGeneral, Verbose, TEXT("Inventory full")); | |
amountRemaining = amount; | |
return false; | |
} | |
FInventoryItem* item = this->FindItemInDataTable(id); | |
if (!item) | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Could not find item in data table")); | |
return false; | |
} | |
if (item->StackSize >= amount) | |
{ | |
item->Amount = amount; | |
this->Items.Insert(*item, freeSlot); | |
amountRemaining = 0; | |
this->OnInventoryItemsChanged.Broadcast(); | |
return true; | |
} | |
item->Amount = item->StackSize; | |
return this->TryAdd(id, amount - item->StackSize, true, amountRemaining); | |
} | |
amountRemaining = amount; | |
bool hasAdded = false; | |
for (FInventoryItem item : this->Items) | |
{ | |
if (item.Id == id) | |
{ | |
int32 stackFreeCount = item.StackSize - item.Amount; | |
if (stackFreeCount > 0) | |
{ | |
if (stackFreeCount >= amount) | |
{ | |
item.Amount += amountRemaining; | |
amountRemaining = 0; | |
this->OnInventoryItemsChanged.Broadcast(); | |
return true; | |
} | |
item.Amount = item.StackSize; | |
amountRemaining -= stackFreeCount; | |
this->OnInventoryItemsChanged.Broadcast(); | |
hasAdded = true; | |
} | |
} | |
} | |
if (amountRemaining > 0) | |
{ | |
hasAdded = this->TryAdd(id, amountRemaining, true, amountRemaining) ? true : hasAdded; | |
} | |
return hasAdded; | |
} | |
bool UInventoryComponent::TryRemove_Implementation(const FName id, const int32 amount, int32& removedAmount) | |
{ | |
if (this->GetOwnerRole() != ROLE_Authority) | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Remote tried to access authority function")); | |
removedAmount = 0; | |
return false; | |
} | |
int32 loopRemovedCount = 0; | |
for (int32 i = 0; i < this->Size; i++) | |
{ | |
if (loopRemovedCount >= amount) | |
{ | |
break; | |
} | |
FInventoryItem item = this->Items[i]; | |
if (item.Id != id) | |
{ | |
continue; | |
} | |
if (item.Amount == AMOUNT_INFINITE) | |
{ | |
if (amount == AMOUNT_INFINITE) | |
{ | |
this->RemoveAt(i); | |
removedAmount = AMOUNT_INFINITE; | |
this->OnInventoryItemsChanged.Broadcast(); | |
return true; | |
} | |
removedAmount = 0; | |
return false; | |
} | |
if (item.Amount > amount) | |
{ | |
this->Items[i].Amount -= amount; | |
loopRemovedCount += amount; | |
break; | |
} | |
loopRemovedCount += this->Items[i].Amount; | |
this->RemoveAt(i); | |
} | |
removedAmount = loopRemovedCount; | |
if (loopRemovedCount > 0) | |
{ | |
this->OnInventoryItemsChanged.Broadcast(); | |
return true; | |
} | |
return false; | |
} | |
void UInventoryComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) | |
{ | |
Super::TickComponent(DeltaTime, TickType, ThisTickFunction); | |
} | |
TArray<FInventoryItem> UInventoryComponent::GetInventoryItems_Implementation() | |
{ | |
return this->Items; | |
} | |
FInventoryItem UInventoryComponent::GetItemAt_Implementation(int32 index) | |
{ | |
if (!this->Items.IsValidIndex(index)) | |
{ | |
UE_LOG(FantasyGeneral, Verbose, TEXT("Target index is not valid")); | |
return FInventoryItem(); | |
} | |
return this->Items[index]; | |
} | |
int32 UInventoryComponent::GetFreeSpace_Implementation() | |
{ | |
int32 freeSpace = 0; | |
for (FInventoryItem item : this->Items) | |
{ | |
if (item.Amount == 0) | |
{ | |
freeSpace += 1; | |
} | |
} | |
return freeSpace; | |
} | |
int32 UInventoryComponent::GetFreeSlotIndex_Implementation() | |
{ | |
for (int32 i = 0; i <= this->Items.Num(); i++) | |
{ | |
if (this->IsFreeSlot(i)) | |
{ | |
return i; | |
} | |
} | |
return INDEX_NONE; | |
} | |
int32 UInventoryComponent::CountAmount_Implementation(FName id) | |
{ | |
int32 amount = 0; | |
for (FInventoryItem item : this->Items) | |
{ | |
if (item.Id == id) | |
{ | |
amount += item.Amount; | |
} | |
} | |
return amount; | |
} | |
int32 UInventoryComponent::CountStacks_Implementation(FName id) | |
{ | |
int32 stackCount = 0; | |
for (FInventoryItem item : this->Items) | |
{ | |
if (item.Id == id) | |
{ | |
stackCount += 1; | |
} | |
} | |
return stackCount; | |
} | |
void UInventoryComponent::Clear_Implementation() | |
{ | |
if (this->GetOwnerRole() != ROLE_Authority) | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Remote tried to access authority function")); | |
return; | |
} | |
this->Items.Empty(this->Size); | |
} | |
void UInventoryComponent::Resize(int32 newSize, bool allowShrink) | |
{ | |
if (this->GetOwnerRole() != ROLE_Authority) | |
{ | |
return; | |
} | |
this->Items.SetNum(newSize, allowShrink); | |
this->Size = newSize; | |
this->OnInventoryItemsChanged.Broadcast(); | |
} | |
void UInventoryComponent::SetAmount(const int32 index, const int32 amount) | |
{ | |
if (this->GetOwnerRole() != ROLE_Authority) | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Remote tried to access authority function")); | |
return; | |
} | |
int32 tmpAmount = amount; | |
if (tmpAmount > this->Items[index].StackSize) | |
{ | |
tmpAmount = this->Items[index].StackSize; | |
} | |
this->Items[index].Amount = tmpAmount; | |
this->OnInventoryItemsChanged.Broadcast(); | |
} | |
void UInventoryComponent::RemoveAt_Implementation(int32 index) | |
{ | |
if (this->GetOwnerRole() != ROLE_Authority) | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Remote tried to access authority function")); | |
return; | |
} | |
this->Items.InsertZeroed(index); | |
this->OnInventoryItemsChanged.Broadcast(); | |
} | |
void UInventoryComponent::MoveStack(int32 source, int32 target) | |
{ | |
if (this->GetOwnerRole() != ROLE_Authority) | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Remote tried to access authority function")); | |
return; | |
} | |
if (target > this->Size || target < 0) | |
{ | |
UE_LOG(FantasyGeneral, Verbose, TEXT("Target index is outside the array")); | |
return; | |
} | |
this->Items.Swap(source, target); | |
this->OnInventoryItemsChanged.Broadcast(); | |
} | |
FInventoryItem* UInventoryComponent::FindItemInDataTable(FName rowName) | |
{ | |
static ConstructorHelpers::FObjectFinder<UDataTable>itemTableConst(TEXT("DataTable'/Game/FantasyGame/Data/DT_ItemList.DT_ItemList'")); | |
UDataTable* itemTable = itemTableConst.Object; | |
if (!itemTable) | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Could not find item table")); | |
return nullptr; | |
} | |
static const FString contextString(TEXT("GENERAL")); | |
return itemTable->FindRow<FInventoryItem>(rowName, contextString); | |
} | |
void UInventoryComponent::Init_Implementation(int32 initialSize, const TArray<FInventoryItem>& initialItems) | |
{ | |
if (this->GetOwnerRole() != ROLE_Authority) | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Remote tried to access authority function")); | |
return; | |
} | |
int32 actualSize = initialSize < initialItems.Num() ? initialItems.Num() : initialSize; | |
this->Size = actualSize; | |
this->Items.Init(FInventoryItem(), actualSize); | |
if (initialItems.Num() > 0) | |
{ | |
for (int32 i = 0; i < initialItems.Num(); i++) | |
{ | |
this->Items.Insert(initialItems[i], i); | |
} | |
} | |
this->OnInventoryItemsChanged.Broadcast(); | |
} | |
bool UInventoryComponent::PlaceAtSlot_Implementation(int32 index, FName id, int32 amount, bool overwrite) | |
{ | |
if (this->GetOwnerRole() != ROLE_Authority) | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Remote tried to access authority function")); | |
return false; | |
} | |
if (!(0 <= index && index < this->Size)) | |
{ | |
UE_LOG(FantasyGeneral, Verbose, TEXT("Target index is outside the array")); | |
return false; | |
} | |
FInventoryItem item = this->Items[index]; | |
if (item.Id != "" || item.Id == "None") | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Could not find item in database")) | |
return false; | |
} | |
if (!this->IsFreeSlot(index) && !overwrite) | |
{ | |
UE_LOG(FantasyGeneral, Warning, TEXT("Cannot replace existing item, unless overwrite is true")) | |
return false; | |
} | |
FInventoryItem* dbItem = this->FindItemInDataTable(id); | |
dbItem->Amount = amount; | |
this->Items.Insert(*dbItem, index); | |
this->OnInventoryItemsChanged.Broadcast(); | |
return true; | |
} | |
void UInventoryComponent::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const | |
{ | |
Super::GetLifetimeReplicatedProps(OutLifetimeProps); | |
DOREPLIFETIME(UInventoryComponent, Items); | |
DOREPLIFETIME(UInventoryComponent, Size); | |
} |
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
// Fill out your copyright notice in the Description page of Project Settings. | |
#pragma once | |
#include "Components/ActorComponent.h" | |
#include "InventoryComponent.generated.h" | |
#define AMOUNT_INFINITE -1 | |
// Delegates | |
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FInventoryItemsChanged); | |
UENUM(BlueprintType) | |
enum class EItemType : uint8 | |
{ | |
Miscellaneous, | |
Equipment, | |
Consumable, | |
Currency | |
}; | |
UENUM(BlueprintType) | |
enum class EItemQuality : uint8 | |
{ | |
Poor, | |
Common, | |
Uncommon, | |
Rare, | |
Epic, | |
Legendary | |
}; | |
USTRUCT(BlueprintType) | |
struct FResourceCost | |
{ | |
GENERATED_BODY(); | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
int32 Amount; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
FName ResourceId; | |
}; | |
USTRUCT(BlueprintType) | |
struct FItemCost | |
{ | |
GENERATED_BODY(); | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
int32 Gold; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
TArray<FResourceCost> Resources; | |
}; | |
USTRUCT(BlueprintType) | |
struct FInventoryItem : public FTableRowBase | |
{ | |
GENERATED_BODY(); | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
FName Id; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
bool IsDroppable; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
UStaticMesh* WorldMesh; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
int32 Amount; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
int32 StackSize; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
EItemType Type; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
EItemQuality Quality; | |
//ToDo: Remove buy and sell cost from inventory item struct to reduce network load | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
FItemCost BuyCost; | |
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventory") | |
FItemCost SellCost; | |
FInventoryItem() | |
{ | |
} | |
}; | |
UCLASS(Blueprintable, BlueprintType, meta = (BlueprintSpawnableComponent)) | |
class FANTASY_API UInventoryComponent : public UActorComponent | |
{ | |
GENERATED_BODY() | |
public: | |
// Sets default values for this component's properties | |
UInventoryComponent(); | |
protected: | |
// Called when the game starts | |
virtual void BeginPlay() override; | |
public: | |
// Called every frame | |
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; | |
/** | |
* The total size of the inventory, including empty slots. | |
*/ | |
UPROPERTY(BlueprintReadOnly, Replicated, Category = "Inventory") | |
int32 Size; | |
UPROPERTY(BlueprintAssignable, Category = "Inventory") | |
FInventoryItemsChanged OnInventoryItemsChanged; | |
/** | |
* Clears the inventory of all items, while keeping the size. | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, BlueprintNativeEvent, Category = "Inventory") | |
void Clear(); | |
virtual void Clear_Implementation(); | |
/** | |
* Calculates the how much of an item with the given id is present in the inventory | |
* @param id - The id to count for | |
* @returns The items amount | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, BlueprintPure, Category = "Inventory") | |
int32 CountAmount(FName id); | |
virtual int32 CountAmount_Implementation(FName id); | |
/** | |
* Calculates the number of stacks for an item with the given id in the inventory | |
* @param id - The id to count for | |
* @returns The number of stacks | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, BlueprintPure, Category = "Inventory") | |
int32 CountStacks(FName id); | |
virtual int32 CountStacks_Implementation(FName id); | |
/** | |
* Retrieves the first free slot | |
* @returns Index of the first free slot | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, BlueprintPure, Category = "Inventory") | |
int32 GetFreeSlotIndex(); | |
virtual int32 GetFreeSlotIndex_Implementation(); | |
/** | |
* Calculates the amount of free slots in the inventory | |
* @returns The number of free slots | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, BlueprintPure, Category = "Inventory") | |
int32 GetFreeSpace(); | |
virtual int32 GetFreeSpace_Implementation(); | |
/** | |
* Provides the user with a list of all items in the inventory | |
* @returns The entire inventory | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, BlueprintPure, Category = "Inventory") | |
TArray<FInventoryItem> GetInventoryItems(); | |
virtual TArray<FInventoryItem> GetInventoryItems_Implementation(); | |
/** | |
* Retrieves an item from an index | |
* @returns The item at the index | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, BlueprintPure, Category = "Inventory") | |
FInventoryItem GetItemAt(int32 index); | |
virtual FInventoryItem GetItemAt_Implementation(int32 index); | |
/** | |
* Initialises the inventory to a specific size with the given items. | |
* @param initialSize - The inventory's size | |
* @param initialItems - The items to place in the inventory | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, BlueprintNativeEvent, Category = "Inventory", meta = (AutoCreateRefTerm = "initialItems")) | |
void Init(int32 initialSize, const TArray<FInventoryItem>& initialItems); | |
virtual void Init_Implementation(int32 initialSize, const TArray<FInventoryItem>& initialItems); | |
/** | |
* Checks if the provided slot is empty | |
* @returns True, if the slot is empty, otherwise false | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Inventory") | |
bool IsFreeSlot(int32 index); | |
/** | |
* Moves the stack from the source index to target index, optionally swapping items. | |
* @param source - The first stack index | |
* @param target - The second stack index | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Inventory") | |
void MoveStack(int32 source, int32 target); | |
/** | |
* Places an item with the designated index and amount at the specified slot, | |
* optionally overwriting the existing item | |
* @param index - The index to place the item at | |
* @param id - The id of the item to place | |
* @param amount - The items amount to place | |
* @param overwrite - If an existing item should be overwritten | |
* @returns True if the placement was sucessful, otherwise false | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, BlueprintNativeEvent, Category = "Inventory") | |
bool PlaceAtSlot(int32 index, FName id, int32 amount, bool overwrite = false); | |
virtual bool PlaceAtSlot_Implementation(int32 index, FName id, int32 amount, bool overwrite = false); | |
/** | |
* Removes the stack at the given index | |
* @param index - The index to remove | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, BlueprintNativeEvent, Category = "Inventory") | |
void RemoveAt(int32 index); | |
virtual void RemoveAt_Implementation(int32 index); | |
/** | |
* Resizes the inventory by the given amount, | |
* optionally shrinking the inventory removing all excess items | |
* @param newSize - The new total size of the inventory | |
* @param allowShrink - If the inventory is allowed to shrink | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Inventory") | |
void Resize(int32 newSize, bool allowShrink = false); | |
/** | |
* Sets the items amount at the given index, mot exeeding the maximum stack size | |
* @param index - The stack's index | |
* @param amount - The stack's new amount | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Inventory") | |
void SetAmount(int32 index, int32 amount); | |
/** | |
* Tries to add an item with the given id to then inventory filling up existing slots first, | |
* then creating new slots. The anount that couldn't be added is returned, too. | |
* @param id - The item's id | |
* @param amount - The amount to add | |
* @param newStack - forcibly creates a new stack, even if existing ones could be filled | |
* @param amountRemaining - The amount that couldn't be added to the inventory | |
* @returns True if at least one item was added, otherwise false | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, BlueprintNativeEvent, Category = "Inventory") | |
bool TryAdd(FName id, int32 amount, bool newStack, int32& amountRemaining); | |
bool TryAdd_Implementation(FName id, int32 amount, bool newStack, int32& amountRemaining); | |
/** | |
* Tries to remove an item with the given id from the inventory.. | |
* @param id - The item's id | |
* @param amount - The amount to remove | |
* @param removedAmount - The amount of items that were removed | |
* @returns True if at least one item was removed, otherwise false | |
*/ | |
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Inventory") | |
bool TryRemove(FName id, int32 amount, int32& removedAmount); | |
bool TryRemove_Implementation(FName id, int32 amount, int32& removedAmount); | |
private: | |
UPROPERTY(Replicated) | |
TArray<FInventoryItem> Items; | |
static FInventoryItem* FindItemInDataTable(FName rowName); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment