I want to implement a shielding effect that has some stacks and each stack completely mitigates a single damage instance. For that I added a UCombatSet::Shield and UCombatSet::MaxShield (in this scenario value of 3) attributes to keep track of the charges left.
I also have two GE:
- Instant: Adds 1 shield (Add Base)
- Duration: Gives 2 Shield (Add Base) for 5 seconds
Initially I did the clamping in my CombatSet class:
void UCombatSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
Super::PreAttributeChange(Attribute, NewValue);
if (Attribute == GetShieldAttribute())
{
NewValue = FMath::Clamp<float>(NewValue, 0.0f, GetMaxShield());
}
}
and the damage mitigation in my DamageExecution class:
if (TargetTags->HasTag(TAG_STATUS_BUFF_SHIELDED))
{
OutExecutionOutput.AddOutputModifier(
FGameplayModifierEvaluatedData(UCombatSet::GetShieldAttribute(), EGameplayModOp::Additive, -1)
);
}
This works totally fine when I work with only the instant GE as everything modifies the BaseValue of the attribute. However, there are a couple issues for the Duration GE.
- If I already have maxed out my shield and activate the duration GE the value overflows. E.g. BaseValue for Shield is 3 and I activate the GE the CurrentValue is set to 5. So even though in the UI it is capped as max 3 shield, two additional damage instance will be blocked.
For this case I figure out that I have to implement UCombatSet::PostGameplayEffectExecute as well:
void UCombatSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
if (Data.EvaluatedData.Attribute == GetShieldAttribute())
{
SetShield(FMath::Clamp(GetShield(), 0.0f, GetMaxShield()));
}
}
- This leads to a new issue now. When I have the duration buff active and receive 1 damage instance, the remaining temporary shield (CurrentValue) will be converted to BaseValue due to the implementation details of how SetShield works. Meaning if the buff expires I’m left with 1 BaseValue instead of 0. For that I tried to set the Shield.CurrentValue instead of the SetShield method in PostGameplayEffectExecute. This worked but lead again to a new issues which seems impossible to resolve.
So my question is: how to I implement such a mechanic? Do I have to use different attributes for the instant Shield and the temporary Shield? Or is there anything I’m completely misunderstanding? I was also thinking about using GE stacks with tag instead of an attribute for shield but couldn’t figure out how to do it properly.