So I am making a game using Lyra Starter, mostly building off the shootercore but adding my own stuff. I’ve made my own Lyra Attribute Sets and my own custom calculation execution class. I have attributes like shield, bonus health, and damage boost. In play-in-editor, they work as expected, but in quick launch or packaged builds, the function you’re supposed to use to capture the attributes (ExecutionParams.AttemptCalculateCapturedAttributeMagnitude) always returns 0
Everything, like being able to modify the attribute or read it, works perfectly fine in packaged, except this function and it only seems to be unable to capture my attribute sets but has no issue with lyra’s attribute set.
#include "Executions/HeroDamageExecution.h"
#include "AbilitySystem/Attributes/LyraHealthSet.h"
#include "AbilitySystem/Attributes/LyraCombatSet.h"
#include "Attributes/HeroHealthSet.h"
#include "Attributes/HeroCombatSet.h"
#include "AbilitySystem/LyraGameplayEffectContext.h"
#include "AbilitySystem/LyraAbilitySourceInterface.h"
#include "Engine/World.h"
#include "LyraLogChannels.h"
#include "Teams/LyraTeamSubsystem.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(HeroDamageExecution)
struct FDamageStatics
{
FGameplayEffectAttributeCaptureDefinition BaseDamageDef;
FGameplayEffectAttributeCaptureDefinition ShieldDef;
FGameplayEffectAttributeCaptureDefinition MaxShieldDef;
FGameplayEffectAttributeCaptureDefinition BonusHealthDef;
FGameplayEffectAttributeCaptureDefinition DamageBonusDef;
FDamageStatics()
{
BaseDamageDef = FGameplayEffectAttributeCaptureDefinition(ULyraCombatSet::GetBaseDamageAttribute(), EGameplayEffectAttributeCaptureSource::Source, true);
//Target Damage Bonus
DamageBonusDef = FGameplayEffectAttributeCaptureDefinition(UHeroCombatSet::GetDamageBoostAttribute(), EGameplayEffectAttributeCaptureSource::Source, true);
//ShieldDef
ShieldDef = FGameplayEffectAttributeCaptureDefinition(UHeroHealthSet::GetShieldAttribute(), EGameplayEffectAttributeCaptureSource::Target, true);
//MaxShieldDef
MaxShieldDef = FGameplayEffectAttributeCaptureDefinition(UHeroHealthSet::GetMaxShieldAttribute(), EGameplayEffectAttributeCaptureSource::Target, true);
//BonusHealth
BonusHealthDef= FGameplayEffectAttributeCaptureDefinition(UHeroHealthSet::GetBonusHealthAttribute(), EGameplayEffectAttributeCaptureSource::Target, true);
}
};
static FDamageStatics& DamageStatics()
{
static FDamageStatics Statics;
return Statics;
}
UHeroDamageExecution::UHeroDamageExecution()
{
RelevantAttributesToCapture.Add(DamageStatics().BaseDamageDef);
RelevantAttributesToCapture.Add(DamageStatics().DamageBonusDef);
RelevantAttributesToCapture.Add(DamageStatics().ShieldDef);
RelevantAttributesToCapture.Add(DamageStatics().MaxShieldDef);
RelevantAttributesToCapture.Add(DamageStatics().BonusHealthDef);
}
void UHeroDamageExecution::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
{
#if WITH_SERVER_CODE
const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();
FLyraGameplayEffectContext* TypedContext = FLyraGameplayEffectContext::ExtractEffectContext(Spec.GetContext());
check(TypedContext);
const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
UAbilitySystemComponent* Source = ExecutionParams.GetSourceAbilitySystemComponent();
FAggregatorEvaluateParameters EvaluateParameters;
EvaluateParameters.SourceTags = SourceTags;
EvaluateParameters.TargetTags = TargetTags;
float BaseDamage = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().BaseDamageDef, EvaluateParameters, BaseDamage);
//Take Damage from shield first, then use left over damage to bonus health, then use the left over damage to health
float RemainingShield = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().ShieldDef, EvaluateParameters, RemainingShield);
GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, FString::Printf(TEXT("Shield: %f"), RemainingShield));
float RemainingBonusHealth = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().BonusHealthDef, EvaluateParameters, RemainingBonusHealth);
float DamageBonus = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().DamageBonusDef, EvaluateParameters, DamageBonus);
const AActor* EffectCauser = TypedContext->GetEffectCauser();
const FHitResult* HitActorResult = TypedContext->GetHitResult();
AActor* HitActor = nullptr;
FVector ImpactLocation = FVector::ZeroVector;
FVector ImpactNormal = FVector::ZeroVector;
FVector StartTrace = FVector::ZeroVector;
FVector EndTrace = FVector::ZeroVector;
// Calculation of hit actor, surface, zone, and distance all rely on whether the calculation has a hit result or not.
// Effects just being added directly w/o having been targeted will always come in without a hit result, which must default
// to some fallback information.
if (HitActorResult)
{
const FHitResult& CurHitResult = *HitActorResult;
HitActor = CurHitResult.HitObjectHandle.FetchActor();
if (HitActor)
{
ImpactLocation = CurHitResult.ImpactPoint;
ImpactNormal = CurHitResult.ImpactNormal;
StartTrace = CurHitResult.TraceStart;
EndTrace = CurHitResult.TraceEnd;
}
}
// Handle case of no hit result or hit result not actually returning an actor
UAbilitySystemComponent* TargetAbilitySystemComponent = ExecutionParams.GetTargetAbilitySystemComponent();
if (!HitActor)
{
HitActor = TargetAbilitySystemComponent ? TargetAbilitySystemComponent->GetAvatarActor_Direct() : nullptr;
if (HitActor)
{
ImpactLocation = HitActor->GetActorLocation();
}
}
// Apply rules for team damage/self damage/etc...
float DamageInteractionAllowedMultiplier = 0.0f;
if (HitActor)
{
ULyraTeamSubsystem* TeamSubsystem = HitActor->GetWorld()->GetSubsystem<ULyraTeamSubsystem>();
if (ensure(TeamSubsystem))
{
DamageInteractionAllowedMultiplier = TeamSubsystem->CanCauseDamage(EffectCauser, HitActor) ? 1.0 : 0.0;
}
}
// Determine distance
double Distance = WORLD_MAX;
if (TypedContext->HasOrigin())
{
Distance = FVector::Dist(TypedContext->GetOrigin(), ImpactLocation);
}
else if (EffectCauser)
{
Distance = FVector::Dist(EffectCauser->GetActorLocation(), ImpactLocation);
}
else
{
UE_LOG(LogLyraAbilitySystem, Error, TEXT("Damage Calculation cannot deduce a source location for damage coming from %s; Falling back to WORLD_MAX dist!"), *GetPathNameSafe(Spec.Def));
}
// Apply ability source modifiers
float PhysicalMaterialAttenuation = 1.0f;
float DistanceAttenuation = 1.0f;
if (const ILyraAbilitySourceInterface* AbilitySource = TypedContext->GetAbilitySource())
{
if (const UPhysicalMaterial* PhysMat = TypedContext->GetPhysicalMaterial())
{
PhysicalMaterialAttenuation = AbilitySource->GetPhysicalMaterialAttenuation(PhysMat, SourceTags, TargetTags);
}
DistanceAttenuation = AbilitySource->GetDistanceAttenuation(Distance, SourceTags, TargetTags);
}
DistanceAttenuation = FMath::Max(DistanceAttenuation, 0.0f);
// Clamping is done when damage is converted to -health
const float DamageDone = FMath::Max(BaseDamage * DistanceAttenuation * PhysicalMaterialAttenuation * DamageInteractionAllowedMultiplier, 0.0f);
//get source
if (Source) {
//add damage done to sources ultimate energy
UGameplayEffect* GEUltimateGain = NewObject<UGameplayEffect>(GetTransientPackage(), FName(TEXT("Ultimate Gain")));
GEUltimateGain->DurationPolicy = EGameplayEffectDurationType::Instant;
GEUltimateGain->Modifiers.SetNum(1);
FGameplayModifierInfo& UltGain = GEUltimateGain->Modifiers[0];
UltGain.ModifierMagnitude = FScalableFloat(DamageDone);
UltGain.ModifierOp = EGameplayModOp::Additive;
UltGain.Attribute = UHeroCombatSet::GetUltimateEnergyAttribute();
Source->ApplyGameplayEffectToSelf(GEUltimateGain, 1.0f, Source->MakeEffectContext());
}
float DamageBonusMult = (DamageBonus / 100) + 1;
if (DamageDone > 0.0f) {
float RemainingDamage = DamageDone * DamageBonusMult;
//if shield is not spent
//Shield is only a factor if Shield gameplay tag is applied
if (TargetTags->HasTagExact(FGameplayTag::RequestGameplayTag(FName("Status.ShieldActive")))) {
if (RemainingShield > 0.0f) {
//will clamp to 0 somewhere else
OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(UHeroHealthSet::GetShieldAttribute(), EGameplayModOp::Additive, -RemainingDamage));
}
if (RemainingShield - RemainingDamage < 0.0f) {
OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(UHeroHealthSet::GetShieldAttribute(), EGameplayModOp::Override, 0.0f));
RemainingDamage = FMath::Abs(RemainingShield - RemainingDamage);
}
else {
RemainingDamage = 0.0f;
}
}
if (RemainingBonusHealth > 0.0f && RemainingDamage > 0.0f) {
OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(UHeroHealthSet::GetBonusHealthAttribute(), EGameplayModOp::Additive, -RemainingDamage));
//have we spent all abaliable bonus health
if (RemainingBonusHealth - RemainingDamage < 0.0f) {
RemainingDamage = FMath::Abs(RemainingBonusHealth - RemainingDamage);
}
else {
RemainingDamage = 0.0f;
}
}
if (RemainingDamage > 0.0f) {
// Apply a damage modifier, this gets turned into - health on the target
OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(ULyraHealthSet::GetDamageAttribute(), EGameplayModOp::Additive, RemainingDamage));
}
}
/*if (DamageDone > 0.0f)
{
// Apply a damage modifier, this gets turned into - health on the target
OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(ULyraHealthSet::GetDamageAttribute(), EGameplayModOp::Additive, DamageDone));
}*/
#endif // #if WITH_SERVER_CODE
}