GAS Attribute Capture in Calculation Class Not working In packaged build in lyra project 5.5

LarryTheLadd
Sat, 08 Mar 2025 23:49:20 +0000
C++

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
}

5 posts - 3 participants