145 lines
5.4 KiB
C++
145 lines
5.4 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "LyraDamageExecution.h"
|
|
#include "GameplayEffectTypes.h"
|
|
#include "AbilitySystem/Attributes/LyraHealthSet.h"
|
|
#include "AbilitySystem/Attributes/LyraCombatSet.h"
|
|
#include "AbilitySystem/LyraGameplayEffectContext.h"
|
|
#include "AbilitySystem/LyraAbilitySourceInterface.h"
|
|
#include "Teams/LyraTeamSubsystem.h"
|
|
|
|
struct FDamageStatics
|
|
{
|
|
FGameplayEffectAttributeCaptureDefinition HealthDef;
|
|
FGameplayEffectAttributeCaptureDefinition BaseDamageDef;
|
|
|
|
FDamageStatics()
|
|
{
|
|
HealthDef = FGameplayEffectAttributeCaptureDefinition(ULyraHealthSet::GetHealthAttribute(), EGameplayEffectAttributeCaptureSource::Target, false);
|
|
BaseDamageDef = FGameplayEffectAttributeCaptureDefinition(ULyraCombatSet::GetBaseDamageAttribute(), EGameplayEffectAttributeCaptureSource::Source, true);
|
|
}
|
|
};
|
|
|
|
static FDamageStatics& DamageStatics()
|
|
{
|
|
static FDamageStatics Statics;
|
|
return Statics;
|
|
}
|
|
|
|
|
|
ULyraDamageExecution::ULyraDamageExecution()
|
|
{
|
|
RelevantAttributesToCapture.Add(DamageStatics().HealthDef);
|
|
RelevantAttributesToCapture.Add(DamageStatics().BaseDamageDef);
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
InvalidScopedModifierAttributes.Add(DamageStatics().HealthDef);
|
|
#endif // #if WITH_EDITORONLY_DATA
|
|
}
|
|
|
|
void ULyraDamageExecution::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
|
|
{
|
|
#if WITH_SERVER_CODE
|
|
const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();
|
|
FLyraGameplayEffectContext* TypedContext = FLyraGameplayEffectContext::ExtractEffectContext(Spec.GetContext());
|
|
check(TypedContext);
|
|
|
|
const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
|
const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
|
|
|
FAggregatorEvaluateParameters EvaluateParameters;
|
|
EvaluateParameters.SourceTags = SourceTags;
|
|
EvaluateParameters.TargetTags = TargetTags;
|
|
|
|
float CurrentHealth = 0.0f;
|
|
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().HealthDef, EvaluateParameters, CurrentHealth);
|
|
|
|
float BaseDamage = 0.0f;
|
|
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().BaseDamageDef, EvaluateParameters, BaseDamage);
|
|
|
|
|
|
|
|
const AActor* EffectCauser = TypedContext->GetEffectCauser();
|
|
const FHitResult* HitActorResult = TypedContext->GetHitResult();
|
|
|
|
AActor* HitActor = nullptr;
|
|
FVector ImpactLocation = FVector::ZeroVector;
|
|
FVector ImpactNormal = FVector::ZeroVector;
|
|
FVector StartTrace = FVector::ZeroVector;
|
|
FVector EndTrace = FVector::ZeroVector;
|
|
|
|
// Calculation of hit actor, surface, zone, and distance all rely on whether the calculation has a hit result or not.
|
|
// Effects just being added directly w/o having been targeted will always come in without a hit result, which must default
|
|
// to some fallback information.
|
|
if (HitActorResult)
|
|
{
|
|
const FHitResult& CurHitResult = *HitActorResult;
|
|
HitActor = CurHitResult.HitObjectHandle.FetchActor();
|
|
if (HitActor)
|
|
{
|
|
ImpactLocation = CurHitResult.ImpactPoint;
|
|
ImpactNormal = CurHitResult.ImpactNormal;
|
|
StartTrace = CurHitResult.TraceStart;
|
|
EndTrace = CurHitResult.TraceEnd;
|
|
}
|
|
}
|
|
|
|
// Handle case of no hit result or hit result not actually returning an actor
|
|
UAbilitySystemComponent* TargetAbilitySystemComponent = ExecutionParams.GetTargetAbilitySystemComponent();
|
|
if (!HitActor)
|
|
{
|
|
HitActor = TargetAbilitySystemComponent ? TargetAbilitySystemComponent->GetAvatarActor_Direct() : nullptr;
|
|
if (HitActor)
|
|
{
|
|
ImpactLocation = HitActor->GetActorLocation();
|
|
}
|
|
}
|
|
|
|
// Apply rules for team damage/self damage/etc...
|
|
float DamageInteractionAllowedMultiplier = 0.0f;
|
|
if (HitActor)
|
|
{
|
|
ULyraTeamSubsystem* TeamSubsystem = HitActor->GetWorld()->GetSubsystem<ULyraTeamSubsystem>();
|
|
DamageInteractionAllowedMultiplier = TeamSubsystem->CanCauseDamage(EffectCauser, HitActor) ? 1.0 : 0.0;
|
|
}
|
|
|
|
// Determine distance
|
|
float Distance = WORLD_MAX;
|
|
|
|
if (TypedContext->HasOrigin())
|
|
{
|
|
Distance = FVector::Dist(TypedContext->GetOrigin(), ImpactLocation);
|
|
}
|
|
else if (EffectCauser)
|
|
{
|
|
Distance = FVector::Dist(EffectCauser->GetActorLocation(), ImpactLocation);
|
|
}
|
|
else
|
|
{
|
|
ensureMsgf(false, TEXT("Damage Calculation cannot deduce a source location for damage coming from %s; Falling back to WORLD_MAX dist!"), *GetPathNameSafe(Spec.Def));
|
|
}
|
|
|
|
// Apply ability source modifiers
|
|
float PhysicalMaterialAttenuation = 1.0f;
|
|
float DistanceAttenuation = 1.0f;
|
|
if (const ILyraAbilitySourceInterface* AbilitySource = TypedContext->GetAbilitySource())
|
|
{
|
|
if (const UPhysicalMaterial* PhysMat = TypedContext->GetPhysicalMaterial())
|
|
{
|
|
PhysicalMaterialAttenuation = AbilitySource->GetPhysicalMaterialAttenuation(PhysMat, SourceTags, TargetTags);
|
|
}
|
|
|
|
DistanceAttenuation = AbilitySource->GetDistanceAttenuation(Distance, SourceTags, TargetTags);
|
|
}
|
|
DistanceAttenuation = FMath::Max(DistanceAttenuation, 0.0f);
|
|
|
|
// This clamp prevents us from doing more damage than there is health available.
|
|
const float DamageDone = FMath::Clamp(BaseDamage * DistanceAttenuation * PhysicalMaterialAttenuation * DamageInteractionAllowedMultiplier, 0.0f, CurrentHealth);
|
|
|
|
if (DamageDone > 0.0f)
|
|
{
|
|
OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(ULyraHealthSet::GetHealthAttribute(), EGameplayModOp::Additive, -DamageDone));
|
|
}
|
|
#endif // #if WITH_SERVER_CODE
|
|
}
|