第一次提交

This commit is contained in:
不明不惑
2026-03-03 01:23:02 +08:00
commit 3e434877e8
1053 changed files with 102411 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Targeting/Filters/GCS_TargetingFilterTask_Affiliation.h"
#include "GCS_CombatSystemSettings.h"
#include "GameFramework/Pawn.h"
#include "GameFramework/Controller.h"
#include "Team/GCS_CombatTeamAgentInterface.h"
#include "Utility/GCS_CombatFunctionLibrary.h"
bool UGCS_TargetingFilterTask_Affiliation::ShouldFilterTarget(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
{
if (const UGCS_CombatSystemSettings* Settings = UGCS_CombatSystemSettings::Get())
{
if (Settings->bDisableAffiliationCheck)
{
return false;
}
}
FGenericTeamId SourceTeamId = GetSourceTeamId(TargetingHandle, TargetData);
FGenericTeamId TargetTeamId = GetTargetTeamId(TargetingHandle, TargetData);
if (TargetTeamId == FGenericTeamId::NoTeam && bIgnoreTargetWithNoTeam)
{
return true;
}
return FAISenseAffiliationFilter::ShouldSenseTeam(SourceTeamId, TargetTeamId, DetectionByAffiliation.GetAsFlags()) == false;
}
FGenericTeamId UGCS_TargetingFilterTask_Affiliation::GetSourceTeamId(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
{
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
AActor* Actor = SourceContext->InstigatorActor ? SourceContext->InstigatorActor : SourceContext->SourceActor;
if (IsValid(Actor))
{
return UGCS_CombatFunctionLibrary::QueryCombatTeamId(Actor, true, true);;
}
}
return FGenericTeamId::NoTeam;
}
FGenericTeamId UGCS_TargetingFilterTask_Affiliation::GetTargetTeamId(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
{
if (const AActor* TargetActor = TargetData.HitResult.GetActor())
{
return UGCS_CombatFunctionLibrary::QueryCombatTeamId(TargetActor, true, true);;
}
return FGenericTeamId::NoTeam;
}

View File

@@ -0,0 +1,19 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Targeting/Filters/GCS_TargetingFilterTask_IsDead.h"
#include "GCS_CombatEntityInterface.h"
#include "Utility/GCS_CombatFunctionLibrary.h"
bool UGCS_TargetingFilterTask_IsDead::ShouldFilterTarget(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
{
AActor* TargetActor = TargetData.HitResult.GetActor();
if (UObject* Implementer = UGCS_CombatFunctionLibrary::GetCombatEntity(TargetActor))
{
return IGCS_CombatEntityInterface::Execute_IsDead(Implementer);
}
return false;
}

View File

@@ -0,0 +1,40 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Targeting/Filters/GCS_TargetingFilterTask_TagsRequirements.h"
#include "AbilitySystemComponent.h"
#include "AbilitySystemGlobals.h"
bool UGCS_TargetingFilterTask_TagsRequirements::ShouldFilterTarget(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
{
const AActor* TargetActor = TargetData.HitResult.GetActor();
if (UAbilitySystemComponent* ASC = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(TargetActor))
{
FGameplayTagContainer ActorTags;
ASC->GetOwnedGameplayTags(ActorTags);
return bInvert ? !TagQuery.Matches(ActorTags) : TagQuery.Matches(ActorTags);
}
if (bLookingForTagAssetInterface)
{
const IGameplayTagAssetInterface* TagAssetInterface = Cast<IGameplayTagAssetInterface>(TargetActor);
if (!TagAssetInterface)
{
const TArray<UActorComponent*> Components = TargetActor->GetComponentsByInterface(UGameplayTagAssetInterface::StaticClass());
TagAssetInterface = Components.IsValidIndex(0) ? Cast<IGameplayTagAssetInterface>(Components[0]) : nullptr;
}
if (TagAssetInterface)
{
FGameplayTagContainer ActorTags;
TagAssetInterface->GetOwnedGameplayTags(ActorTags);
return bInvert ? !TagQuery.Matches(ActorTags) : TagQuery.Matches(ActorTags);
}
}
return false;
}

View File

@@ -0,0 +1,21 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Targeting/Filters/GCS_TargetingFilterTask_TraceInstance.h"
#include "Collision/DEPRECATED_GCS_CollisionTraceInstance.h"
bool UGCS_TargetingFilterTask_TraceInstance::ShouldFilterTarget(const FTargetingRequestHandle& TargetingHandle, const FTargetingDefaultResultData& TargetData) const
{
const AActor* TargetActor = TargetData.HitResult.GetActor();
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
if (const UDEPRECATED_GCS_CollisionTraceInstance* TraceInstance = Cast<UDEPRECATED_GCS_CollisionTraceInstance>(SourceContext->SourceObject))
{
return !TraceInstance->CanHitActor(TargetActor);
}
}
return false;
}

View File

@@ -0,0 +1,93 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Targeting/GCS_TargetingFunctionLibrary.h"
#include "Components/MeshComponent.h"
FTargetingSourceContext UGCS_TargetingFunctionLibrary::GetTargetingSourceContext(FTargetingRequestHandle TargetingHandle)
{
if (TargetingHandle.IsValid())
{
if (FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
return *SourceContext;
}
}
return FTargetingSourceContext();
}
FString UGCS_TargetingFunctionLibrary::GetTargetingSourceContextDebugString(FTargetingRequestHandle TargetingHandle)
{
if (TargetingHandle.IsValid())
{
if (FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
return FString::Format(TEXT("ctx(actor:{0},instigator:{1},source:{2})"), {
*GetNameSafe(SourceContext->SourceActor), *GetNameSafe(SourceContext->InstigatorActor), *GetNameSafe(SourceContext->SourceObject)
});
}
}
return TEXT("None");
}
void UGCS_TargetingFunctionLibrary::GetTargetingResultsActors(FTargetingRequestHandle TargetingHandle, TArray<AActor*>& Targets)
{
if (TargetingHandle.IsValid())
{
if (FTargetingDefaultResultsSet* Results = FTargetingDefaultResultsSet::Find(TargetingHandle))
{
for (const FTargetingDefaultResultData& ResultData : Results->TargetResults)
{
if (AActor* Target = ResultData.HitResult.GetActor())
{
Targets.Add(Target);
}
}
}
}
}
void UGCS_TargetingFunctionLibrary::GetTargetingResults(FTargetingRequestHandle TargetingHandle, TArray<FHitResult>& OutTargets)
{
if (TargetingHandle.IsValid())
{
if (FTargetingDefaultResultsSet* Results = FTargetingDefaultResultsSet::Find(TargetingHandle))
{
for (const FTargetingDefaultResultData& ResultData : Results->TargetResults)
{
OutTargets.Add(ResultData.HitResult);
}
}
}
}
FTargetingSourceContext UGCS_TargetingFunctionLibrary::ConvertTargetingLocationInfoToSourceContext(FGameplayAbilityTargetingLocationInfo LocationInfo)
{
FTargetingSourceContext Context = FTargetingSourceContext();
//Return or calculate based on LocationType.
switch (LocationInfo.LocationType)
{
case EGameplayAbilityTargetingLocationType::ActorTransform:
if (LocationInfo.SourceActor)
{
Context.SourceActor = LocationInfo.SourceActor;
}
break;
case EGameplayAbilityTargetingLocationType::SocketTransform:
if (LocationInfo.SourceComponent)
{
// Bad socket name will just return component transform anyway, so we're safe
Context.SourceLocation = LocationInfo.SourceComponent->GetSocketTransform(LocationInfo.SourceSocketName).GetLocation();
}
break;
case EGameplayAbilityTargetingLocationType::LiteralTransform:
Context.SourceLocation = LocationInfo.LiteralTransform.GetLocation();
default:
check(false);
break;
}
return Context;
}

View File

@@ -0,0 +1,7 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Targeting/GCS_TargetingSourceInterface.h"
// Add default functionality here for any IGCS_TargetingSourceInterface functions that are not pure virtual.

View File

@@ -0,0 +1,412 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Targeting/GCS_TargetingSystemComponent.h"
#include "GCS_LogChannels.h"
#include "Kismet/KismetMathLibrary.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/Character.h"
#include "Camera/PlayerCameraManager.h"
#include "Net/UnrealNetwork.h"
#include "TargetingSystem/TargetingSubsystem.h"
UGCS_TargetingSystemComponent::UGCS_TargetingSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = true;
SetIsReplicatedByDefault(true);
}
UGCS_TargetingSystemComponent* UGCS_TargetingSystemComponent::GetTargetingSystemComponent(const AActor* Actor)
{
return Actor ? Actor->FindComponentByClass<UGCS_TargetingSystemComponent>() : nullptr;
}
void UGCS_TargetingSystemComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(ThisClass, TargetedActor, COND_OwnerOnly);
}
// Called when the game starts
void UGCS_TargetingSystemComponent::BeginPlay()
{
Super::BeginPlay();
// ...
}
// Called every frame
void UGCS_TargetingSystemComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
RefreshTargeting(DeltaTime);
}
void UGCS_TargetingSystemComponent::RefreshTargeting(float DeltaTime)
{
if (GetOwnerRole() >= ROLE_AutonomousProxy)
{
if (bAutoUpdatePotentialTargets || IsValid(TargetedActor))
{
RefreshPotentialTargets();
}
bool LocalBool = PotentialTargets.Contains(TargetedActor);
// Current TargetedActor no longer consider valid.
if (TargetedActor && !LocalBool)
{
OnLockOff();
SetTargetedActor(nullptr);
}
}
}
void UGCS_TargetingSystemComponent::SearchForActorToTarget()
{
RefreshPotentialTargets();
SelectFromPotentialTargets();
//No target found/unlocked.
if (!IsValid(TargetedActor) && !bAutoUpdatePotentialTargets)
{
PotentialTargets.Empty();
}
}
AActor* UGCS_TargetingSystemComponent::SelectClosestActorFromPotentialTargets(float Radius) const
{
// 检查 Owner 是否有效
if (!GetOwner())
{
UE_LOG(LogTemp, Warning, TEXT("SelectClosestActorFromPotentialTargets: Owner is null"));
return nullptr;
}
// 检查 PotentialTargets 是否为空
if (PotentialTargets.Num() == 0)
{
return nullptr;
}
// 过滤有效 Actor 并在范围内
TArray<AActor*> FilteredPotentialTargets;
const FVector OwnerLocation = GetOwner()->GetActorLocation();
for (AActor* Actor : PotentialTargets)
{
// 检查 Actor 是否有效且未被销毁
if (IsValid(Actor))
{
float Distance = FVector::Dist(OwnerLocation, Actor->GetActorLocation());
if (Distance <= Radius)
{
FilteredPotentialTargets.Add(Actor);
}
}
}
// 如果过滤后没有有效目标
if (FilteredPotentialTargets.Num() == 0)
{
return nullptr;
}
// 寻找最近的 Actor
AActor* ClosestActor = FilteredPotentialTargets[0];
float MinDistance = FVector::Dist(OwnerLocation, ClosestActor->GetActorLocation());
for (int32 i = 1; i < FilteredPotentialTargets.Num(); ++i)
{
AActor* CurrentActor = FilteredPotentialTargets[i];
if (IsValid(CurrentActor))
{
float Distance = FVector::Dist(OwnerLocation, CurrentActor->GetActorLocation());
if (Distance < MinDistance)
{
MinDistance = Distance;
ClosestActor = CurrentActor;
}
}
}
return ClosestActor;
}
bool UGCS_TargetingSystemComponent::FilterActorsWithPreset(UTargetingPreset* InTargetingPreset, const TArray<AActor*> InTargets, TArray<AActor*>& OutActors)
{
if (InTargetingPreset == nullptr)
{
return false;
}
if (UTargetingSubsystem* TargetingSubsystem = UTargetingSubsystem::Get(GetWorld()))
{
FTargetingSourceContext SourceContext;
SourceContext.SourceActor = GetOwner();
FTargetingRequestHandle TargetingHandle = UTargetingSubsystem::MakeTargetRequestHandle(TargetingPreset, SourceContext);
if (TargetingHandle.IsValid() && InTargets.Num() > 0)
{
FTargetingDefaultResultsSet& TargetingResults = FTargetingDefaultResultsSet::FindOrAdd(TargetingHandle);
for (AActor* Target : InTargets)
{
if (!Target)
{
continue;
}
bool bAddResult = !TargetingResults.TargetResults.ContainsByPredicate([Target](const FTargetingDefaultResultData& Data) -> bool
{
return (Data.HitResult.GetActor() == Target);
});
if (bAddResult)
{
FTargetingDefaultResultData* ResultData = new(TargetingResults.TargetResults) FTargetingDefaultResultData();
ResultData->HitResult.HitObjectHandle = FActorInstanceHandle(Target);
ResultData->HitResult.Location = Target->GetActorLocation();
}
}
}
FTargetingRequestDelegate Delegate = FTargetingRequestDelegate::CreateWeakLambda(this, [&](FTargetingRequestHandle InTargetingHandle)
{
TargetingSubsystem->GetTargetingResultsActors(InTargetingHandle, OutActors);
});
FTargetingImmediateTaskData& ImmeidateTaskData = FTargetingImmediateTaskData::FindOrAdd(TargetingHandle);
ImmeidateTaskData.bReleaseOnCompletion = true;
TargetingSubsystem->ExecuteTargetingRequestWithHandle(TargetingHandle, Delegate);
}
return !OutActors.IsEmpty();
}
void UGCS_TargetingSystemComponent::SetTargetedActor(AActor* NewActor)
{
SetTargetedActor(NewActor, true);
}
void UGCS_TargetingSystemComponent::SetTargetedActor(AActor* NewActor, bool bSendRpc)
{
if (GetOwnerRole() < ROLE_AutonomousProxy)
{
return;
}
if (bSendRpc)
{
if (GetOwnerRole() >= ROLE_Authority)
{
ClientSetTargetedActor(NewActor);
}
else
{
ServerSetTargetedActor(NewActor);
}
}
TargetedActor = NewActor;
}
void UGCS_TargetingSystemComponent::ClientSetTargetedActor_Implementation(AActor* NewActor)
{
SetTargetedActor(NewActor, false);
}
void UGCS_TargetingSystemComponent::ServerSetTargetedActor_Implementation(AActor* NewActor)
{
SetTargetedActor(NewActor, false);
}
void UGCS_TargetingSystemComponent::OnLockOff_Implementation()
{
OnTargetLockOffEvent.Broadcast(TargetedActor);
}
void UGCS_TargetingSystemComponent::OnLockOn_Implementation()
{
OnTargetLockOnEvent.Broadcast(TargetedActor);
}
void UGCS_TargetingSystemComponent::SelectFromPotentialTargets()
{
if (!IsValid(TargetedActor))
{
TMap<AActor*, float> LocalPotentialTargets;
for (TObjectPtr<AActor>& Elem : PotentialTargets)
{
if (CanBeTargeted(Elem))
{
LocalPotentialTargets.Add(Elem, UKismetMathLibrary::Abs(CalculateViewAngle(Elem)));
}
}
if (LocalPotentialTargets.Num() > 0)
{
TArray<AActor*> LocalTargets;
TArray<float> LocalAngles;
LocalPotentialTargets.GenerateKeyArray(LocalTargets);
LocalPotentialTargets.GenerateValueArray(LocalAngles);
int32 MinIndex;
const float MaxValue = FMath::Min<float>(LocalAngles, &MinIndex);
SetTargetedActor(LocalTargets[MinIndex]);
OnLockOn();
}
}
else
{
OnLockOff();
SetTargetedActor(nullptr);
}
}
void UGCS_TargetingSystemComponent::RefreshPotentialTargets()
{
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UGCS_TargetingSystemComponent::RefreshPotentialTargets"), UGCS_TargetingSystemComponent_RefreshPotentialTargets, STATGROUP_GCS)
TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__)
if (TargetingPreset == nullptr)
{
return;
}
if (UTargetingSubsystem* TargetingSubsystem = UTargetingSubsystem::Get(GetWorld()))
{
FTargetingSourceContext SourceContext;
SourceContext.SourceActor = GetOwner();
SourceContext.SourceObject = this;
FTargetingRequestHandle TargetingHandle = UTargetingSubsystem::MakeTargetRequestHandle(TargetingPreset, SourceContext);
FTargetingRequestDelegate Delegate = FTargetingRequestDelegate::CreateWeakLambda(this, [this,TargetingSubsystem](FTargetingRequestHandle InTargetingHandle)
{
TArray<AActor*> Results;
TargetingSubsystem->GetTargetingResultsActors(InTargetingHandle, Results);
PotentialTargets.Empty();
PotentialTargets = Results;
});
if (bUseAsyncTargeting)
{
FTargetingAsyncTaskData& AsyncTaskData = FTargetingAsyncTaskData::FindOrAdd(TargetingHandle);
AsyncTaskData.bReleaseOnCompletion = true;
TargetingSubsystem->StartAsyncTargetingRequestWithHandle(TargetingHandle, Delegate);
}
else
{
FTargetingImmediateTaskData& ImmeidateTaskData = FTargetingImmediateTaskData::FindOrAdd(TargetingHandle);
ImmeidateTaskData.bReleaseOnCompletion = true;
TargetingSubsystem->ExecuteTargetingRequestWithHandle(TargetingHandle, Delegate);
}
}
}
bool UGCS_TargetingSystemComponent::CanBeTargeted_Implementation(AActor* ActorToTarget)
{
return true;
}
void UGCS_TargetingSystemComponent::StaticSwitchToNewTarget(bool RightDirection)
{
if (TargetedActor)
{
TMap<AActor*, float> LocalPotentialTargets;
for (TObjectPtr<AActor>& Elem : PotentialTargets)
{
if (Elem != TargetedActor)
{
float LocalDistance = UKismetMathLibrary::Vector_Distance(GetOwner()->GetActorLocation(), Elem->GetActorLocation());
FRotator RequiredRotation = UKismetMathLibrary::FindLookAtRotation(GetOwner()->GetActorLocation(), Elem->GetActorLocation());
FRotator DeltaRotation;
ACharacter* LocalOwnerCharacter = Cast<ACharacter>(GetOwner());
DeltaRotation = UKismetMathLibrary::NormalizedDeltaRotator(LocalOwnerCharacter->GetControlRotation(), RequiredRotation);
if (RightDirection == 1)
{
if (DeltaRotation.Yaw < 0.f && DeltaRotation.Yaw > -100.f)
{
LocalPotentialTargets.Add(Elem, DeltaRotation.Yaw);
}
else
{
LocalPotentialTargets.Add(Elem, -10000.f);
}
}
else
{
if (DeltaRotation.Yaw > 0.f && DeltaRotation.Yaw < 100.f)
{
LocalPotentialTargets.Add(Elem, DeltaRotation.Yaw);
}
else
{
LocalPotentialTargets.Add(Elem, 10000.f);
}
}
}
}
if (LocalPotentialTargets.Num() > 0)
{
TArray<AActor*> LocalTargets;
TArray<float> LocalAngles;
LocalPotentialTargets.GenerateKeyArray(LocalTargets);
LocalPotentialTargets.GenerateValueArray(LocalAngles);
if (RightDirection == 1)
{
int32 FoundIndex;
const float MaxValue = FMath::Max<float>(LocalAngles, &FoundIndex);
if (MaxValue > -10000.f)
{
OnLockOff();
SetTargetedActor(LocalTargets[FoundIndex]);
OnLockOn();
}
}
else
{
int32 FoundIndex;
const float MinValue = FMath::Min<float>(LocalAngles, &FoundIndex);
if (MinValue < 10000.f)
{
OnLockOff();
SetTargetedActor(LocalTargets[FoundIndex]);
OnLockOn();
}
}
}
}
}
float UGCS_TargetingSystemComponent::CalculateViewAngle(const AActor* TargetActor)
{
APawn* OwningPawn = GetPawn<APawn>();
FRotator FinalRotation = FRotator::ZeroRotator;
if (TargetActor && OwningPawn)
{
FRotator RequiredRotation = UKismetMathLibrary::FindLookAtRotation(OwningPawn->GetPawnViewLocation(), TargetActor->GetActorLocation());
FRotator DeltaRotation = UKismetMathLibrary::NormalizedDeltaRotator(RequiredRotation, OwningPawn->GetViewRotation());
FinalRotation = DeltaRotation;
}
return UKismetMathLibrary::Abs(UKismetMathLibrary::Abs(FinalRotation.Yaw) + UKismetMathLibrary::Abs(FinalRotation.Pitch));
}

