2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "LyraInventoryManagerComponent.h"
2022-09-13 07:18:28 +00:00
# include "Engine/ActorChannel.h"
# include "Engine/World.h"
# include "GameFramework/Actor.h"
# include "GameFramework/GameplayMessageSubsystem.h"
2022-05-23 18:41:30 +00:00
# include "LyraInventoryItemDefinition.h"
2022-09-13 07:18:28 +00:00
# include "LyraInventoryItemInstance.h"
# include "Misc/AssertionMacros.h"
# include "NativeGameplayTags.h"
2022-05-23 18:41:30 +00:00
# include "Net/UnrealNetwork.h"
2022-09-13 07:18:28 +00:00
# include "UObject/NameTypes.h"
# include "UObject/Object.h"
# include "UObject/ObjectPtr.h"
# include "UObject/UObjectBaseUtility.h"
2022-05-23 18:41:30 +00:00
2022-09-13 07:18:28 +00:00
class FLifetimeProperty ;
struct FReplicationFlags ;
2022-05-23 18:41:30 +00:00
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 ) ;
2022-09-13 07:18:28 +00:00
if ( IsUsingRegisteredSubObjectList ( ) & & IsReadyForReplication ( ) & & Result )
{
AddReplicatedSubObject ( Result ) ;
}
2022-05-23 18:41:30 +00:00
}
return Result ;
}
void ULyraInventoryManagerComponent : : AddItemInstance ( ULyraInventoryItemInstance * ItemInstance )
{
InventoryList . AddEntry ( ItemInstance ) ;
2022-09-13 07:18:28 +00:00
if ( IsUsingRegisteredSubObjectList ( ) & & IsReadyForReplication ( ) & & ItemInstance )
{
AddReplicatedSubObject ( ItemInstance ) ;
}
2022-05-23 18:41:30 +00:00
}
void ULyraInventoryManagerComponent : : RemoveItemInstance ( ULyraInventoryItemInstance * ItemInstance )
{
InventoryList . RemoveEntry ( ItemInstance ) ;
2022-09-13 07:18:28 +00:00
if ( ItemInstance & & IsUsingRegisteredSubObjectList ( ) )
{
RemoveReplicatedSubObject ( ItemInstance ) ;
}
2022-05-23 18:41:30 +00:00
}
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 ;
}
2022-09-13 07:18:28 +00:00
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 ) ;
}
}
}
}
2022-05-23 18:41:30 +00:00
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; }
// };