RealtimeStyleTransferRuntime/Source/LyraGame/Inventory/LyraInventoryManagerCompone...

324 lines
9.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraInventoryManagerComponent.h"
#include "Engine/ActorChannel.h"
#include "Engine/World.h"
#include "GameFramework/Actor.h"
#include "GameFramework/GameplayMessageSubsystem.h"
#include "LyraInventoryItemDefinition.h"
#include "LyraInventoryItemInstance.h"
#include "Misc/AssertionMacros.h"
#include "NativeGameplayTags.h"
#include "Net/UnrealNetwork.h"
#include "UObject/NameTypes.h"
#include "UObject/Object.h"
#include "UObject/ObjectPtr.h"
#include "UObject/UObjectBaseUtility.h"
class FLifetimeProperty;
struct FReplicationFlags;
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);
if (IsUsingRegisteredSubObjectList() && IsReadyForReplication() && Result)
{
AddReplicatedSubObject(Result);
}
}
return Result;
}
void ULyraInventoryManagerComponent::AddItemInstance(ULyraInventoryItemInstance* ItemInstance)
{
InventoryList.AddEntry(ItemInstance);
if (IsUsingRegisteredSubObjectList() && IsReadyForReplication() && ItemInstance)
{
AddReplicatedSubObject(ItemInstance);
}
}
void ULyraInventoryManagerComponent::RemoveItemInstance(ULyraInventoryItemInstance* ItemInstance)
{
InventoryList.RemoveEntry(ItemInstance);
if (ItemInstance && IsUsingRegisteredSubObjectList())
{
RemoveReplicatedSubObject(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;
}
void ULyraInventoryManagerComponent::ReadyForReplication()
{
Super::ReadyForReplication();
// Register existing ULyraInventoryItemInstance
if (IsUsingRegisteredSubObjectList())
{
for (const FLyraInventoryEntry& Entry : InventoryList.Entries)
{
ULyraInventoryItemInstance* Instance = Entry.Instance;
if (IsValid(Instance))
{
AddReplicatedSubObject(Instance);
}
}
}
}
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; }
// };