View File

@@ -0,0 +1,369 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Targeting/Selections/GCS_TargetingSelectionTask_LineTrace.h"
#include "CollisionQueryParams.h"
#include "KismetTraceUtils.h"
#include "Components/PrimitiveComponent.h"
#include "Engine/EngineTypes.h"
#include "Engine/World.h"
#include "GameFramework/Actor.h"
#include "GameFramework/Pawn.h"
#include "Kismet/KismetSystemLibrary.h"
#include "TargetingSystem/TargetingSubsystem.h"
#if ENABLE_DRAW_DEBUG
#include "Engine/Canvas.h"
#endif // ENABLE_DRAW_DEBUG
UGCS_TargetingSelectionTask_LineTrace::UGCS_TargetingSelectionTask_LineTrace(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bComplexTrace = false;
bIgnoreSourceActor = false;
bIgnoreInstigatorActor = false;
bGenerateDefaultHitResult = true;
}
void UGCS_TargetingSelectionTask_LineTrace::Execute(const FTargetingRequestHandle& TargetingHandle) const
{
Super::Execute(TargetingHandle);
SetTaskAsyncState(TargetingHandle, ETargetingTaskAsyncState::Executing);
if (IsAsyncTargetingRequest(TargetingHandle))
{
ExecuteAsyncTrace(TargetingHandle);
}
else
{
ExecuteImmediateTrace(TargetingHandle);
}
}
FVector UGCS_TargetingSelectionTask_LineTrace::GetSourceLocation_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
if (SourceContext->SourceActor)
{
return SourceContext->SourceActor->GetActorLocation();
}
return SourceContext->SourceLocation;
}
return FVector::ZeroVector;
}
FVector UGCS_TargetingSelectionTask_LineTrace::GetSourceOffset_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
return DefaultSourceOffset;
}
FVector UGCS_TargetingSelectionTask_LineTrace::GetTraceDirection_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
if (SourceContext->SourceActor)
{
if (APawn* Pawn = Cast<APawn>(SourceContext->SourceActor))
{
return Pawn->GetControlRotation().Vector();
}
else
{
return SourceContext->SourceActor->GetActorForwardVector();
}
}
}
return FVector::ZeroVector;
}
float UGCS_TargetingSelectionTask_LineTrace::GetTraceLength_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
return DefaultTraceLength.GetValueAtLevel(GetTraceLevel(TargetingHandle));
}
void UGCS_TargetingSelectionTask_LineTrace::GetAdditionalActorsToIgnore_Implementation(const FTargetingRequestHandle& TargetingHandle, TArray<AActor*>& OutAdditionalActorsToIgnore) const
{
}
float UGCS_TargetingSelectionTask_LineTrace::GetTraceLevel_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
return 0.0f;
}
void UGCS_TargetingSelectionTask_LineTrace::ExecuteImmediateTrace(const FTargetingRequestHandle& TargetingHandle) const
{
if (UWorld* World = GetSourceContextWorld(TargetingHandle))
{
#if ENABLE_DRAW_DEBUG
ResetTraceResultsDebugString(TargetingHandle);
#endif // ENABLE_DRAW_DEBUG
const FVector Direction = GetTraceDirection(TargetingHandle).GetSafeNormal();
const FVector Start = (GetSourceLocation(TargetingHandle) + GetSourceOffset(TargetingHandle));
const FVector End = Start + (Direction * GetTraceLength(TargetingHandle));
FCollisionQueryParams Params(SCENE_QUERY_STAT(ExecuteImmediateTrace), bComplexTrace);
InitCollisionParams(TargetingHandle, Params);
bool bHasBlockingHit = false;
TArray<FHitResult> Hits;
if (CollisionProfileName.Name != TEXT("NoCollision"))
{
bHasBlockingHit = World->LineTraceMultiByProfile(Hits, Start, End, CollisionProfileName.Name, Params);
}
else
{
const ECollisionChannel CollisionChannel = UEngineTypes::ConvertToCollisionChannel(TraceChannel);
bHasBlockingHit = World->LineTraceMultiByChannel(Hits, Start, End, CollisionChannel, Params);
}
#if ENABLE_DRAW_DEBUG
DrawDebugTrace(TargetingHandle, Start, End, bHasBlockingHit, Hits);
#endif // ENABLE_DRAW_DEBUG
ProcessHitResults(TargetingHandle, Hits);
}
SetTaskAsyncState(TargetingHandle, ETargetingTaskAsyncState::Completed);
}
void UGCS_TargetingSelectionTask_LineTrace::ExecuteAsyncTrace(const FTargetingRequestHandle& TargetingHandle) const
{
if (UWorld* World = GetSourceContextWorld(TargetingHandle))
{
AActor* SourceActor = nullptr;
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
SourceActor = SourceContext->SourceActor;
}
const FVector Direction = GetTraceDirection(TargetingHandle).GetSafeNormal();
const FVector Start = (GetSourceLocation(TargetingHandle) + GetSourceOffset(TargetingHandle));
const FVector End = Start + (Direction * GetTraceLength(TargetingHandle));
FCollisionQueryParams Params(SCENE_QUERY_STAT(ExecuteAsyncTrace), bComplexTrace);
InitCollisionParams(TargetingHandle, Params);
FTraceDelegate Delegate = FTraceDelegate::CreateUObject(this, &UGCS_TargetingSelectionTask_LineTrace::HandleAsyncTraceComplete, TargetingHandle);
if (CollisionProfileName.Name != TEXT("NoCollision"))
{
World->AsyncLineTraceByProfile(EAsyncTraceType::Multi, Start, End, CollisionProfileName.Name, Params, &Delegate);
}
else
{
const ECollisionChannel CollisionChannel = UEngineTypes::ConvertToCollisionChannel(TraceChannel);
World->AsyncLineTraceByChannel(EAsyncTraceType::Multi, Start, End, CollisionChannel, Params, FCollisionResponseParams::DefaultResponseParam, &Delegate);
}
}
else
{
SetTaskAsyncState(TargetingHandle, ETargetingTaskAsyncState::Completed);
}
}
void UGCS_TargetingSelectionTask_LineTrace::HandleAsyncTraceComplete(const FTraceHandle& InTraceHandle, FTraceDatum& InTraceDatum, FTargetingRequestHandle TargetingHandle) const
{
if (TargetingHandle.IsValid())
{
#if ENABLE_DRAW_DEBUG
ResetTraceResultsDebugString(TargetingHandle);
// We have to manually find if there is a blocking hit.
bool bHasBlockingHit = false;
for (const FHitResult& HitResult : InTraceDatum.OutHits)
{
if (HitResult.bBlockingHit)
{
bHasBlockingHit = true;
break;
}
}
DrawDebugTrace(TargetingHandle, InTraceDatum.Start, InTraceDatum.End, bHasBlockingHit, InTraceDatum.OutHits);
#endif // ENABLE_DRAW_DEBUG
ProcessHitResults(TargetingHandle, InTraceDatum.OutHits);
}
SetTaskAsyncState(TargetingHandle, ETargetingTaskAsyncState::Completed);
}
void UGCS_TargetingSelectionTask_LineTrace::ProcessHitResults(const FTargetingRequestHandle& TargetingHandle, const TArray<FHitResult>& Hits) const
{
if (!TargetingHandle.IsValid())
{
return;
}
FTargetingDefaultResultsSet& TargetingResults = FTargetingDefaultResultsSet::FindOrAdd(TargetingHandle);
if (Hits.Num() > 0)
{
for (const FHitResult& HitResult : Hits)
{
if (!HitResult.GetActor())
{
continue;
}
bool bAddResult = true;
for (const FTargetingDefaultResultData& ResultData : TargetingResults.TargetResults)
{
if (ResultData.HitResult.GetActor() == HitResult.GetActor())
{
bAddResult = false;
break;
}
}
if (bAddResult)
{
FTargetingDefaultResultData* ResultData = new(TargetingResults.TargetResults) FTargetingDefaultResultData();
ResultData->HitResult = HitResult;
}
}
#if ENABLE_DRAW_DEBUG
BuildTraceResultsDebugString(TargetingHandle, TargetingResults.TargetResults);
#endif // ENABLE_DRAW_DEBUG
}
else if (bGenerateDefaultHitResult)
{
// If there were no hits, add a default HitResult at the end of the trace
FHitResult HitResult;
const FVector Start = (GetSourceLocation(TargetingHandle) + GetSourceOffset(TargetingHandle));
const FVector End = Start + (GetTraceDirection(TargetingHandle) * GetTraceLength(TargetingHandle));
// Start param could be player ViewPoint. We want HitResult to always display the StartLocation.
HitResult.TraceStart = Start;
HitResult.TraceEnd = End;
HitResult.Location = End;
HitResult.ImpactPoint = End;
FTargetingDefaultResultData* ResultData = new(TargetingResults.TargetResults) FTargetingDefaultResultData();
ResultData->HitResult = HitResult;
}
}
void UGCS_TargetingSelectionTask_LineTrace::InitCollisionParams(const FTargetingRequestHandle& TargetingHandle, FCollisionQueryParams& OutParams) const
{
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
if (bIgnoreSourceActor && SourceContext->SourceActor)
{
OutParams.AddIgnoredActor(SourceContext->SourceActor);
}
if (bIgnoreInstigatorActor && SourceContext->InstigatorActor)
{
OutParams.AddIgnoredActor(SourceContext->InstigatorActor);
}
TArray<AActor*> AdditionalActorsToIgnoreArray;
GetAdditionalActorsToIgnore(TargetingHandle, AdditionalActorsToIgnoreArray);
if (AdditionalActorsToIgnoreArray.Num() > 0)
{
OutParams.AddIgnoredActors(AdditionalActorsToIgnoreArray);
}
}
}
#if WITH_EDITOR
bool UGCS_TargetingSelectionTask_LineTrace::CanEditChange(const FProperty* InProperty) const
{
bool bCanEdit = Super::CanEditChange(InProperty);
if (bCanEdit && InProperty)
{
const FName PropertyName = InProperty->GetFName();
if (PropertyName == GET_MEMBER_NAME_CHECKED(UGCS_TargetingSelectionTask_LineTrace, TraceChannel))
{
return (CollisionProfileName.Name == TEXT("NoCollision"));
}
}
return true;
}
#endif // WITH_EDITOR
#if ENABLE_DRAW_DEBUG
void UGCS_TargetingSelectionTask_LineTrace::DrawDebug(UTargetingSubsystem* TargetingSubsystem, FTargetingDebugInfo& Info, const FTargetingRequestHandle& TargetingHandle, float XOffset, float YOffset,
int32 MinTextRowsToAdvance) const
{
#if WITH_EDITORONLY_DATA
if (UTargetingSubsystem::IsTargetingDebugEnabled())
{
FTargetingDebugData& DebugData = FTargetingDebugData::FindOrAdd(TargetingHandle);
FString& ScratchPadString = DebugData.DebugScratchPadStrings.FindOrAdd(GetNameSafe(this));
if (!ScratchPadString.IsEmpty())
{
if (Info.Canvas)
{
Info.Canvas->SetDrawColor(FColor::Yellow);
}
FString TaskString = FString::Printf(TEXT("Results : %s"), *ScratchPadString);
TargetingSubsystem->DebugLine(Info, TaskString, XOffset, YOffset, MinTextRowsToAdvance);
}
}
#endif // WITH_EDITORONLY_DATA
}
void UGCS_TargetingSelectionTask_LineTrace::DrawDebugTrace(const FTargetingRequestHandle TargetingHandle, const FVector& StartLocation, const FVector& EndLocation, const bool bHit,
const TArray<FHitResult>& Hits) const
{
if (UTargetingSubsystem::IsTargetingDebugEnabled())
{
if (UWorld* World = GetSourceContextWorld(TargetingHandle))
{
const float DrawTime = UTargetingSubsystem::GetOverrideTargetingLifeTime();
const EDrawDebugTrace::Type DrawDebugType = DrawTime <= 0.0f ? EDrawDebugTrace::Type::ForOneFrame : EDrawDebugTrace::Type::ForDuration;
const FLinearColor TraceColor = FLinearColor::Red;
const FLinearColor TraceHitColor = FLinearColor::Green;
DrawDebugLineTraceMulti(World, StartLocation, EndLocation, DrawDebugType, bHit, Hits, TraceColor, TraceHitColor, DrawTime);
}
}
}
void UGCS_TargetingSelectionTask_LineTrace::BuildTraceResultsDebugString(const FTargetingRequestHandle& TargetingHandle, const TArray<FTargetingDefaultResultData>& TargetResults) const
{
#if WITH_EDITORONLY_DATA
if (UTargetingSubsystem::IsTargetingDebugEnabled())
{
FTargetingDebugData& DebugData = FTargetingDebugData::FindOrAdd(TargetingHandle);
FString& ScratchPadString = DebugData.DebugScratchPadStrings.FindOrAdd(GetNameSafe(this));
for (const FTargetingDefaultResultData& TargetData : TargetResults)
{
if (const AActor* Target = TargetData.HitResult.GetActor())
{
if (ScratchPadString.IsEmpty())
{
ScratchPadString = FString::Printf(TEXT("%s"), *GetNameSafe(Target));
}
else
{
ScratchPadString += FString::Printf(TEXT(", %s"), *GetNameSafe(Target));
}
}
}
}
#endif // WITH_EDITORONLY_DATA
}
void UGCS_TargetingSelectionTask_LineTrace::ResetTraceResultsDebugString(const FTargetingRequestHandle& TargetingHandle) const
{
#if WITH_EDITORONLY_DATA
FTargetingDebugData& DebugData = FTargetingDebugData::FindOrAdd(TargetingHandle);
FString& ScratchPadString = DebugData.DebugScratchPadStrings.FindOrAdd(GetNameSafe(this));
ScratchPadString.Reset();
#endif // WITH_EDITORONLY_DATA
}
#endif // ENABLE_DRAW_DEBUG

