-
-
Save michaeltchapman/e305f6468f302f52a85ca767935afb08 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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