Files
Aura-TopDownRPG-GAS/Source/Aura/Private/AbilitySystem/MMC/ExecCalc_Damage.cpp

128 lines
5.9 KiB
C++

// Copyright Echo Devgroup
#include "AbilitySystem/MMC/ExecCalc_Damage.h"
#include "AbilitySystemComponent.h"
#include "AuraGameplayTags.h"
#include "AbilitySystem/AuraAbilitySystemLibrary.h"
#include "AbilitySystem/AuraAttributeSet.h"
#include "AbilitySystem/Data/CharacterClassInfo.h"
#include "Interact/CombatInterface.h"
struct AuraDamageStatics
{
DECLARE_ATTRIBUTE_CAPTUREDEF(Armor);
DECLARE_ATTRIBUTE_CAPTUREDEF(ArmorPenetration);
DECLARE_ATTRIBUTE_CAPTUREDEF(BlockChance);
DECLARE_ATTRIBUTE_CAPTUREDEF(CriticalChance);
DECLARE_ATTRIBUTE_CAPTUREDEF(CriticalResistance);
DECLARE_ATTRIBUTE_CAPTUREDEF(CriticalDamage);
AuraDamageStatics()
{
DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, Armor, Target, false);
DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, ArmorPenetration, Source, false);
DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, BlockChance, Target, false);
DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, CriticalChance, Source, false);
DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, CriticalResistance, Target, false);
DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, CriticalDamage, Source, false);
}
};
static const AuraDamageStatics DamageStatics()
{
static AuraDamageStatics DStatics;
return DStatics;
}
UExecCalc_Damage::UExecCalc_Damage()
{
RelevantAttributesToCapture.Add(DamageStatics().ArmorDef);
RelevantAttributesToCapture.Add(DamageStatics().BlockChanceDef);
RelevantAttributesToCapture.Add(DamageStatics().ArmorPenetrationDef);
RelevantAttributesToCapture.Add(DamageStatics().CriticalChanceDef);
RelevantAttributesToCapture.Add(DamageStatics().CriticalResistanceDef);
RelevantAttributesToCapture.Add(DamageStatics().CriticalDamageDef);
}
void UExecCalc_Damage::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams,
FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
{
//Set Gameplay Effect variables
const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();
//Get Source info
const UAbilitySystemComponent* SourceASC = ExecutionParams.GetSourceAbilitySystemComponent();
AActor* SourceAvatar = SourceASC ? SourceASC->GetAvatarActor() : nullptr;
ICombatInterface* SourceCombatInterface = Cast<ICombatInterface>(SourceAvatar);
const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
//Get Target info
const UAbilitySystemComponent* TargetASC = ExecutionParams.GetTargetAbilitySystemComponent();
AActor* TargetAvatar = TargetASC ? TargetASC->GetAvatarActor() : nullptr;
ICombatInterface* TargetCombatInterface = Cast<ICombatInterface>(TargetAvatar);
const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
FAggregatorEvaluateParameters EvaluateParams;
EvaluateParams.SourceTags = SourceTags;
EvaluateParams.TargetTags = TargetTags;
//Get Damage set by caller magnitude
float Damage = Spec.GetSetByCallerMagnitude(FAuraGameplayTags::Get().Damage);
//Capture BlockChance on Target, and determine if there was a successful block
//If blocked, reduce damage to 50% damage
float TargetBlockChance = 0.f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().BlockChanceDef, EvaluateParams, TargetBlockChance);
TargetBlockChance = FMath::Max<float>(TargetBlockChance, 0.f);
const bool bBlocked = FMath::RandRange(1, 100) < TargetBlockChance;
Damage = bBlocked ? Damage / 2.f : Damage;
//TODO: Elemental weaknesses
//TODO: Add Resistances
const UCharacterClassInfo* CharacterClassInfo = UAuraAbilitySystemLibrary::GetCharacterClassInfo(SourceAvatar);
const FRealCurve* ArmorPenetrationCurve = CharacterClassInfo->DamageCalculationCoefficients->FindCurve(FName("ArmorPenetration"), FString());
const float ArmorPenetrationCoefficient = ArmorPenetrationCurve->Eval(SourceCombatInterface->GetPlayerLevel());
const FRealCurve* EffectiveArmorCurve = CharacterClassInfo->DamageCalculationCoefficients->FindCurve(FName("EffectiveArmor"), FString());
const float EffectiveArmorCoefficient = ArmorPenetrationCurve->Eval(TargetCombatInterface->GetPlayerLevel());
//Armor calculations
//Get the Target's armor
float TargetArmor = 0.f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().ArmorDef, EvaluateParams, TargetArmor);
TargetArmor = FMath::Max<float>(TargetArmor, 0.f);
//Source Armor Penetration, ignores a percentage of the Target's Armor.
float SourceArmorPenetration = 0.f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().ArmorPenetrationDef, EvaluateParams, SourceArmorPenetration);
SourceArmorPenetration = FMath::Max<float>(SourceArmorPenetration, 0.f);
//4 points Armor penetration is 1% armor ignored
const float EffectiveArmor = TargetArmor *= (100 - SourceArmorPenetration * ArmorPenetrationCoefficient) / 100.f;
//Damage reduced by Armor % where 5 armor = 1%
Damage *= (100 - EffectiveArmor * EffectiveArmorCoefficient) / 100.f;
//Critical Hit Chance
float SourceCriticalChance = 0.f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().CriticalChanceDef, EvaluateParams, SourceCriticalChance);
SourceCriticalChance = FMath::Max<float>(SourceCriticalChance, 0.f);
float TargetCriticalResistance = 0.f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().CriticalResistanceDef, EvaluateParams, TargetCriticalResistance);
TargetCriticalResistance = FMath::Max<float>(TargetCriticalResistance, 0.f);
float SourceCriticalDamage = 0.f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().CriticalDamageDef, EvaluateParams, SourceCriticalDamage);
SourceCriticalDamage = FMath::Max<float>(SourceCriticalDamage, 0.f);
const bool bCritical = FMath::RandRange(1, 100) < (SourceCriticalChance - TargetCriticalResistance);
Damage = bCritical ? Damage * 1+SourceCriticalDamage : Damage;
const FGameplayModifierEvaluatedData EvaluatedData(UAuraAttributeSet::GetIncomingDamageAttribute(), EGameplayModOp::Additive, Damage);
OutExecutionOutput.AddOutputModifier(EvaluatedData);
}