// Copyright 2025 https://yuewu.dev/en All Rights Reserved. #include "Weapon/GCS_WeaponActor.h" #include "Components/PrimitiveComponent.h" #include "GameFramework/Pawn.h" #include "GCS_LogChannels.h" #include "Collision/GCS_TraceSystemComponent.h" #include "Misc/DataValidation.h" #include "Net/UnrealNetwork.h" #include "Net/Core/PushModel/PushModel.h" AGCS_WeaponActor::AGCS_WeaponActor(const FObjectInitializer& ObjectInitializer) { PrimaryActorTick.bCanEverTick = true; bReplicates = true; bWeaponActive = false; } APawn* AGCS_WeaponActor::GetWeaponOwner_Implementation() const { return Cast(GetOwner()); } const FGameplayTagContainer AGCS_WeaponActor::GetWeaponTags_Implementation() const { return WeaponTags; } void AGCS_WeaponActor::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); FDoRepLifetimeParams Parameters; Parameters.bIsPushBased = true; DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, bWeaponActive, Parameters) } void AGCS_WeaponActor::SetWeaponActive_Implementation(bool bNewActive) { if (GetOwner()->HasAuthority()) { const bool prev = bWeaponActive; bWeaponActive = bNewActive; MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, bWeaponActive, this); OnWeaponActiveStateChanged(prev); } } bool AGCS_WeaponActor::IsWeaponActive_Implementation() const { return bWeaponActive; } UPrimitiveComponent* AGCS_WeaponActor::GetPrimitiveComponent_Implementation() const { if (WeaponMeshTagName.IsValid()) { UPrimitiveComponent* Primitive = Cast(GetOwner()->FindComponentByTag(UPrimitiveComponent::StaticClass(), WeaponMeshTagName)); if (!IsValid(Primitive)) { GCS_CLOG(Warning, "failed to find weapon mesh via tag (%s) as weapon primitive component. weapon owner:%s", *WeaponMeshTagName.ToString(), *GetOwner()->GetName()); return nullptr; } return Primitive; } GCS_CLOG(Warning, "no weapon primitive component provided. weapon owner:%s", *GetOwner()->GetName()); return nullptr; } void AGCS_WeaponActor::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const { TagContainer = Execute_GetWeaponTags(this); } void AGCS_WeaponActor::OnWeaponActiveStateChanged_Implementation(bool Prev) { OnWeaponActiveStateChangedEvent.Broadcast(bWeaponActive); } void AGCS_WeaponActor::BeginPlay() { Super::BeginPlay(); } void AGCS_WeaponActor::EndPlay(const EEndPlayReason::Type EndPlayReason) { if (Execute_IsWeaponActive(this)) { bWeaponActive = false; RefreshTraceInstance(); } Super::EndPlay(EndPlayReason); } void AGCS_WeaponActor::RefreshTraceInstance_Implementation() { APawn* Pawn = Execute_GetWeaponOwner(this); if (!IsValid(Pawn)) { GCS_CLOG(Warning, "mising weapon owner!") return; } UGCS_TraceSystemComponent* TSC = UGCS_TraceSystemComponent::GetTraceSystemComponent(Pawn); if (!IsValid(TSC)) { GCS_CLOG(Warning, "missing trace system on weapon owner(%s)!", *GetNameSafe(Pawn)) return; } if (bWeaponActive) { TraceHandles.Reset(); for (const FGCS_TraceDefinition& TraceDefinition : TraceDefinitions) { UPrimitiveComponent* Primitive = GetSourceComponentForTrace(TraceDefinition.TraceTag); if (Primitive == nullptr) { GCS_CLOG(Error, "No SourceComponent provided for trace:%s,check your GetSourceComponentForTrace implementation.", *TraceDefinition.TraceTag.ToString()); continue; } FGCS_TraceHandle Handle = TSC->AddTrace(TraceDefinition, Primitive, GetSourceObjectForTrace()); if (Handle.IsValidHandle()) { TraceHandles.Add(Handle); } } TSC->OnTraceHitEvent.AddDynamic(this, &ThisClass::OnAnyTraceHit); TSC->OnTraceStateChangedEvent.AddDynamic(this, &ThisClass::OnAnyTraceStateChanged); } else { TSC->OnTraceHitEvent.RemoveDynamic(this, &ThisClass::OnAnyTraceHit); TSC->OnTraceStateChangedEvent.RemoveDynamic(this, &ThisClass::OnAnyTraceStateChanged); for (const FGCS_TraceHandle& TraceHandle : TraceHandles) { TSC->RemoveTrace(TraceHandle); } } } UObject* AGCS_WeaponActor::GetSourceObjectForTrace_Implementation() { return this; } UPrimitiveComponent* AGCS_WeaponActor::GetSourceComponentForTrace_Implementation(const FGameplayTag& TraceTag) const { // Default Using weapon primitive as source component for trace if (UPrimitiveComponent* PrimitiveComponent = Execute_GetPrimitiveComponent(this)) { return PrimitiveComponent; } GCS_CLOG(Error, "Weapon(%s) didn't return return valid primitive component to be used as SourceComponent for traces, try implement this function to get properly SourceComponent For Trace.", *GetNameSafe(this)) return nullptr; } void AGCS_WeaponActor::OnAnyTraceHit_Implementation(const FGCS_TraceHandle& TraceHandle, const FHitResult& HitResult) { } void AGCS_WeaponActor::OnAnyTraceStateChanged_Implementation(const FGCS_TraceHandle& TraceHandle, bool NewState) { } void AGCS_WeaponActor::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); } #if WITH_EDITOR EDataValidationResult AGCS_WeaponActor::IsDataValid(class FDataValidationContext& Context) const { for (int32 i = 0; i < TraceDefinitions.Num(); i++) { if (!TraceDefinitions[i].IsValidDefinition()) { Context.AddWarning(FText::FromString(FString::Format(TEXT("Found invalid trace definition at index({0})"), {i}))); return EDataValidationResult::Invalid; } } return Super::IsDataValid(Context); } #endif