RealtimeStyleTransferRuntime/Source/LyraGame/Interaction/Tasks/AbilityTask_WaitForInteract...

177 lines
6.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AbilityTask_WaitForInteractableTargets.h"
#include "GameFramework/Actor.h"
#include "Physics/LyraCollisionChannels.h"
#include "Interaction/IInteractableTarget.h"
#include "Interaction/InteractionStatics.h"
#include "Interaction/InteractionQuery.h"
#include "AbilitySystemComponent.h"
#include "GameFramework/PlayerController.h"
UAbilityTask_WaitForInteractableTargets::UAbilityTask_WaitForInteractableTargets(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UAbilityTask_WaitForInteractableTargets::LineTrace(FHitResult& OutHitResult, const UWorld* World, const FVector& Start, const FVector& End, FName ProfileName, const FCollisionQueryParams Params)
{
check(World);
OutHitResult = FHitResult();
TArray<FHitResult> HitResults;
World->LineTraceMultiByProfile(HitResults, Start, End, ProfileName, Params);
OutHitResult.TraceStart = Start;
OutHitResult.TraceEnd = End;
if (HitResults.Num() > 0)
{
OutHitResult = HitResults[0];
}
}
void UAbilityTask_WaitForInteractableTargets::AimWithPlayerController(const AActor* InSourceActor, FCollisionQueryParams Params, const FVector& TraceStart, float MaxRange, FVector& OutTraceEnd, bool bIgnorePitch) const
{
if (!Ability) // Server and launching client only
{
return;
}
//@TODO: Bots?
APlayerController* PC = Ability->GetCurrentActorInfo()->PlayerController.Get();
check(PC);
FVector ViewStart;
FRotator ViewRot;
PC->GetPlayerViewPoint(ViewStart, ViewRot);
const FVector ViewDir = ViewRot.Vector();
FVector ViewEnd = ViewStart + (ViewDir * MaxRange);
ClipCameraRayToAbilityRange(ViewStart, ViewDir, TraceStart, MaxRange, ViewEnd);
FHitResult HitResult;
LineTrace(HitResult, InSourceActor->GetWorld(), ViewStart, ViewEnd, TraceProfile.Name, Params);
const bool bUseTraceResult = HitResult.bBlockingHit && (FVector::DistSquared(TraceStart, HitResult.Location) <= (MaxRange * MaxRange));
const FVector AdjustedEnd = (bUseTraceResult) ? HitResult.Location : ViewEnd;
FVector AdjustedAimDir = (AdjustedEnd - TraceStart).GetSafeNormal();
if (AdjustedAimDir.IsZero())
{
AdjustedAimDir = ViewDir;
}
if (!bTraceAffectsAimPitch && bUseTraceResult)
{
FVector OriginalAimDir = (ViewEnd - TraceStart).GetSafeNormal();
if (!OriginalAimDir.IsZero())
{
// Convert to angles and use original pitch
const FRotator OriginalAimRot = OriginalAimDir.Rotation();
FRotator AdjustedAimRot = AdjustedAimDir.Rotation();
AdjustedAimRot.Pitch = OriginalAimRot.Pitch;
AdjustedAimDir = AdjustedAimRot.Vector();
}
}
OutTraceEnd = TraceStart + (AdjustedAimDir * MaxRange);
}
bool UAbilityTask_WaitForInteractableTargets::ClipCameraRayToAbilityRange(FVector CameraLocation, FVector CameraDirection, FVector AbilityCenter, float AbilityRange, FVector& ClippedPosition)
{
FVector CameraToCenter = AbilityCenter - CameraLocation;
float DotToCenter = FVector::DotProduct(CameraToCenter, CameraDirection);
if (DotToCenter >= 0) //If this fails, we're pointed away from the center, but we might be inside the sphere and able to find a good exit point.
{
float DistanceSquared = CameraToCenter.SizeSquared() - (DotToCenter * DotToCenter);
float RadiusSquared = (AbilityRange * AbilityRange);
if (DistanceSquared <= RadiusSquared)
{
float DistanceFromCamera = FMath::Sqrt(RadiusSquared - DistanceSquared);
float DistanceAlongRay = DotToCenter + DistanceFromCamera; //Subtracting instead of adding will get the other intersection point
ClippedPosition = CameraLocation + (DistanceAlongRay * CameraDirection); //Cam aim point clipped to range sphere
return true;
}
}
return false;
}
void UAbilityTask_WaitForInteractableTargets::UpdateInteractableOptions(const FInteractionQuery& InteractQuery, const TArray<TScriptInterface<IInteractableTarget>>& InteractableTargets)
{
TArray<FInteractionOption> NewOptions;
for (const TScriptInterface<IInteractableTarget>& InteractiveTarget : InteractableTargets)
{
TArray<FInteractionOption> TempOptions;
FInteractionOptionBuilder InteractionBuilder(InteractiveTarget, TempOptions);
InteractiveTarget->GatherInteractionOptions(InteractQuery, InteractionBuilder);
for (FInteractionOption& Option : TempOptions)
{
FGameplayAbilitySpec* InteractionAbilitySpec = nullptr;
// if there is a handle an a target ability system, we're triggering the ability on the target.
if (Option.TargetAbilitySystem && Option.TargetInteractionAbilityHandle.IsValid())
{
// Find the spec
InteractionAbilitySpec = Option.TargetAbilitySystem->FindAbilitySpecFromHandle(Option.TargetInteractionAbilityHandle);
}
// If there's an interaction ability then we're activating it on ourselves.
else if (Option.InteractionAbilityToGrant)
{
// Find the spec
InteractionAbilitySpec = AbilitySystemComponent->FindAbilitySpecFromClass(Option.InteractionAbilityToGrant);
if (InteractionAbilitySpec)
{
// update the option
Option.TargetAbilitySystem = AbilitySystemComponent.Get();
Option.TargetInteractionAbilityHandle = InteractionAbilitySpec->Handle;
}
}
if (InteractionAbilitySpec)
{
// Filter any options that we can't activate right now for whatever reason.
if (InteractionAbilitySpec->Ability->CanActivateAbility(InteractionAbilitySpec->Handle, AbilitySystemComponent->AbilityActorInfo.Get()))
{
NewOptions.Add(Option);
}
}
}
}
bool bOptionsChanged = false;
if (NewOptions.Num() == CurrentOptions.Num())
{
NewOptions.Sort();
for (int OptionIndex = 0; OptionIndex < NewOptions.Num(); OptionIndex++)
{
const FInteractionOption& NewOption = NewOptions[OptionIndex];
const FInteractionOption& CurrentOption = CurrentOptions[OptionIndex];
if (NewOption != CurrentOption)
{
bOptionsChanged = true;
break;
}
}
}
else
{
bOptionsChanged = true;
}
if (bOptionsChanged)
{
CurrentOptions = NewOptions;
InteractableObjectsChanged.Broadcast(CurrentOptions);
}
}