// 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(SourceAvatar); const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags(); //Get Target info const UAbilitySystemComponent* TargetASC = ExecutionParams.GetTargetAbilitySystemComponent(); AActor* TargetAvatar = TargetASC ? TargetASC->GetAvatarActor() : nullptr; ICombatInterface* TargetCombatInterface = Cast(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(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(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(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(SourceCriticalChance, 0.f); float TargetCriticalResistance = 0.f; ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().CriticalResistanceDef, EvaluateParams, TargetCriticalResistance); TargetCriticalResistance = FMath::Max(TargetCriticalResistance, 0.f); float SourceCriticalDamage = 0.f; ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().CriticalDamageDef, EvaluateParams, SourceCriticalDamage); SourceCriticalDamage = FMath::Max(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); }