View File

@@ -0,0 +1,120 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Targeting/Selections/GCS_TargetingSelectionTask_TraceExt.h"
#include "GCS_LogChannels.h"
#include "Collision/DEPRECATED_GCS_CollisionTraceInstance.h"
#include "Targeting/GCS_TargetingSourceInterface.h"
UDEPRECATED_GCS_CollisionTraceInstance* UGCS_TargetingSelectionTask_TraceExt::GetSourceTraceInstance_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
if (SourceContext->SourceObject)
{
return Cast<UDEPRECATED_GCS_CollisionTraceInstance>(SourceContext->SourceObject);
}
}
UE_LOG(LogGCS, Error, TEXT("No valid CollisionTraceInstance passed in as SourceObject! TargetingPreset:%s"), *GetOuter()->GetName());
return nullptr;
}
void UGCS_TargetingSelectionTask_TraceExt::Execute(const FTargetingRequestHandle& TargetingHandle) const
{
Super::Execute(TargetingHandle);
}
FVector UGCS_TargetingSelectionTask_TraceExt::GetSourceLocation_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
if (bUseContextLocationAsSourceLocation)
{
return SourceContext->SourceLocation;
}
if (SourceContext->SourceActor)
{
return SourceContext->SourceActor->GetActorLocation();
}
return SourceContext->SourceLocation;
}
return FVector::ZeroVector;
}
FVector UGCS_TargetingSelectionTask_TraceExt::GetTraceDirection_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
if (SourceContext->SourceObject && SourceContext->SourceObject->GetClass()->ImplementsInterface(UGCS_TargetingSourceInterface::StaticClass()))
{
FVector TraceDirection;
if (IGCS_TargetingSourceInterface::Execute_GetTraceDirection(SourceContext->SourceObject, TraceDirection))
{
return TraceDirection;
}
}
return SourceContext->SourceLocation;
}
return Super::GetTraceDirection_Implementation(TargetingHandle);
}
void UGCS_TargetingSelectionTask_TraceExt::GetAdditionalActorsToIgnore_Implementation(const FTargetingRequestHandle& TargetingHandle, TArray<AActor*>& OutAdditionalActorsToIgnore) const
{
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
if (SourceContext->SourceObject && SourceContext->SourceObject->GetClass()->ImplementsInterface(UGCS_TargetingSourceInterface::StaticClass()))
{
OutAdditionalActorsToIgnore.Append(IGCS_TargetingSourceInterface::Execute_GetAdditionalActorsToIgnore(SourceContext->SourceObject));
}
}
}
float UGCS_TargetingSelectionTask_TraceExt::GetTraceLevel_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
if (!IsValid(SourceContext->SourceObject))
{
UE_LOG(LogGCS, Error, TEXT("No valid Context Source Object found! TargetingPreset:%s"), *GetOuter()->GetName());
return 0;
}
if (!SourceContext->SourceObject->GetClass()->ImplementsInterface(UGCS_TargetingSourceInterface::StaticClass()))
{
UE_LOG(LogGCS, Error, TEXT("Source Object(%s) doesn't implements GCS_TargetingSourceInterface.! TargetingPreset:%s"),
*SourceContext->SourceObject->GetName(), *GetOuter()->GetName());
return 0;
}
}
return 0;
}
float UGCS_TargetingSelectionTask_TraceExt::GetTraceLength_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
return bTraceLengthLevel ? DefaultTraceLength.GetValueAtLevel(GetTraceLevel(TargetingHandle)) : DefaultTraceLength.GetValue();
}
float UGCS_TargetingSelectionTask_TraceExt::GetSweptTraceRadius_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
return bSweptTraceRadiusLevel ? DefaultSweptTraceRadius.GetValueAtLevel(GetTraceLevel(TargetingHandle)) : DefaultSweptTraceRadius.GetValue();
}
float UGCS_TargetingSelectionTask_TraceExt::GetSweptTraceCapsuleHalfHeight_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
return bSweptTraceCapsuleHalfHeightLevel ? DefaultSweptTraceCapsuleHalfHeight.GetValueAtLevel(GetTraceLevel(TargetingHandle)) : DefaultSweptTraceCapsuleHalfHeight.GetValue();
}
FVector UGCS_TargetingSelectionTask_TraceExt::GetSweptTraceBoxHalfExtents_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
if (bSweptTraceBoxHalfExtentLevel)
{
float Level = GetTraceLevel(TargetingHandle);
return FVector(DefaultSweptTraceBoxHalfExtentX.GetValueAtLevel(Level), DefaultSweptTraceBoxHalfExtentY.GetValueAtLevel(Level), DefaultSweptTraceBoxHalfExtentZ.GetValueAtLevel(Level));
}
return Super::GetSweptTraceBoxHalfExtents_Implementation(TargetingHandle);
}

