// 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(); 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 }