// Copyright 2025 https://yuewu.dev/en All Rights Reserved. #include "GIS_EquipmentInstance.h" #include "Engine/World.h" #include "Components/SkeletalMeshComponent.h" #include "Engine/BlueprintGeneratedClass.h" #include "GameFramework/Character.h" #include "Net/UnrealNetwork.h" #include "TimerManager.h" #if UE_WITH_IRIS #include "Iris/ReplicationSystem/ReplicationFragmentUtil.h" #endif // UE_WITH_IRIS #include "GIS_EquipmentActorInterface.h" #include "GIS_EquipmentSystemComponent.h" #include "Items/GIS_ItemInstance.h" #include "GIS_ItemFragment_Equippable.h" #include "GIS_LogChannels.h" #include "Pickups/GIS_WorldItemComponent.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(GIS_EquipmentInstance) class FLifetimeProperty; class UClass; class USceneComponent; UGIS_EquipmentInstance::UGIS_EquipmentInstance(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bIsActive = false; } bool UGIS_EquipmentInstance::IsSupportedForNetworking() const { return true; } UWorld* UGIS_EquipmentInstance::GetWorld() const { if (OwningPawn) { return OwningPawn->GetWorld(); } if (UObject* Outer = GetOuter()) { return Outer->GetWorld(); } return nullptr; } void UGIS_EquipmentInstance::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); // DOREPLIFETIME(ThisClass, SourceItem); // fix: https://forums.unrealengine.com/t/subobject-replication-for-blueprint-child-class/106205/4 UBlueprintGeneratedClass* bpClass = Cast(this->GetClass()); if (bpClass != nullptr) { bpClass->GetLifetimeBlueprintReplicationList(OutLifetimeProps); } DOREPLIFETIME(ThisClass, EquipmentActors); } void UGIS_EquipmentInstance::ReceiveOwningPawn_Implementation(APawn* NewPawn) { OwningPawn = NewPawn; } APawn* UGIS_EquipmentInstance::GetOwningPawn_Implementation() const { return OwningPawn; } void UGIS_EquipmentInstance::ReceiveSourceItem_Implementation(UGIS_ItemInstance* NewItem) { SourceItem = NewItem; } UGIS_ItemInstance* UGIS_EquipmentInstance::GetSourceItem_Implementation() const { return SourceItem; } void UGIS_EquipmentInstance::OnEquipmentBeginPlay_Implementation() { if (OwningPawn->HasAuthority()) { SpawnAndSetupEquipmentActors(SourceItem->FindFragmentByClass()->ActorsToSpawn); } else { SetupEquipmentActors(EquipmentActors); } } void UGIS_EquipmentInstance::OnEquipmentTick_Implementation(float DeltaSeconds) { } void UGIS_EquipmentInstance::OnEquipmentEndPlay_Implementation() { DestroyEquipmentActors(); } #if UE_WITH_IRIS void UGIS_EquipmentInstance::RegisterReplicationFragments(UE::Net::FFragmentRegistrationContext& Context, UE::Net::EFragmentRegistrationFlags RegistrationFlags) { using namespace UE::Net; // Build descriptors and allocate PropertyReplicationFragments for this object FReplicationFragmentUtil::CreateAndRegisterFragmentsForObject(this, Context, RegistrationFlags); } #endif // UE_WITH_IRIS bool UGIS_EquipmentInstance::IsEquipmentActive_Implementation() const { return bIsActive; } void UGIS_EquipmentInstance::OnActiveStateChanged_Implementation(bool NewActiveState) { bIsActive = NewActiveState; SetupActiveStateForEquipmentActors(EquipmentActors); OnActiveStateChangedEvent.Broadcast(NewActiveState); } APawn* UGIS_EquipmentInstance::GetTypedOwningPawn(TSubclassOf PawnType) const { APawn* Result = nullptr; if (UClass* ActualPawnType = PawnType) { if (APawn* Pawn = Execute_GetOwningPawn(this)) { if (Pawn->IsA(ActualPawnType)) { Result = Pawn; } } } return Result; } bool UGIS_EquipmentInstance::CanActivate_Implementation() const { return true; } int32 UGIS_EquipmentInstance::GetIndexOfEquipmentActor(const AActor* InEquipmentActor) const { if (IsValid(InEquipmentActor) && EquipmentActors.Contains(InEquipmentActor)) { for (int32 i = 0; i < EquipmentActors.Num(); i++) { if (EquipmentActors[i] == InEquipmentActor) { return i; } } } return INDEX_NONE; } AActor* UGIS_EquipmentInstance::GetTypedEquipmentActor(TSubclassOf DesiredClass) const { if (UClass* RealClass = DesiredClass) { for (const TObjectPtr& SpawnedActor : EquipmentActors) { if (SpawnedActor && SpawnedActor->GetClass()->IsChildOf(RealClass)) { return SpawnedActor; } } } return nullptr; } void UGIS_EquipmentInstance::SpawnAndSetupEquipmentActors(const TArray& ActorsToSpawn) { if (OwningPawn == nullptr || !OwningPawn->HasAuthority()) { return; } USceneComponent* AttachParent = GetAttachParentForSpawnedActors(OwningPawn); for (int32 i = 0; i < ActorsToSpawn.Num(); ++i) { const FGIS_EquipmentActorToSpawn& SpawnInfo = ActorsToSpawn[i]; if (SpawnInfo.ActorToSpawn.IsNull()) { continue; } TSubclassOf ActorClass = SpawnInfo.ActorToSpawn.LoadSynchronous(); if (!ActorClass) { GIS_CLOG(Warning, "%s Failed to load actor class at index: %d ", *GetName(), i); continue; } if (!ActorClass->ImplementsInterface(UGIS_EquipmentActorInterface::StaticClass())) { GIS_CLOG(Warning, "actor class(%s) doesn't implements:%s", *ActorClass->GetName(), *UGIS_EquipmentActorInterface::StaticClass()->GetName()); continue; } AActor* NewActor = GetWorld()->SpawnActorDeferred(ActorClass, FTransform::Identity, OwningPawn); if (NewActor == nullptr) { GIS_CLOG(Warning, "%s Failed to spawn actor of class: %s at index: %d ", *GetName(), *ActorClass->GetName(), i); continue; } BeforeSpawningActor(NewActor); NewActor->FinishSpawning(FTransform::Identity, /*bIsDefaultTransform=*/true); if (SpawnInfo.bShouldAttach) { NewActor->SetActorRelativeTransform(SpawnInfo.AttachTransform); NewActor->AttachToComponent(AttachParent, FAttachmentTransformRules::KeepRelativeTransform, SpawnInfo.AttachSocket); } EquipmentActors.Add(NewActor); } SetupEquipmentActors(EquipmentActors); } void UGIS_EquipmentInstance::DestroyEquipmentActors() { if (OwningPawn == nullptr || !OwningPawn->HasAuthority()) { return; } for (AActor* Actor : EquipmentActors) { if (!IsValid(Actor)) { continue; } if (IsValid(Actor) && Actor->GetClass()->ImplementsInterface(UGIS_EquipmentActorInterface::StaticClass())) { IGIS_EquipmentActorInterface::Execute_ReceiveEquipmentEndPlay(Actor); } if (IsValid(Actor)) { Actor->Destroy(); } } EquipmentActors.Empty(); } USceneComponent* UGIS_EquipmentInstance::GetAttachParentForSpawnedActors_Implementation(APawn* Pawn) const { if (ACharacter* Char = Cast(Pawn)) { return Char->GetMesh(); } if (Pawn) { return Pawn->FindComponentByClass(); } return nullptr; } void UGIS_EquipmentInstance::BeforeSpawningActor_Implementation(AActor* SpawningActor) const { } void UGIS_EquipmentInstance::SetupEquipmentActors_Implementation(const TArray& InActors) { if (!OwningPawn) { return; } if (OwningPawn->HasAuthority()) { for (int32 i = 0; i < InActors.Num(); i++) { UGIS_WorldItemComponent* WorldItem = InActors[i]->FindComponentByClass(); if (WorldItem != nullptr) { WorldItem->SetItemInfo(SourceItem, 1); } } } for (int32 i = 0; i < InActors.Num(); i++) { AActor* Actor = InActors[i]; if (IsValid(Actor)) { if (Actor->GetClass()->ImplementsInterface(UGIS_EquipmentActorInterface::StaticClass())) { IGIS_EquipmentActorInterface::Execute_ReceiveSourceEquipment(Actor, this, i); IGIS_EquipmentActorInterface::Execute_ReceiveEquipmentBeginPlay(Actor); } } } } void UGIS_EquipmentInstance::OnRep_EquipmentActors() { } bool UGIS_EquipmentInstance::IsEquipmentActorsValid(int32 Num) const { if (EquipmentActors.Num() == Num) { bool bValid = true; for (int32 i = 0; i < EquipmentActors.Num(); i++) { if (!IsValid(EquipmentActors[i])) { bValid = false; break; } } return bValid; } return false; } void UGIS_EquipmentInstance::SetupInitialStateForEquipmentActors(const TArray& InActors) { } void UGIS_EquipmentInstance::SetupActiveStateForEquipmentActors(const TArray& InActors) const { for (int32 i = 0; i < InActors.Num(); i++) { AActor* Actor = InActors[i]; if (IsValid(Actor) && Actor->GetClass()->ImplementsInterface(UGIS_EquipmentInterface::StaticClass())) { Execute_OnActiveStateChanged(Actor, bIsActive); } } }