281 lines
8.1 KiB
C++
281 lines
8.1 KiB
C++
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||
|
|
||
|
#include "LyraInventoryManagerComponent.h"
|
||
|
#include "LyraInventoryItemInstance.h"
|
||
|
#include "LyraInventoryItemDefinition.h"
|
||
|
#include "Net/UnrealNetwork.h"
|
||
|
#include "Engine/ActorChannel.h"
|
||
|
|
||
|
#include "NativeGameplayTags.h"
|
||
|
#include "GameFramework/GameplayMessageSubsystem.h"
|
||
|
|
||
|
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_Lyra_Inventory_Message_StackChanged, "Lyra.Inventory.Message.StackChanged");
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// FLyraInventoryEntry
|
||
|
|
||
|
FString FLyraInventoryEntry::GetDebugString() const
|
||
|
{
|
||
|
TSubclassOf<ULyraInventoryItemDefinition> ItemDef;
|
||
|
if (Instance != nullptr)
|
||
|
{
|
||
|
ItemDef = Instance->GetItemDef();
|
||
|
}
|
||
|
|
||
|
return FString::Printf(TEXT("%s (%d x %s)"), *GetNameSafe(Instance), StackCount, *GetNameSafe(ItemDef));
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// FLyraInventoryList
|
||
|
|
||
|
void FLyraInventoryList::PreReplicatedRemove(const TArrayView<int32> RemovedIndices, int32 FinalSize)
|
||
|
{
|
||
|
for (int32 Index : RemovedIndices)
|
||
|
{
|
||
|
FLyraInventoryEntry& Stack = Entries[Index];
|
||
|
BroadcastChangeMessage(Stack, /*OldCount=*/ Stack.StackCount, /*NewCount=*/ 0);
|
||
|
Stack.LastObservedCount = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FLyraInventoryList::PostReplicatedAdd(const TArrayView<int32> AddedIndices, int32 FinalSize)
|
||
|
{
|
||
|
for (int32 Index : AddedIndices)
|
||
|
{
|
||
|
FLyraInventoryEntry& Stack = Entries[Index];
|
||
|
BroadcastChangeMessage(Stack, /*OldCount=*/ 0, /*NewCount=*/ Stack.StackCount);
|
||
|
Stack.LastObservedCount = Stack.StackCount;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FLyraInventoryList::PostReplicatedChange(const TArrayView<int32> ChangedIndices, int32 FinalSize)
|
||
|
{
|
||
|
for (int32 Index : ChangedIndices)
|
||
|
{
|
||
|
FLyraInventoryEntry& Stack = Entries[Index];
|
||
|
check(Stack.LastObservedCount != INDEX_NONE);
|
||
|
BroadcastChangeMessage(Stack, /*OldCount=*/ Stack.LastObservedCount, /*NewCount=*/ Stack.StackCount);
|
||
|
Stack.LastObservedCount = Stack.StackCount;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FLyraInventoryList::BroadcastChangeMessage(FLyraInventoryEntry& Entry, int32 OldCount, int32 NewCount)
|
||
|
{
|
||
|
FLyraInventoryChangeMessage Message;
|
||
|
Message.InventoryOwner = OwnerComponent;
|
||
|
Message.Instance = Entry.Instance;
|
||
|
Message.NewCount = NewCount;
|
||
|
Message.Delta = NewCount - OldCount;
|
||
|
|
||
|
UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(OwnerComponent->GetWorld());
|
||
|
MessageSystem.BroadcastMessage(TAG_Lyra_Inventory_Message_StackChanged, Message);
|
||
|
}
|
||
|
|
||
|
ULyraInventoryItemInstance* FLyraInventoryList::AddEntry(TSubclassOf<ULyraInventoryItemDefinition> ItemDef, int32 StackCount)
|
||
|
{
|
||
|
ULyraInventoryItemInstance* Result = nullptr;
|
||
|
|
||
|
check(ItemDef != nullptr);
|
||
|
check(OwnerComponent);
|
||
|
|
||
|
AActor* OwningActor = OwnerComponent->GetOwner();
|
||
|
check(OwningActor->HasAuthority());
|
||
|
|
||
|
|
||
|
FLyraInventoryEntry& NewEntry = Entries.AddDefaulted_GetRef();
|
||
|
NewEntry.Instance = NewObject<ULyraInventoryItemInstance>(OwnerComponent->GetOwner()); //@TODO: Using the actor instead of component as the outer due to UE-127172
|
||
|
NewEntry.Instance->SetItemDef(ItemDef);
|
||
|
for (ULyraInventoryItemFragment* Fragment : GetDefault<ULyraInventoryItemDefinition>(ItemDef)->Fragments)
|
||
|
{
|
||
|
if (Fragment != nullptr)
|
||
|
{
|
||
|
Fragment->OnInstanceCreated(NewEntry.Instance);
|
||
|
}
|
||
|
}
|
||
|
NewEntry.StackCount = StackCount;
|
||
|
Result = NewEntry.Instance;
|
||
|
|
||
|
//const ULyraInventoryItemDefinition* ItemCDO = GetDefault<ULyraInventoryItemDefinition>(ItemDef);
|
||
|
MarkItemDirty(NewEntry);
|
||
|
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
void FLyraInventoryList::AddEntry(ULyraInventoryItemInstance* Instance)
|
||
|
{
|
||
|
unimplemented();
|
||
|
}
|
||
|
|
||
|
void FLyraInventoryList::RemoveEntry(ULyraInventoryItemInstance* Instance)
|
||
|
{
|
||
|
for (auto EntryIt = Entries.CreateIterator(); EntryIt; ++EntryIt)
|
||
|
{
|
||
|
FLyraInventoryEntry& Entry = *EntryIt;
|
||
|
if (Entry.Instance == Instance)
|
||
|
{
|
||
|
EntryIt.RemoveCurrent();
|
||
|
MarkArrayDirty();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TArray<ULyraInventoryItemInstance*> FLyraInventoryList::GetAllItems() const
|
||
|
{
|
||
|
TArray<ULyraInventoryItemInstance*> Results;
|
||
|
Results.Reserve(Entries.Num());
|
||
|
for (const FLyraInventoryEntry& Entry : Entries)
|
||
|
{
|
||
|
if (Entry.Instance != nullptr) //@TODO: Would prefer to not deal with this here and hide it further?
|
||
|
{
|
||
|
Results.Add(Entry.Instance);
|
||
|
}
|
||
|
}
|
||
|
return Results;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// ULyraInventoryManagerComponent
|
||
|
|
||
|
ULyraInventoryManagerComponent::ULyraInventoryManagerComponent(const FObjectInitializer& ObjectInitializer)
|
||
|
: Super(ObjectInitializer)
|
||
|
, InventoryList(this)
|
||
|
{
|
||
|
SetIsReplicatedByDefault(true);
|
||
|
}
|
||
|
|
||
|
void ULyraInventoryManagerComponent::GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const
|
||
|
{
|
||
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||
|
|
||
|
DOREPLIFETIME(ThisClass, InventoryList);
|
||
|
}
|
||
|
|
||
|
bool ULyraInventoryManagerComponent::CanAddItemDefinition(TSubclassOf<ULyraInventoryItemDefinition> ItemDef, int32 StackCount)
|
||
|
{
|
||
|
//@TODO: Add support for stack limit / uniqueness checks / etc...
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
ULyraInventoryItemInstance* ULyraInventoryManagerComponent::AddItemDefinition(TSubclassOf<ULyraInventoryItemDefinition> ItemDef, int32 StackCount)
|
||
|
{
|
||
|
ULyraInventoryItemInstance* Result = nullptr;
|
||
|
if (ItemDef != nullptr)
|
||
|
{
|
||
|
Result = InventoryList.AddEntry(ItemDef, StackCount);
|
||
|
}
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
void ULyraInventoryManagerComponent::AddItemInstance(ULyraInventoryItemInstance* ItemInstance)
|
||
|
{
|
||
|
InventoryList.AddEntry(ItemInstance);
|
||
|
}
|
||
|
|
||
|
void ULyraInventoryManagerComponent::RemoveItemInstance(ULyraInventoryItemInstance* ItemInstance)
|
||
|
{
|
||
|
InventoryList.RemoveEntry(ItemInstance);
|
||
|
}
|
||
|
|
||
|
TArray<ULyraInventoryItemInstance*> ULyraInventoryManagerComponent::GetAllItems() const
|
||
|
{
|
||
|
return InventoryList.GetAllItems();
|
||
|
}
|
||
|
|
||
|
ULyraInventoryItemInstance* ULyraInventoryManagerComponent::FindFirstItemStackByDefinition(TSubclassOf<ULyraInventoryItemDefinition> ItemDef) const
|
||
|
{
|
||
|
for (const FLyraInventoryEntry& Entry : InventoryList.Entries)
|
||
|
{
|
||
|
ULyraInventoryItemInstance* Instance = Entry.Instance;
|
||
|
|
||
|
if (IsValid(Instance))
|
||
|
{
|
||
|
if (Instance->GetItemDef() == ItemDef)
|
||
|
{
|
||
|
return Instance;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
int32 ULyraInventoryManagerComponent::GetTotalItemCountByDefinition(TSubclassOf<ULyraInventoryItemDefinition> ItemDef) const
|
||
|
{
|
||
|
int32 TotalCount = 0;
|
||
|
for (const FLyraInventoryEntry& Entry : InventoryList.Entries)
|
||
|
{
|
||
|
ULyraInventoryItemInstance* Instance = Entry.Instance;
|
||
|
|
||
|
if (IsValid(Instance))
|
||
|
{
|
||
|
if (Instance->GetItemDef() == ItemDef)
|
||
|
{
|
||
|
++TotalCount;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TotalCount;
|
||
|
}
|
||
|
|
||
|
bool ULyraInventoryManagerComponent::ConsumeItemsByDefinition(TSubclassOf<ULyraInventoryItemDefinition> ItemDef, int32 NumToConsume)
|
||
|
{
|
||
|
AActor* OwningActor = GetOwner();
|
||
|
if (!OwningActor || !OwningActor->HasAuthority())
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//@TODO: N squared right now as there's no acceleration structure
|
||
|
int32 TotalConsumed = 0;
|
||
|
while (TotalConsumed < NumToConsume)
|
||
|
{
|
||
|
if (ULyraInventoryItemInstance* Instance = ULyraInventoryManagerComponent::FindFirstItemStackByDefinition(ItemDef))
|
||
|
{
|
||
|
InventoryList.RemoveEntry(Instance);
|
||
|
++TotalConsumed;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TotalConsumed == NumToConsume;
|
||
|
}
|
||
|
|
||
|
bool ULyraInventoryManagerComponent::ReplicateSubobjects(UActorChannel* Channel, class FOutBunch* Bunch, FReplicationFlags* RepFlags)
|
||
|
{
|
||
|
bool WroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
|
||
|
|
||
|
for (FLyraInventoryEntry& Entry : InventoryList.Entries)
|
||
|
{
|
||
|
ULyraInventoryItemInstance* Instance = Entry.Instance;
|
||
|
|
||
|
if (Instance && IsValid(Instance))
|
||
|
{
|
||
|
WroteSomething |= Channel->ReplicateSubobject(Instance, *Bunch, *RepFlags);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return WroteSomething;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
|
||
|
// UCLASS(Abstract)
|
||
|
// class ULyraInventoryFilter : public UObject
|
||
|
// {
|
||
|
// public:
|
||
|
// virtual bool PassesFilter(ULyraInventoryItemInstance* Instance) const { return true; }
|
||
|
// };
|
||
|
|
||
|
// UCLASS()
|
||
|
// class ULyraInventoryFilter_HasTag : public ULyraInventoryFilter
|
||
|
// {
|
||
|
// public:
|
||
|
// virtual bool PassesFilter(ULyraInventoryItemInstance* Instance) const { return true; }
|
||
|
// };
|
||
|
|