View File

@@ -0,0 +1,212 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Targeting/Selections/GCS_TargetingSelectionTask_TraceExt_BindShape.h"
#include "GCS_LogChannels.h"
#include "Components/BoxComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/SphereComponent.h"
#include "Components/ShapeComponent.h"
#include "Misc/DataValidation.h"
#include "Targeting/GCS_TargetingFunctionLibrary.h"
#include "Targeting/GCS_TargetingSourceInterface.h"
void UGCS_TargetingSelectionTask_TraceExt_BindShape::Execute(const FTargetingRequestHandle& TargetingHandle) const
{
bool bMatchShapeType = true;
UShapeComponent* Shape = GetTraceShape(TargetingHandle);
if (!IsValid(Shape))
{
GCS_LOG(Warning, "")
bMatchShapeType = false;
}
if (TraceType == ETargetingTraceType::Box && !Shape->IsA<UBoxComponent>())
{
GCS_LOG(Warning, "%s: Trace type mismatched! want Box, got %s. %s",
*GetNameSafe(GetOuter()),
*GetNameSafe(Shape->GetClass()),
*UGCS_TargetingFunctionLibrary::GetTargetingSourceContextDebugString(TargetingHandle)
)
bMatchShapeType = false;
}
if (TraceType == ETargetingTraceType::Capsule && !Shape->IsA<UCapsuleComponent>())
{
GCS_LOG(Warning, "%s: Trace type mismatched! want Capsule, got %s. %s",
*GetNameSafe(GetOuter()),
*GetNameSafe(Shape->GetClass()),
*UGCS_TargetingFunctionLibrary::GetTargetingSourceContextDebugString(TargetingHandle)
)
bMatchShapeType = false;
}
if (TraceType == ETargetingTraceType::Sphere && !Shape->IsA<USphereComponent>())
{
GCS_LOG(Warning, "%s: Trace type mismatched! want Capsule, got %s. %s",
*GetNameSafe(GetOuter()),
*GetNameSafe(Shape->GetClass()),
*UGCS_TargetingFunctionLibrary::GetTargetingSourceContextDebugString(TargetingHandle)
)
bMatchShapeType = false;
}
if (bMatchShapeType)
{
Super::Execute(TargetingHandle);
}
else
{
SetTaskAsyncState(TargetingHandle, ETargetingTaskAsyncState::Completed);
}
}
float UGCS_TargetingSelectionTask_TraceExt_BindShape::GetSweptTraceRadius_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
float BaseValue = -1;
if (USphereComponent* Shape = Cast<USphereComponent>(GetTraceShape(TargetingHandle)))
{
BaseValue = Shape->GetScaledSphereRadius();
}
if (const UCapsuleComponent* Shape = Cast<UCapsuleComponent>(GetTraceShape(TargetingHandle)))
{
BaseValue = Shape->GetScaledCapsuleRadius();
}
float Value = Super::GetSweptTraceRadius_Implementation(TargetingHandle);
if (BaseValue < 0)
{
return Value;
}
if (SweptTraceRadiusModType == EGCS_TraceDataModifyType::Add)
{
return BaseValue + Value;
}
if (SweptTraceRadiusModType == EGCS_TraceDataModifyType::Multiply)
{
return BaseValue * Value;
}
return BaseValue;
}
float UGCS_TargetingSelectionTask_TraceExt_BindShape::GetSweptTraceCapsuleHalfHeight_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
float BaseValue = -1;
if (const UCapsuleComponent* Capsule = Cast<UCapsuleComponent>(GetTraceShape(TargetingHandle)))
{
BaseValue = Capsule->GetScaledCapsuleHalfHeight();
}
float Value = Super::GetSweptTraceCapsuleHalfHeight_Implementation(TargetingHandle);
if (BaseValue < 0)
{
return Value;
}
if (SweptTraceCapsuleHalfHeightModType == EGCS_TraceDataModifyType::Add)
{
return BaseValue + Value;
}
if (SweptTraceCapsuleHalfHeightModType == EGCS_TraceDataModifyType::Multiply)
{
return BaseValue * Value;
}
return BaseValue;
}
FVector UGCS_TargetingSelectionTask_TraceExt_BindShape::GetSweptTraceBoxHalfExtents_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
FVector BaseValue = FVector::ZeroVector;
if (const UBoxComponent* Shape = Cast<UBoxComponent>(GetTraceShape(TargetingHandle)))
{
BaseValue = Shape->GetScaledBoxExtent();
}
FVector Value = Super::GetSweptTraceBoxHalfExtents_Implementation(TargetingHandle);
if (BaseValue == FVector::ZeroVector)
{
return Value;
}
if (SweptTraceBoxHalfExtentModType == EGCS_TraceDataModifyType::Add)
{
return BaseValue + Value;
}
if (SweptTraceBoxHalfExtentModType == EGCS_TraceDataModifyType::Multiply)
{
return BaseValue * Value;
}
return BaseValue;
}
UShapeComponent* UGCS_TargetingSelectionTask_TraceExt_BindShape::GetTraceShape(const FTargetingRequestHandle& TargetingHandle) const
{
UShapeComponent* ShapeComponent = nullptr;
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
if (SourceContext->SourceObject)
{
if (SourceContext->SourceObject->GetClass()->ImplementsInterface(UGCS_TargetingSourceInterface::StaticClass()))
{
if (IGCS_TargetingSourceInterface::Execute_GetTraceShape(SourceContext->SourceObject, ShapeComponent))
{
return ShapeComponent;
}
UE_LOG(LogGCS, VeryVerbose, TEXT("Source Object(%s) doesn't provide valid ShapeComponent! TargetingPreset:%s"),
*SourceContext->SourceObject->GetName(), *GetOuter()->GetName());
}
else
{
UE_LOG(LogGCS, VeryVerbose, TEXT("Source Object(%s) doesn't implements GCS_TargetingSourceInterface.! TargetingPreset:%s"),
*SourceContext->SourceObject->GetName(), *GetOuter()->GetName());
}
}
else
{
UE_LOG(LogGCS, Error, TEXT("No valid Context Source Object found! TargetingPreset:%s"), *GetOuter()->GetName());
}
}
return nullptr;
}
FRotator UGCS_TargetingSelectionTask_TraceExt_BindShape::GetSweptTraceRotation_Implementation(const FTargetingRequestHandle& TargetingHandle) const
{
if (const FTargetingSourceContext* SourceContext = FTargetingSourceContext::Find(TargetingHandle))
{
if (SourceContext->SourceObject && SourceContext->SourceObject->GetClass()->ImplementsInterface(UGCS_TargetingSourceInterface::StaticClass()))
{
FRotator TraceRotation;
if (IGCS_TargetingSourceInterface::Execute_GetSweptTraceRotation(SourceContext->SourceObject, TraceRotation))
{
return TraceRotation;
}
}
}
return Super::GetSweptTraceRotation_Implementation(TargetingHandle);
}
#if WITH_EDITORONLY_DATA
EDataValidationResult UGCS_TargetingSelectionTask_TraceExt_BindShape::IsDataValid(class FDataValidationContext& Context) const
{
if (TraceType == ETargetingTraceType::Line)
{
FString Txt = FString::Format(TEXT("TraceType == Line is not allowed in this type of task:{0} "), {GetClass()->GetName()});
Context.AddError(FText::FromString(Txt));
}
if (bUseContextLocationAsSourceLocation)
{
FString Txt = FString::Format(TEXT("bUseContextLocationAsSourceLocation is not allowed in this type of task:{0} "), {GetClass()->GetName()});
Context.AddError(FText::FromString(Txt));
}
return Super::IsDataValid(Context);
}
#endif