diff --git a/Config/Tags/PF2GameplayCues.ini b/Config/Tags/PF2GameplayCues.ini new file mode 100644 index 000000000..3a8e3dee3 --- /dev/null +++ b/Config/Tags/PF2GameplayCues.ini @@ -0,0 +1,8 @@ +; OpenPF2 for UE Game Logic, Copyright 2023, Guy Elsmore-Paddock. All Rights Reserved. +; +; This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not +; distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +; Gameplay Cues that provide special FX and sound FX in response to effect activations. +[/Script/GameplayTags.GameplayTagsList] +GameplayTagList=(Tag="GameplayCue.Character.InflictDamage", DevComment="Gameplay cue fired whenever one character inflicts damage on another.") diff --git a/Source/OpenPF2Core/Private/Calculations/PF2ApplyDamageFromSourceExecution.cpp b/Source/OpenPF2Core/Private/Calculations/PF2ApplyDamageFromSourceExecution.cpp index e17fb0130..c0ce11c8c 100644 --- a/Source/OpenPF2Core/Private/Calculations/PF2ApplyDamageFromSourceExecution.cpp +++ b/Source/OpenPF2Core/Private/Calculations/PF2ApplyDamageFromSourceExecution.cpp @@ -17,6 +17,8 @@ #include "Libraries/PF2AbilitySystemLibrary.h" +#include "Utilities/PF2GameplayAbilityUtilities.h" + UPF2ApplyDamageFromSourceExecution::UPF2ApplyDamageFromSourceExecution() { const FPF2AttackAttributeStatics AttackCaptures = FPF2AttackAttributeStatics::GetInstance(); @@ -28,15 +30,22 @@ UPF2ApplyDamageFromSourceExecution::UPF2ApplyDamageFromSourceExecution() { this->RelevantAttributesToCapture.Add(*Capture); } + + // Cache the tag to avoid lookup overhead. + this->InflictDamageCueTag = PF2GameplayAbilityUtilities::GetTag( + FName("GameplayCue.Character.InflictDamage") + ); } void UPF2ApplyDamageFromSourceExecution::Execute_Implementation( const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const { + UAbilitySystemComponent* TargetAsc = ExecutionParams.GetTargetAbilitySystemComponent(); const FPF2AttackAttributeStatics AttackCaptures = FPF2AttackAttributeStatics::GetInstance(); const FPF2TargetCharacterAttributeStatics TargetCaptures = FPF2TargetCharacterAttributeStatics::GetInstance(); float AttackDegreeOfSuccess = 0.0f; + bool bHaveAnyDamage = false; const FAggregatorEvaluateParameters EvaluationParameters = UPF2AbilitySystemLibrary::BuildEvaluationParameters(ExecutionParams); @@ -102,6 +111,8 @@ void UPF2ApplyDamageFromSourceExecution::Execute_Implementation( if (EffectiveDamage > 0) { + FGameplayCueParameters CueParams = PopulateGameplayCueParameters(ExecutionParams); + // Apply: Damage, less resistance. OutExecutionOutput.AddOutputModifier( FGameplayModifierEvaluatedData( @@ -110,6 +121,38 @@ void UPF2ApplyDamageFromSourceExecution::Execute_Implementation( EffectiveDamage ) ); + + bHaveAnyDamage = true; + + // For now, pass the damage type along as a source tag. These feels like a hack, but saves us from having to + // define a custom parameter object and/or context object to pass along inside the parameter object. + // + // An alternative would be to pass the damage type along in the OriginalTag field, but the intent of that + // field appears to be to capture what gameplay tag was emitted by a GE to locate the cue. The + // MatchedTagName field, meanwhile, appears to be for holding the name of the tag that the selected cue has. + CueParams.AggregatedSourceTags.AddTag( + AttackCaptures.GetDamageTypeForDamageAttribute(Capture->AttributeToCapture) + ); + + CueParams.RawMagnitude = EffectiveDamage; + + TargetAsc->ExecuteGameplayCue( + this->InflictDamageCueTag, + CueParams + ); } } + + if (!bHaveAnyDamage) + { + // Fire off a cue for a miss (no damage), so that the player can see a zero. + FGameplayCueParameters CueParams = PopulateGameplayCueParameters(ExecutionParams); + + CueParams.RawMagnitude = 0; + + TargetAsc->ExecuteGameplayCue( + this->InflictDamageCueTag, + CueParams + ); + } } diff --git a/Source/OpenPF2Core/Public/Calculations/PF2ApplyDamageFromSourceExecution.h b/Source/OpenPF2Core/Public/Calculations/PF2ApplyDamageFromSourceExecution.h index 830778fb4..4cd978b4e 100644 --- a/Source/OpenPF2Core/Public/Calculations/PF2ApplyDamageFromSourceExecution.h +++ b/Source/OpenPF2Core/Public/Calculations/PF2ApplyDamageFromSourceExecution.h @@ -28,6 +28,11 @@ class OPENPF2CORE_API UPF2ApplyDamageFromSourceExecution : public UGameplayEffec { GENERATED_BODY() + /** + * The gameplay tag for gameplay cues that activate upon damage being inflicted to the target. + */ + FGameplayTag InflictDamageCueTag; + public: // ================================================================================================================= // Constructors @@ -42,4 +47,32 @@ class OPENPF2CORE_API UPF2ApplyDamageFromSourceExecution : public UGameplayEffec // ================================================================================================================= virtual void Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, OUT FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override; + +protected: + /** + * Gets the gameplay tag for gameplay cues that activate upon damage being inflicted to the target. + * + * @return + * The gameplay tag for the damage cue. + */ + FORCEINLINE const FGameplayTag& GetInflictDamageCueTag() const + { + return this->InflictDamageCueTag; + } + + /** + * Populates parameters from a gameplay cue from the parameters of the current GE execution. + * + * @param [in] ExecutionParams + * The parameters passed to the current GE execution. + * + * @return + * The new Gameplay Cue parameters. + */ + FORCEINLINE FGameplayCueParameters PopulateGameplayCueParameters( + const FGameplayEffectCustomExecutionParameters& ExecutionParams) const + { + return FGameplayCueParameters(ExecutionParams.GetOwningSpec().GetEffectContext()); + } + };