// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Net/Serialization/FastArraySerializer.h" #include "Components/PawnComponent.h" #include "LyraCharacterPartTypes.h" #include "GameplayTagContainer.h" #include "Cosmetics/LyraCosmeticAnimationTypes.h" #include "LyraPawnComponent_CharacterParts.generated.h" class USkeletalMeshComponent; class UChildActorComponent; class ULyraPawnComponent_CharacterParts; struct FLyraCharacterPartList; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLyraSpawnedCharacterPartsChanged, ULyraPawnComponent_CharacterParts*, ComponentWithChangedParts); ////////////////////////////////////////////////////////////////////// // A single applied character part USTRUCT() struct FLyraAppliedCharacterPartEntry : public FFastArraySerializerItem { GENERATED_BODY() FLyraAppliedCharacterPartEntry() {} FString GetDebugString() const; private: friend FLyraCharacterPartList; friend ULyraPawnComponent_CharacterParts; private: // The character part being represented UPROPERTY() FLyraCharacterPart Part; // Handle index we returned to the user (server only) UPROPERTY(NotReplicated) int32 PartHandle = INDEX_NONE; // The spawned actor instance (client only) UPROPERTY(NotReplicated) TObjectPtr SpawnedComponent = nullptr; }; ////////////////////////////////////////////////////////////////////// // Replicated list of applied character parts USTRUCT(BlueprintType) struct FLyraCharacterPartList : public FFastArraySerializer { GENERATED_BODY() FLyraCharacterPartList() : OwnerComponent(nullptr) { } FLyraCharacterPartList(ULyraPawnComponent_CharacterParts* InOwnerComponent) : OwnerComponent(InOwnerComponent) { } public: //~FFastArraySerializer contract void PreReplicatedRemove(const TArrayView RemovedIndices, int32 FinalSize); void PostReplicatedAdd(const TArrayView AddedIndices, int32 FinalSize); void PostReplicatedChange(const TArrayView ChangedIndices, int32 FinalSize); //~End of FFastArraySerializer contract bool NetDeltaSerialize(FNetDeltaSerializeInfo& DeltaParms) { return FFastArraySerializer::FastArrayDeltaSerialize(Entries, DeltaParms, *this); } FLyraCharacterPartHandle AddEntry(FLyraCharacterPart NewPart); void RemoveEntry(FLyraCharacterPartHandle Handle); void ClearAllEntries(bool bBroadcastChangeDelegate); FGameplayTagContainer CollectCombinedTags() const; private: friend ULyraPawnComponent_CharacterParts; bool SpawnActorForEntry(FLyraAppliedCharacterPartEntry& Entry); bool DestroyActorForEntry(FLyraAppliedCharacterPartEntry& Entry); private: // Replicated list of equipment entries UPROPERTY() TArray Entries; // The component that contains this list UPROPERTY() ULyraPawnComponent_CharacterParts* OwnerComponent; // Upcounter for handles int32 PartHandleCounter = 0; }; template<> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 { enum { WithNetDeltaSerializer = true }; }; ////////////////////////////////////////////////////////////////////// // A component that handles spawning cosmetic actors attached to the owner pawn on all clients UCLASS(meta=(BlueprintSpawnableComponent)) class ULyraPawnComponent_CharacterParts : public UPawnComponent { GENERATED_BODY() public: ULyraPawnComponent_CharacterParts(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); //~UActorComponent interface virtual void BeginPlay() override; virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; //~End of UActorComponent interface // Adds a character part to the actor that owns this customization component, should be called on the authority only UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category=Cosmetics) FLyraCharacterPartHandle AddCharacterPart(const FLyraCharacterPart& NewPart); // Removes a previously added character part from the actor that owns this customization component, should be called on the authority only UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category=Cosmetics) void RemoveCharacterPart(FLyraCharacterPartHandle Handle); // Removes all added character parts, should be called on the authority only UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category=Cosmetics) void RemoveAllCharacterParts(); // Gets the list of all spawned character parts from this component UFUNCTION(BlueprintCallable, BlueprintPure=false, BlueprintCosmetic, Category=Cosmetics) TArray GetCharacterPartActors() const; // If the parent actor is derived from ACharacter, returns the Mesh component, otherwise nullptr USkeletalMeshComponent* GetParentMeshComponent() const; // Returns the scene component to attach the spawned actors to // If the parent actor is derived from ACharacter, we'll use the Mesh component, otherwise the root component USceneComponent* GetSceneComponentToAttachTo() const; // Returns the set of combined gameplay tags from attached character parts, optionally filtered to only tags that start with the specified root UFUNCTION(BlueprintCallable, BlueprintPure=false, BlueprintCosmetic, Category=Cosmetics) FGameplayTagContainer GetCombinedTags(FGameplayTag RequiredPrefix) const; void BroadcastChanged(); public: // Delegate that will be called when the list of spawned character parts has changed UPROPERTY(BlueprintAssignable, Category=Cosmetics, BlueprintCallable) FLyraSpawnedCharacterPartsChanged OnCharacterPartsChanged; private: // List of character parts UPROPERTY(Replicated) FLyraCharacterPartList CharacterPartList; // Rules for how to pick a body style mesh for animation to play on, based on character part cosmetics tags UPROPERTY(EditAnywhere, Category=Cosmetics) FLyraAnimBodyStyleSelectionSet BodyMeshes; };