Skip to content

Instantly share code, notes, and snippets.

@michaeltchapman
Created April 8, 2020 15:16
Show Gist options
  • Save michaeltchapman/e305f6468f302f52a85ca767935afb08 to your computer and use it in GitHub Desktop.
Save michaeltchapman/e305f6468f302f52a85ca767935afb08 to your computer and use it in GitHub Desktop.
#include "EffectTypes.h"
#include "maladius.h"
#include "AbilitySystemGlobals.h"
#include "AbilitySystemComponent.h"
#include "AsyncAbilitySystemComponent.h"
#include "GlobalGameStateBase.h"
#include "GameplayEffect.h"
#include "GameplayAbility.h"
#include "BaseGameplayAbility.h"
#include "BaseCharacter.h"
#include "Spell.h"
bool FMActorFilter::PassesFilter(UAbilitySystemComponent* Source, UAbilitySystemComponent* Target)
{
if (AllowSelf != EMActorFilterMatchType::Skip && ((Source == Target) != (AllowSelf == EMActorFilterMatchType::MustPass)))
{
return false;
}
return true;
}
bool FMFactionFilter::PassesFilter(UAbilitySystemComponent* Source, UAbilitySystemComponent* Target)
{
ABaseCharacter* SourceChar = Cast<ABaseCharacter>(Source->GetAvatarActor());
ABaseCharacter* TargetChar = Cast<ABaseCharacter>(Target->GetAvatarActor());
if (SourceChar && TargetChar)
{
AGlobalGameStateBase *GameState = Cast<AGlobalGameStateBase>(SourceChar->GetWorld()->GetGameState());
check(GameState);
EHostility Relationship = GameState->GetHostility(SourceChar->GetFactionIndex(), TargetChar->GetFactionIndex());
bool FriendlyTest = false;
if (FriendlyFaction == EMFactionFilterMatchType::Skip)
{
FriendlyTest = true;
}
else if (FriendlyFaction == EMFactionFilterMatchType::MustPass && Relationship == EHostility::E_Friendly)
{
FriendlyTest = true;
}
else if (FriendlyFaction == EMFactionFilterMatchType::MustFail && Relationship != EHostility::E_Friendly)
{
FriendlyTest = true;
}
bool NeutralTest = false;
if (NeutralFaction == EMFactionFilterMatchType::Skip)
{
NeutralTest = true;
}
else if (NeutralFaction == EMFactionFilterMatchType::MustPass && Relationship == EHostility::E_Neutral)
{
NeutralTest = true;
}
else if (NeutralFaction == EMFactionFilterMatchType::MustFail && Relationship != EHostility::E_Neutral)
{
NeutralTest = true;
}
bool HostileTest = false;
if (HostileFaction == EMFactionFilterMatchType::Skip)
{
HostileTest = true;
}
else if (HostileFaction == EMFactionFilterMatchType::MustPass && Relationship == EHostility::E_Hostile)
{
HostileTest = true;
}
else if (HostileFaction == EMFactionFilterMatchType::MustFail && Relationship != EHostility::E_Hostile)
{
HostileTest = true;
}
return HostileTest && NeutralTest && FriendlyTest;
}
return true;
}
void FMEffectApplicationContainer::InitialiseEffectContainer(AActor* Source, float VariableLevel, float NormalisedCharge, float VariableRatio, float NormalisedRatio, AActor* AbilityOrigin, float Period, bool bClean)
{
if (UAbilitySystemComponent* ASC = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Source))
{
SourceAbilitySystemComponent = ASC;
for (FMEffectApplicationItem& Item : Items)
{
for (FMEffectItem& EffectItem : Item.Effects)
{
if (EffectItem.GeneratedSpec.IsValid())
{
if (bClean)
{
EffectItem.GeneratedSpec.Clear();
}
else {
UE_LOG(LogTemp, Warning, TEXT("GenerateEffectSpecs called on ApplicationContainer that already has generated specs!"));
continue;
}
}
if (*EffectItem.GameplayEffect == nullptr)
{
UE_LOG(LogTemp, Warning, TEXT("GenerateEffectSpecs called on ApplicationContainer with invalid GameplayEffect!"));
continue;
}
float Level = GetLevelForEffect(EffectItem, VariableLevel, NormalisedCharge, VariableRatio, NormalisedRatio) * Period;
int32 StackCount = EffectItem.StackCount;
if (AbilityOrigin && Cast<ASpell>(AbilityOrigin) && Cast<ASpell>(AbilityOrigin)->OwningAbility)
{
UBaseGameplayAbility* SourceAbility = Cast<ASpell>(AbilityOrigin)->OwningAbility;
if (SourceAbility)
{
EffectItem.GeneratedSpec = SourceAbility->MakeGameplayEffectSpec(EffectItem.GameplayEffect, Level);
}
}
else {
FGameplayEffectContextHandle ContextHandle = ASC->MakeEffectContext();
if (AbilityOrigin && Source->GetInstigator())
{
ContextHandle.AddInstigator(Source->GetInstigator(), AbilityOrigin);
}
else {
ContextHandle.AddInstigator(Source, Source);
}
EffectItem.GeneratedSpec = FGameplayEffectSpecHandle(new FGameplayEffectSpec(EffectItem.GameplayEffect.GetDefaultObject(), ContextHandle, Level));
}
EffectItem.GeneratedSpec.Data->StackCount = StackCount;
for (FMEffectModifier& Modifier : EffectItem.Modifiers)
{
EffectItem.GeneratedSpec.Data->SetSetByCallerMagnitude(Modifier.ModifierName, GetLevelForStrategy(Modifier.ModifierLevelStrategy, Modifier.ModifierMagnitude, VariableLevel, NormalisedCharge, VariableRatio, NormalisedRatio * Period));
}
}
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("GenerateEffectSpecs called on Source %s with no ASC"), *GetNameSafe(Source));
}
}
float FMEffectApplicationContainer::GetLevelForEffect(FMEffectItem &Item, float VariableLevel, float NormalisedCharge, float VariableRatio, float NormalisedRatio)
{
return GetLevelForStrategy(Item.LevelStrategy, Item.Level, VariableLevel, NormalisedCharge, VariableRatio, NormalisedRatio);
}
float FMEffectApplicationContainer::GetLevelForStrategy(EMEffectLevelStrategy Strategy, float Base, float VariableLevel, float NormalisedCharge, float VariableRatio, float NormalisedRatio)
{
switch (Strategy)
{
case EMEffectLevelStrategy::Base:
return Base;
case EMEffectLevelStrategy::BaseVarianceCharge:
return Base + VariableLevel*NormalisedCharge;
case EMEffectLevelStrategy::BaseVarianceInvCharge:
return Base + VariableLevel*(1.0f - NormalisedCharge);
case EMEffectLevelStrategy::BaseVarianceRatio:
return Base + VariableRatio*NormalisedRatio;
case EMEffectLevelStrategy::BaseVarianceInvRatio:
return Base + VariableRatio*(1.0f - NormalisedRatio);
case EMEffectLevelStrategy::BaseVarianceInvChargeInvRatio:
return Base + VariableRatio*(1.f - NormalisedRatio) + VariableLevel*(1.f - NormalisedCharge);
case EMEffectLevelStrategy::BaseVarianceChargeInvRatio:
return Base + VariableRatio*(1.f - NormalisedRatio) + VariableLevel*NormalisedCharge;
case EMEffectLevelStrategy::BaseVarianceChargeRatio:
return Base + VariableRatio*NormalisedRatio + VariableLevel*NormalisedCharge;
case EMEffectLevelStrategy::BaseVarianceChargeSquared:
return Base + VariableLevel*NormalisedCharge*NormalisedCharge;
case EMEffectLevelStrategy::BaseVarianceChargeSquaredRatioSquared:
return Base + VariableLevel * NormalisedCharge*NormalisedCharge + VariableRatio * NormalisedRatio*NormalisedRatio;
case EMEffectLevelStrategy::BaseVarianceChargeSquaredInvRatioSquared:
return Base + VariableLevel * NormalisedCharge*NormalisedCharge + VariableRatio * (1.f-NormalisedRatio)*(1.f-NormalisedRatio);
default:
return Base;
break;
}
}
void FMEffectApplicationContainer::GenerateEffectSpecs(AActor* Source, float LevelOverride, int32 StackOverride, AActor* AbilityOrigin)
{
if (UAbilitySystemComponent* ASC = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Source))
{
SourceAbilitySystemComponent = ASC;
for (FMEffectApplicationItem& Item : Items)
{
for (FMEffectItem& EffectItem : Item.Effects)
{
if (EffectItem.GeneratedSpec.IsValid())
{
UE_LOG(LogTemp, Warning, TEXT("GenerateEffectSpecs called on ApplicationContainer that already has generated specs!"));
continue;
}
if (*EffectItem.GameplayEffect == nullptr)
{
UE_LOG(LogTemp, Warning, TEXT("GenerateEffectSpecs called on ApplicationContainer with invalid GameplayEffect!"));
continue;
}
float Level = LevelOverride > 0.f ? LevelOverride : EffectItem.Level;
int32 StackCount = StackOverride > 0 ? StackOverride : EffectItem.StackCount;
if (AbilityOrigin && Cast<ASpell>(AbilityOrigin) && Cast<ASpell>(AbilityOrigin)->OwningAbility)
{
UBaseGameplayAbility* SourceAbility = Cast<ASpell>(AbilityOrigin)->OwningAbility;
if (SourceAbility)
{
EffectItem.GeneratedSpec = SourceAbility->MakeGameplayEffectSpec(EffectItem.GameplayEffect, Level);
}
}
else {
FGameplayEffectContextHandle ContextHandle = ASC->MakeEffectContext();
if (AbilityOrigin && Source->GetInstigator())
{
ContextHandle.AddInstigator(Source->GetInstigator(), AbilityOrigin);
}
else {
ContextHandle.AddInstigator(Source, AbilityOrigin);
}
EffectItem.GeneratedSpec = FGameplayEffectSpecHandle(new FGameplayEffectSpec(EffectItem.GameplayEffect.GetDefaultObject(), ContextHandle, Level));
}
EffectItem.GeneratedSpec.Data->StackCount = StackCount;
for (FMEffectModifier& Modifer : EffectItem.Modifiers)
{
EffectItem.GeneratedSpec.Data->SetSetByCallerMagnitude(Modifer.ModifierName, Modifer.ModifierMagnitude);
}
}
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("GenerateEffectSpecs called on Source %s with no ASC"), *GetNameSafe(Source));
}
}
void FMEffectApplicationContainer::SetCallerMagnitude(FName AttributeName, float Magnitude)
{
for (FMEffectApplicationItem& Item : Items)
{
for (FMEffectItem& EffectItem : Item.Effects)
{
EffectItem.GeneratedSpec.Data->SetSetByCallerMagnitude(AttributeName, Magnitude);
}
}
}
void FMEffectApplicationContainer::SetCallerMagnitudeOnEffect(FName AttributeName, float Magnitude, TSubclassOf<UGameplayEffect> Effect)
{
if (Effect)
{
for (FMEffectApplicationItem& Item : Items)
{
for (FMEffectItem& EffectItem : Item.Effects)
{
if (EffectItem.GameplayEffect == Effect)
{
EffectItem.GeneratedSpec.Data->SetSetByCallerMagnitude(AttributeName, Magnitude);
}
}
}
}
}
void FMEffectApplicationContainer::AddModifierForEffect(FName AttributeName, float Magnitude, TSubclassOf<UGameplayEffect> Effect)
{
if (Effect)
{
for (FMEffectApplicationItem& Item : Items)
{
for (FMEffectItem& EffectItem : Item.Effects)
{
if (EffectItem.GameplayEffect == Effect)
{
EffectItem.Modifiers.Add(FMEffectModifier(AttributeName, Magnitude));
}
}
}
}
}
void FMEffectApplicationContainer::AddHitResult(FHitResult Result)
{
for (FMEffectApplicationItem& Item : Items)
{
for (FMEffectItem& EffectItem : Item.Effects)
{
if (EffectItem.GeneratedSpec.Data.IsValid())
{
EffectItem.GeneratedSpec.Data->GetContext().AddHitResult(Result, true);
}
}
}
}
bool FMEffectApplicationContainer::ApplyEffectApplicationContainerToTargetASC(UAsyncAbilitySystemComponent* TargetASC, FHitResult HitResult, EMCueSimulation SimulateCue)
{
bool RetVal = false;
if (TargetASC)
{
if (!SourceAbilitySystemComponent)
{
UE_LOG(LogTemp, Warning, TEXT("ApplyEffectApplicationContainerToTarget called on container with no SourceAbilitySystemComponent. call GeneratEffectSpecs first"));
return false;
}
for (FMEffectApplicationItem& Item : Items)
{
if (Item.Filter.PassesFilter(SourceAbilitySystemComponent, TargetASC) == false)
{
continue;
}
if (Item.FactionFilter.PassesFilter(SourceAbilitySystemComponent, TargetASC) == false)
{
continue;
}
for (FMEffectItem& EffectItem : Item.Effects)
{
if (EffectItem.GeneratedSpec.IsValid() == false)
{
UE_LOG(LogTemp, Warning, TEXT("ApplyEffectApplicationContainerToTarget called on ApplicationContainer that has NO generated specs!"));
continue;
}
if (!EffectItem.GeneratedSpec.Data->GetContext().GetHitResult())
{
if (!HitResult.GetActor())
{
HitResult.Actor = TargetASC->GetAvatarActor();
}
AddHitResult(HitResult);
}
FActiveGameplayEffectHandle NewHandle;
if (SourceAbilitySystemComponent->ScopedPredictionKey.IsLocalClientKey() || SourceAbilitySystemComponent->IsOwnerActorAuthoritative())
{
NewHandle = TargetASC->ApplyGameplayEffectSpecToSelf(*EffectItem.GeneratedSpec.Data.Get(), SourceAbilitySystemComponent->ScopedPredictionKey);
}
else {
// Execute client-side instant FX and triggers cues
TargetASC->SimulateEffect(*EffectItem.GeneratedSpec.Data.Get());
}
/*
// Ghetto weak prediction
// If we're a client, check if the spec can be applied and if so, execute its weak predicted cues immediately
if (SimulateCue == EMCueSimulation::Local && TargetASC->GetNetMode() != ENetMode::NM_DedicatedServer)
{
}*/
if (NewHandle.WasSuccessfullyApplied())
{
RetVal = true;
}
}
}
}
return RetVal;
}
bool FMEffectApplicationContainer::ApplyEffectApplicationContainerToTarget(AActor* Target, UBaseGameplayAbility* SourceAbility, FHitResult HitResult, EMCueSimulation CueSimulation)
{
bool RetVal = false;
if (SourceAbilitySystemComponent == nullptr)
{
UE_LOG(LogTemp, Warning, TEXT("ApplyEffectApplicationContainerToTarget called on container with no SourceAbilitySystemComponent. call GeneratEffectSpecs first"));
return false;
}
UAsyncAbilitySystemComponent* ASC = Cast<UAsyncAbilitySystemComponent>(UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Target));
return ApplyEffectApplicationContainerToTargetASC(ASC, HitResult, CueSimulation);
/*
if (UAsyncAbilitySystemComponent* ASC = Cast<UAsyncAbilitySystemComponent>(UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Target)))
{
for (FMEffectApplicationItem& Item : Items)
{
if (Item.Filter.PassesFilter(SourceAbilitySystemComponent, ASC) == false)
{
continue;
}
if (Item.FactionFilter.PassesFilter(SourceAbilitySystemComponent, ASC) == false)
{
continue;
}
for (FMEffectItem& EffectItem : Item.Effects)
{
if (EffectItem.GeneratedSpec.IsValid() == false)
{
UE_LOG(LogTemp, Warning, TEXT("ApplyEffectApplicationContainerToTarget called on ApplicationContainer that has NO generated specs!"));
continue;
}
HitResult.Actor = Target;
AddHitResult(HitResult);
FActiveGameplayEffectHandle NewHandle;
// Ghetto weak prediction
// If we're a client, check if the spec can be applied and if so, execute its weak predicted cues immediately
if (CueSimulation == EMCueSimulation::Local && ASC->GetNetMode() != ENetMode::NM_DedicatedServer)
{
// Execute client-side instant FX cues
ASC->SimulateEffect(*EffectItem.GeneratedSpec.Data.Get());
}
NewHandle = ASC->ApplyGameplayEffectSpecToSelf(*EffectItem.GeneratedSpec.Data.Get(), SourceAbilitySystemComponent->ScopedPredictionKey);
if (NewHandle.WasSuccessfullyApplied())
{
RetVal = true;
}
}
}
}
return RetVal;*/
}
TArray<FActiveGameplayEffectHandle> FMEffectApplicationContainer::ApplyEffectApplicationContainerToTargetWithHandles(AActor* Target, UBaseGameplayAbility* SourceAbility)
{
TArray<FActiveGameplayEffectHandle> Handles;
if (SourceAbilitySystemComponent == nullptr)
{
UE_LOG(LogTemp, Warning, TEXT("ApplyEffectApplicationContainerToTarget called on container with no SourceAbilitySystemComponent. call GeneratEffectSpecs first"));
return Handles;
}
if (UAbilitySystemComponent* ASC = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Target))
{
for (FMEffectApplicationItem& Item : Items)
{
if (Item.Filter.PassesFilter(SourceAbilitySystemComponent, ASC) == false)
{
continue;
}
if (Item.FactionFilter.PassesFilter(SourceAbilitySystemComponent, ASC) == false)
{
continue;
}
for (FMEffectItem& EffectItem : Item.Effects)
{
if (EffectItem.GeneratedSpec.IsValid() == false)
{
UE_LOG(LogTemp, Warning, TEXT("ApplyEffectApplicationContainerToTarget called on ApplicationContainer that has NO generated specs!"));
continue;
}
//FActiveGameplayEffectHandle NewHandle = ASC->ApplyGameplayEffectSpecToSelf(*EffectItem.GeneratedSpec.Data.Get(), SourceAbilitySystemComponent->ScopedPredictionKey);
FActiveGameplayEffectHandle NewHandle;
if (SourceAbility)
{
NewHandle = SourceAbilitySystemComponent->ApplyGameplayEffectSpecToTarget(*EffectItem.GeneratedSpec.Data.Get(), ASC, SourceAbility->GetCurrentActivationInfo().GetActivationPredictionKey());
}
else {
NewHandle = ASC->ApplyGameplayEffectSpecToSelf(*EffectItem.GeneratedSpec.Data.Get(), ASC->ScopedPredictionKey);
}
if (NewHandle.IsValid())
{
Handles.Add(NewHandle);
}
}
}
}
return Handles;
}
void FMEffectApplicationContainer::PopulateEffectContainer(FMEffectApplicationContainer &Container,
TSubclassOf<UGameplayEffect> Effect,
float Level,
EMActorFilterMatchType SelfFilter,
EMFactionFilterMatchType FriendlyFilter,
EMFactionFilterMatchType NeutralFilter,
EMFactionFilterMatchType HostileFilter)
{
FMEffectItem Item;
Item.GameplayEffect = Effect;
Item.Level = Level;
FMEffectApplicationItem ApplicationItem;
ApplicationItem.Effects.Add(Item);
ApplicationItem.Filter.AllowSelf = SelfFilter;
ApplicationItem.FactionFilter.FriendlyFaction = FriendlyFilter;
ApplicationItem.FactionFilter.NeutralFaction = NeutralFilter;
ApplicationItem.FactionFilter.HostileFaction = HostileFilter;
Container.Items.Add(ApplicationItem);
}
#pragma once
#include "GameplayEffectTypes.h"
#include "GameplayEffect.h"
#include "EffectTypes.generated.h"
/*
Depending on the type of event occurring, we might fall into a few different categories, each requiring different replication strategies.
We want to avoid having the cue fire twice, so mainly either:
- cues fire locally on all clients
- cue is fired replicated from server
- cue is fired replicated from instigating client
*/
UENUM()
enum class EMCueSimulation : uint8
{
/** Cues are fired locally, no replication. */
Local,
/** Cue is executed from the server via ASC and replicated */
Server,
/** Cue is executed from instigating client and replicated */
Instigator,
};
UENUM()
enum class EMActorFilterMatchType : uint8
{
/** Skip this check completely. */
Skip,
/** Actor must pass this check (true) */
MustPass,
/** Actor must fail this check (not true) */
MustFail,
};
UENUM()
enum class EMFactionFilterMatchType : uint8
{
/** Skip this check completely. */
Skip,
/** Actor must pass this check (true) */
MustPass,
/** Actor must fail this check (not true) */
MustFail,
};
USTRUCT(BlueprintType)
struct FMFactionFilter
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere)
EMFactionFilterMatchType FriendlyFaction;
UPROPERTY(EditAnywhere)
EMFactionFilterMatchType NeutralFaction;
UPROPERTY(EditAnywhere)
EMFactionFilterMatchType HostileFaction;
bool PassesFilter(UAbilitySystemComponent* Source, UAbilitySystemComponent* Target);
};
USTRUCT(BlueprintType)
struct FMActorFilter
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere)
EMActorFilterMatchType AllowSelf;
bool PassesFilter(UAbilitySystemComponent* Source, UAbilitySystemComponent* Target);
};
// ----------------------------------------
/*
Assume NormalisedCharge and Ratio are both 0..1.f
*/
UENUM()
enum class EMEffectLevelStrategy : uint8
{
/** The level is always the same */
Base,
/** Level is determined by BaseLevel + NormalisedCharge*VariableCharge */
BaseVarianceCharge,
/** Level is determined by BaseLevel + (1.0f - NormalisedCharge)*VariableCharge */
BaseVarianceInvCharge,
/** Level is determined by BaseLevel + (1.0f - Ratio)*VariableRatio */
BaseVarianceRatio,
/** Level is determined by BaseLevel + Ratio*VariableRatio */
BaseVarianceInvRatio,
/** Level is determined by BaseLevel + NormalisedCharge*VariableCharge + (1.0f - Ratio)*VariableRatio */
BaseVarianceChargeInvRatio,
/** Level is determined by BaseLevel + (1.0f - NormalisedCharge)*VariableCharge + (1.0f - Ratio)*VariableRatio */
BaseVarianceInvChargeInvRatio,
/** Level is determined by BaseLevel + NormalisedCharge*VariableCharge + Ratio*VariableRatio */
BaseVarianceChargeRatio,
/** Level is determined by BaseLevel + NormalisedCharge^2*VariableCharge */
BaseVarianceChargeSquared,
/** Level is determined by BaseLevel + NormalisedCharge^2*VariableCharge + Ratio^2*VariableRatio */
BaseVarianceChargeSquaredRatioSquared,
/** Level is determined by BaseLevel + NormalisedCharge^2*VariableCharge + (1.f-Ratio)^2*VariableRatio */
BaseVarianceChargeSquaredInvRatioSquared,
};
USTRUCT(BlueprintType)
struct FMEffectModifier
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere)
FName ModifierName;
UPROPERTY(EditAnywhere)
float ModifierMagnitude;
UPROPERTY(EditAnywhere)
EMEffectLevelStrategy ModifierLevelStrategy;
FMEffectModifier() : ModifierName(NAME_None), ModifierMagnitude(-1.f)
{
}
FMEffectModifier(FName Modifier, float Magnitude) :
ModifierName(Modifier),
ModifierMagnitude(Magnitude),
ModifierLevelStrategy(EMEffectLevelStrategy::Base)
{
}
FMEffectModifier(FName Modifier, float Magnitude, EMEffectLevelStrategy ModifierStrategy) :
ModifierName(Modifier),
ModifierMagnitude(Magnitude),
ModifierLevelStrategy(ModifierStrategy)
{
}
};
USTRUCT(BlueprintType)
struct FMEffectItem
{
GENERATED_USTRUCT_BODY()
FMEffectItem() : Level(1.f), StackCount(1)
{
}
UPROPERTY(EditAnywhere)
TSubclassOf<UGameplayEffect> GameplayEffect;
UPROPERTY(EditAnywhere)
float Level;
UPROPERTY(EditAnywhere)
int32 StackCount;
UPROPERTY(EditAnywhere)
TArray<FMEffectModifier> Modifiers;
UPROPERTY()
FGameplayEffectSpecHandle GeneratedSpec;
UPROPERTY(EditAnywhere)
EMEffectLevelStrategy LevelStrategy;
};
USTRUCT(BlueprintType)
struct FMEffectApplicationItem
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere)
FMActorFilter Filter;
UPROPERTY(EditAnywhere)
FMFactionFilter FactionFilter;
UPROPERTY(EditAnywhere)
TArray<FMEffectItem> Effects;
};
/** A generic utility struct that can hold a list of "Apply this GE (Spec, Level, StackCount) to actors matching this filter". */
USTRUCT(BlueprintType)
struct FMEffectApplicationContainer
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere)
TArray<FMEffectApplicationItem> Items;
/** Cached source when specs are generated */
UPROPERTY(BlueprintReadWrite)
UAbilitySystemComponent* SourceAbilitySystemComponent;
// Source is the instigator actor, AbilityOrigin is the spell actor
void InitialiseEffectContainer(AActor* Source, float VariableLevel = 0.f, float NormalisedCharge = 0.f, float VariableRatio = 0.f, float NormalisedRatio = 0.f, AActor* AbilityOrigin = nullptr, float Period = 1.f, bool bClean = false);
float GetLevelForEffect(FMEffectItem &Item, float VariableLevel = 0.f, float NormalisedCharge = 0.f, float VariableRatio = 0.f, float NormalisedRatio = 0.f);
float GetLevelForStrategy(EMEffectLevelStrategy Strategy, float Base = 0.f, float VariableLevel = 0.f, float NormalisedCharge = 0.f, float VariableRatio = 0.f, float NormalisedRatio = 0.f);
void GenerateEffectSpecs(AActor* Source, float LevelOverride = 0.f, int32 StackOverride = 0, AActor* AbilityOrigin = nullptr);
void SetCallerMagnitude(FName AttributeName, float Magnitude);
void SetCallerMagnitudeOnEffect(FName AttributeName, float Magnitude, TSubclassOf<UGameplayEffect> Effect);
void AddModifierForEffect(FName AttributeName, float Magnitude, TSubclassOf<UGameplayEffect> Effect);
void AddHitResult(FHitResult Result);
TArray<FActiveGameplayEffectHandle> ApplyEffectApplicationContainerToTargetWithHandles(AActor* Target, class UBaseGameplayAbility* SourceAbility = nullptr);
static void PopulateEffectContainer(FMEffectApplicationContainer &Container,
TSubclassOf<UGameplayEffect> Effect,
float Level = 0.f,
EMActorFilterMatchType SelfFilter = EMActorFilterMatchType::Skip,
EMFactionFilterMatchType FriendlyFilter = EMFactionFilterMatchType::Skip,
EMFactionFilterMatchType NeutralFilter = EMFactionFilterMatchType::Skip,
EMFactionFilterMatchType HostileFilter = EMFactionFilterMatchType::Skip);
// returns true if any effect was applied successfully
bool ApplyEffectApplicationContainerToTarget(AActor* Target, class UBaseGameplayAbility* SourceAbility = nullptr, FHitResult HitResult = FHitResult(), EMCueSimulation SimulateCue = EMCueSimulation::Local);
bool ApplyEffectApplicationContainerToTargetASC(class UAsyncAbilitySystemComponent* TargetASC, FHitResult HitResult = FHitResult(), EMCueSimulation SimulateCue = EMCueSimulation::Local);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment