Skip to content

Commit

Permalink
[#60] Switch TEML Calculations to Use Native Tags instead of Strings
Browse files Browse the repository at this point in the history
This makes the code less fragile if tags get renamed in the future,
since the tag names no longer appear in the code as strings. If the
variable name is wrong, it will fail to build, rather than it being a
run-time error.
  • Loading branch information
GuyPaddock committed Jul 19, 2024
1 parent 1b24bf4 commit 7adc4e1
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

#include "Abilities/Attacks/PF2SpellAttackRollCalculation.h"

#include "GameplayTags/Stats/KeyAbilities.h"
#include "GameplayTags/Stats/Proficiencies/SpellAttacks.h"

UPF2SpellAttackRollCalculation::UPF2SpellAttackRollCalculation() :
UPF2KeyAbilityTemlCalculationBase(TEXT("PF2.Proficiency.SpellAttack"), TEXT("PF2.SpellcastingAbility"))
UPF2KeyAbilityTemlCalculationBase(Pf2TagProficiencySpellAttack, Pf2TagSpellcastingAbilities)
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

#include "CharacterStats/PF2ClassDifficultyClassCalculation.h"

#include "GameplayTags/Stats/KeyAbilities.h"
#include "GameplayTags/Stats/Proficiencies/ClassDc.h"

UPF2ClassDifficultyClassCalculation::UPF2ClassDifficultyClassCalculation() :
UPF2KeyAbilityTemlCalculationBase(TEXT("PF2.Proficiency.ClassDc"), TEXT("PF2.KeyAbility"), 10.0f)
UPF2KeyAbilityTemlCalculationBase(Pf2TagProficiencyClassDc, Pf2TagKeyAbilities, 10.0f)
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,69 +17,65 @@
#include "CharacterStats/PF2CharacterAttributeSet.h"
#include "CharacterStats/PF2TemlCalculation.h"

#include "GameplayTags/Stats/KeyAbilities.h"

#include "Libraries/PF2AbilitySystemLibrary.h"
#include "Libraries/PF2TagLibrary.h"

#include "Utilities/PF2GameplayAbilityUtilities.h"

UPF2KeyAbilityTemlCalculationBase::UPF2KeyAbilityTemlCalculationBase() :
UPF2KeyAbilityTemlCalculationBase(
TEXT(""),
TEXT("PF2.KeyAbility")
FGameplayTag(),
Pf2TagKeyAbilities
)
{
}

UPF2KeyAbilityTemlCalculationBase::UPF2KeyAbilityTemlCalculationBase(
const FString& StatGameplayTagPrefix,
const FString& KeyAbilityGameplayTagPrefix,
const float BaseValue) :
StatGameplayTagPrefix(StatGameplayTagPrefix),
UPF2KeyAbilityTemlCalculationBase::UPF2KeyAbilityTemlCalculationBase(const FGameplayTag& StatPrefixTag,
const FGameplayTag& KeyAbilityPrefixTag,
const float BaseValue) :
StatPrefixTag(StatPrefixTag),
BaseValue(BaseValue)
{
this->DefineKeyAbilityCapture(
KeyAbilityGameplayTagPrefix + ".Strength",
UPF2TagLibrary::RequestCombinedTagByString(KeyAbilityPrefixTag, TEXT("Strength")),
UPF2CharacterAttributeSet::GetAbStrengthModifierAttribute()
);

this->DefineKeyAbilityCapture(
KeyAbilityGameplayTagPrefix + ".Dexterity",
UPF2TagLibrary::RequestCombinedTagByString(KeyAbilityPrefixTag, TEXT("Dexterity")),
UPF2CharacterAttributeSet::GetAbDexterityModifierAttribute()
);

this->DefineKeyAbilityCapture(
KeyAbilityGameplayTagPrefix + ".Constitution",
UPF2TagLibrary::RequestCombinedTagByString(KeyAbilityPrefixTag, TEXT("Constitution")),
UPF2CharacterAttributeSet::GetAbConstitutionModifierAttribute()
);

this->DefineKeyAbilityCapture(
KeyAbilityGameplayTagPrefix + ".Intelligence",
UPF2TagLibrary::RequestCombinedTagByString(KeyAbilityPrefixTag, TEXT("Intelligence")),
UPF2CharacterAttributeSet::GetAbIntelligenceModifierAttribute()
);

this->DefineKeyAbilityCapture(
KeyAbilityGameplayTagPrefix + ".Wisdom",
UPF2TagLibrary::RequestCombinedTagByString(KeyAbilityPrefixTag, TEXT("Wisdom")),
UPF2CharacterAttributeSet::GetAbWisdomModifierAttribute()
);

this->DefineKeyAbilityCapture(
KeyAbilityGameplayTagPrefix + ".Charisma",
UPF2TagLibrary::RequestCombinedTagByString(KeyAbilityPrefixTag, TEXT("Charisma")),
UPF2CharacterAttributeSet::GetAbCharismaModifierAttribute()
);
}

void UPF2KeyAbilityTemlCalculationBase::DefineKeyAbilityCapture(
const FString& KeyAbilityTagName,
const FGameplayAttribute& Attribute)
void UPF2KeyAbilityTemlCalculationBase::DefineKeyAbilityCapture(const FGameplayTag& KeyAbilityTag,
const FGameplayAttribute& Attribute)
{
const FGameplayEffectAttributeCaptureDefinition CaptureDefinition =
PF2GameplayAbilityUtilities::BuildSourceCaptureFor(Attribute);

this->KeyAbilityCaptureDefinitions.Add(
KeyAbilityTagName,
CaptureDefinition
);

this->KeyAbilityCaptureDefinitions.Add(KeyAbilityTag, CaptureDefinition);
this->RelevantAttributesToCapture.Add(CaptureDefinition);
}

Expand All @@ -96,15 +92,15 @@ float UPF2KeyAbilityTemlCalculationBase::CalculateBaseMagnitude_Implementation(c
// Spell DC = 10 + your spellcasting ability modifier + proficiency bonus + other bonuses + penalties"
//
// Source: Pathfinder 2E Core Rulebook, page 298, "Spell Attack Roll and Spell DC".
const float ProficiencyBonus = FPF2TemlCalculation(this->StatGameplayTagPrefix, Spec).GetValue(),
const float ProficiencyBonus = FPF2TemlCalculation(this->StatPrefixTag, Spec).GetValue(),
KeyAbilityModifier = this->CalculateKeyAbilityModifier(Spec),
AbilityScore = this->BaseValue + ProficiencyBonus + KeyAbilityModifier;

UE_LOG(
LogPf2Stats,
VeryVerbose,
TEXT("Calculated key ability score ('%s'): %f + %f + %f = %f"),
*(this->StatGameplayTagPrefix),
*(this->StatPrefixTag.ToString()),
this->BaseValue,
ProficiencyBonus,
KeyAbilityModifier,
Expand All @@ -119,6 +115,7 @@ float UPF2KeyAbilityTemlCalculationBase::CalculateKeyAbilityModifier(const FGame
float KeyAbilityModifier = 0.0f;
const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();

// ReSharper disable once CppTooWideScopeInitStatement
const FGameplayEffectAttributeCaptureDefinition KeyAbilityCaptureDefinition =
this->DetermineKeyAbility(SourceTags);

Expand All @@ -145,7 +142,7 @@ FGameplayEffectAttributeCaptureDefinition UPF2KeyAbilityTemlCalculationBase::Det

for (auto PairIterator = this->KeyAbilityCaptureDefinitions.CreateConstIterator(); PairIterator; ++PairIterator)
{
if (const FString TagName = PairIterator.Key(); UPF2TagLibrary::ContainerHasTag(*SourceTags, TagName))
if (const FGameplayTag Tag = PairIterator.Key(); SourceTags->HasTag(Tag))
{
KeyAbilityCaptureDefinition = PairIterator.Value();
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

#include "CharacterStats/PF2SpellDifficultyClassCalculation.h"

#include "GameplayTags/Stats/KeyAbilities.h"
#include "GameplayTags/Stats/Proficiencies/SpellDc.h"

UPF2SpellDifficultyClassCalculation::UPF2SpellDifficultyClassCalculation() :
UPF2KeyAbilityTemlCalculationBase(TEXT("PF2.Proficiency.SpellDc"), TEXT("PF2.SpellcastingAbility"), 10.0f)
UPF2KeyAbilityTemlCalculationBase(Pf2TagProficiencySpellDc, Pf2TagSpellcastingAbilities, 10.0f)
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,20 @@ class OPENPF2GAMEFRAMEWORK_API UPF2KeyAbilityTemlCalculationBase : public UGamep
* proficiency bonus, and TEML tags on the character that have the specified prefix determine the magnitude of the
* boost.
*
* @param StatGameplayTagPrefix
* @param StatPrefixTag
* The tag prefix to use for checking a character's training in the stat. For example, "PF2.Proficiency.ClassDc",
* "PF2.Proficiency.SpellAttack", or "PF2.Proficiency.SpellDc".
* @param KeyAbilityGameplayTagPrefix
* @param KeyAbilityPrefixTag
* The tag prefix to use to determine the key ability for this stat. For the Class DC, this is "PF2.KeyAbility".
* For Spell Attack and Spell DC, this is "PF2.SpellcastingAbility".
* @param BaseValue
* The base value for this stat. For DC stats, this is usually "10". For other stats (e.g. Spell Attack Roll),
* this is 0.
*/
explicit UPF2KeyAbilityTemlCalculationBase(
const FString& StatGameplayTagPrefix,
const FString& KeyAbilityGameplayTagPrefix,
const float BaseValue = 0.0f);
const FGameplayTag& StatPrefixTag,
const FGameplayTag& KeyAbilityPrefixTag,
const float BaseValue = 0.0f);

// =================================================================================================================
// Public Methods - UGameplayModMagnitudeCalculation Implementation
Expand All @@ -70,7 +70,7 @@ class OPENPF2GAMEFRAMEWORK_API UPF2KeyAbilityTemlCalculationBase : public UGamep
/**
* The tag prefix to use for checking a character's training in this stat.
*/
FString StatGameplayTagPrefix;
FGameplayTag StatPrefixTag;

/**
* The base value for this stat.
Expand All @@ -82,28 +82,10 @@ class OPENPF2GAMEFRAMEWORK_API UPF2KeyAbilityTemlCalculationBase : public UGamep
/**
* Map from Key Ability tag names to capture definitions.
*
* Each key in the map is a gameplay tag, which corresponds to a key character ability; and the value is the
* Each key in the map is a gameplay tag that corresponds to a key character ability, while the value is the
* definition for capturing the modifier of that ability.
*/
TMap<FString, FGameplayEffectAttributeCaptureDefinition> KeyAbilityCaptureDefinitions;

/**
* The name of the gameplay tag that indicates the character's Key Ability.
*
* From the Pathfinder 2E Core Rulebook, page 67:
* "This is the ability score that a member of your class cares about the most. Many of your most useful and
* powerful abilities are tied to this ability in some way.
*
* For instance, this is the ability score you’ll use to determine the Difficulty Class (DC) associated with your
* character’s class features and feats. This is called your class DC. If your character is a member of a
* spellcasting class, this Key Ability is used to calculate spell DCs and similar values.
*
* Most classes are associated with one Key Ability score, but some allow you to choose from two options. For
* instance, if you’re a fighter, you can choose either Strength or Dexterity as your Key Ability. A fighter who
* chooses Strength will excel in hand-to-hand combat, while those who choose Dexterity prefer ranged or finesse
* weapons."
*/
FString KeyAbilityGameplayTag;
TMap<FGameplayTag, FGameplayEffectAttributeCaptureDefinition> KeyAbilityCaptureDefinitions;

// =================================================================================================================
// Protected Methods
Expand All @@ -113,13 +95,13 @@ class OPENPF2GAMEFRAMEWORK_API UPF2KeyAbilityTemlCalculationBase : public UGamep
*
* This is used to ensure we can retrieve the modifier for the specified ability later in the calculation phase.
*
* @param KeyAbilityTagName
* The name of the gameplay tag that a character must have for the ability to be considered "key". For example,
* @param KeyAbilityTag
* The gameplay tag that a character must have for the ability to be considered "key". For example,
* "PF2.KeyAbility.Strength" or "PF2.SpellcastingAbility.Intelligence".
* @param Attribute
* The definition of the attribute to capture.
*/
void DefineKeyAbilityCapture(const FString& KeyAbilityTagName, const FGameplayAttribute& Attribute);
void DefineKeyAbilityCapture(const FGameplayTag& KeyAbilityTag, const FGameplayAttribute& Attribute);

/**
* Calculates the Key Ability modifier for the character.
Expand Down

0 comments on commit 7adc4e1

Please sign in to comment.