第一次提交

This commit is contained in:
不明不惑
2026-03-03 01:23:02 +08:00
commit 3e434877e8
1053 changed files with 102411 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "CombatFlow/GCS_AbilityActionSetSettings.h"
bool UGCS_AbilityActionSetSettings::SelectBestAbilityActions(const FGameplayTagContainer& SourceTags, const FGameplayTagContainer& TargetTags, const FGameplayTagContainer& AbilityTags,
TArray<FGCS_AbilityAction>& Actions) const
{
FGCS_AbilityActionSet ActionSet;
bool bFound = false;
for (int32 i = 0; i < ActionSets.Num(); i++)
{
if (ActionSets[i].AbilityTag.IsValid() && ActionSets[i].AbilityTag.MatchesAny(AbilityTags))
{
ActionSet = ActionSets[i];
bFound = true;
break;
}
}
if (!bFound)
{
return false;
}
// try finds in layers.
for (int32 i = 0; i < ActionSet.Layered.Num(); i++)
{
bool bMatchingSource = ActionSet.Layered[i].SourceTagQuery.IsEmpty() || ActionSet.Layered[i].SourceTagQuery.Matches(SourceTags);
bool bMatchingTarget = ActionSet.Layered[i].TargetTagQuery.IsEmpty() || ActionSet.Layered[i].TargetTagQuery.Matches(TargetTags);
if (bMatchingSource && bMatchingTarget)
{
Actions = ActionSet.Layered[i].Actions;
return true;
}
}
// falback to default.
Actions = ActionSet.Actions;
return true;
}
#if WITH_EDITORONLY_DATA
#include "UObject/ObjectSaveContext.h"
void UGCS_AbilityActionSetSettings::PreSave(FObjectPreSaveContext SaveContext)
{
Super::PreSave(SaveContext);
for (FGCS_AbilityActionSet& ActionSet : ActionSets)
{
for (FGCS_AbilityAction& Action : ActionSet.Actions)
{
Action.EditorFriendlyName = Action.Animation != nullptr ? Action.Animation.GetName() : TEXT("Empty Action");
}
for (FGCS_AbilityActionsWithQuery& Layered : ActionSet.Layered)
{
Layered.EditorFriendlyName = FString::Format(TEXT("Source:({0}) Target({1})"), {
Layered.SourceTagQuery.IsEmpty() ? TEXT("Empty") : Layered.SourceTagQuery.GetDescription(),
Layered.TargetTagQuery.IsEmpty() ? TEXT("Empty") : Layered.TargetTagQuery.GetDescription()
});
for (FGCS_AbilityAction& Action : Layered.Actions)
{
Action.EditorFriendlyName = Action.Animation != nullptr ? Action.Animation.GetName() : TEXT("Empty Action");
}
}
}
}
#endif

View File

@@ -0,0 +1,4 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "CombatFlow/GCS_AttackDefinition.h"

View File

@@ -0,0 +1,189 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "CombatFlow/GCS_AttackRequest.h"
#include "Engine/DataTable.h"
#include "GCS_CombatEntityInterface.h"
#include "GameFramework/Character.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/PrimitiveComponent.h"
#include "GameFramework/PlayerController.h"
#include "Utility/GCS_CombatFunctionLibrary.h"
#include "Weapon/GCS_WeaponInterface.h"
FDataTableRowHandle UGCS_AttackRequest_Base::GetAttackDefinitionHandle_Implementation() const
{
return FDataTableRowHandle();
}
FGCS_AttackDefinition UGCS_AttackRequest_Base::GetAttackDefinition() const
{
if (const FGCS_AttackDefinition* Def = GetAttackDefinitionHandle().GetRow<FGCS_AttackDefinition>(TEXT("Get Attack Definition from AttackRequest")))
{
return *Def;
}
return FGCS_AttackDefinition();
}
FDataTableRowHandle UGCS_AttackRequest_Melee::GetAttackDefinitionHandle_Implementation() const
{
return AttackDefinitionHandle;
}
FGCS_BulletDefinition UGCS_AttackRequest_Bullet::GetBulletDefinition() const
{
if (auto Def = BulletDefinitionHandle.GetRow<FGCS_BulletDefinition>(TEXT("Get Bullet Definition from AttackRequest_Bullet")))
{
return *Def;
}
return FGCS_BulletDefinition();
}
FDataTableRowHandle UGCS_AttackRequest_Bullet::GetAttackDefinitionHandle_Implementation() const
{
return GetBulletDefinition().AttackDefinition;
}
FVector UGCS_AttackRequest_Bullet::GetPawnTargetingSourceLocation_Implementation(APawn* SourcePawn) const
{
check(SourcePawn);
FVector RetLocation = SourcePawn->GetActorLocation();
if (SourceSocketName != NAME_None)
{
if (UMeshComponent* Mesh = UGCS_CombatFunctionLibrary::GetMainMeshComponent(SourcePawn))
{
if (Mesh->DoesSocketExist(SourceSocketName))
{
RetLocation = Mesh->GetSocketLocation(SourceSocketName);
}
}
if (ACharacter* Char = Cast<ACharacter>(SourcePawn))
{
if (Char->GetMesh() && Char->GetMesh()->DoesSocketExist(SourceSocketName))
{
RetLocation = Char->GetMesh()->GetSocketLocation(SourceSocketName);
}
}
}
FVector LocalOffsetInWorld = SourcePawn->GetActorTransform().TransformVector(LocationOffset);
return RetLocation + LocalOffsetInWorld;
}
FVector UGCS_AttackRequest_Bullet::GetWeaponTargetingSourceLocation_Implementation(APawn* SourcePawn) const
{
check(SourcePawn)
FVector RetLocation = SourcePawn->GetActorLocation();
if (UObject* CombatImplementer = UGCS_CombatFunctionLibrary::GetCombatEntity(SourcePawn))
{
if (UObject* WeaponImplementer = IGCS_CombatEntityInterface::Execute_GetCurrentWeapon(CombatImplementer, nullptr))
{
if (UPrimitiveComponent* PrimitiveComponent = IGCS_WeaponInterface::Execute_GetPrimitiveComponent(WeaponImplementer))
{
if (SourceWeaponSocketName != NAME_None && PrimitiveComponent->DoesSocketExist(SourceWeaponSocketName))
{
RetLocation = PrimitiveComponent->GetSocketLocation(SourceWeaponSocketName);
}
else
{
RetLocation = PrimitiveComponent->GetOwner()->GetActorLocation();
}
}
}
}
FVector LocalOffsetInWorld = SourcePawn->GetActorTransform().TransformVector(LocationOffset);
return RetLocation + LocalOffsetInWorld;
}
FTransform UGCS_AttackRequest_Bullet::GetTargetingTransform_Implementation(APawn* SourcePawn, EGCS_AbilityTargetingSourceType Source) const
{
check(SourcePawn);
// The caller should determine the transform without calling this if the mode is custom!
check(Source != EGCS_AbilityTargetingSourceType::Custom);
static double FocalDistance = 1024.0f;
FVector FocalLoc;
bool bFocalBased = SourcePawn->Controller != nullptr && (Source == EGCS_AbilityTargetingSourceType::CameraTowardsFocus || Source == EGCS_AbilityTargetingSourceType::PawnTowardsFocus || Source ==
EGCS_AbilityTargetingSourceType::WeaponTowardsFocus);
if (bFocalBased)
{
APlayerController* PC = Cast<APlayerController>(SourcePawn->Controller);
FVector CamLoc;
FRotator CamRot;
if (PC != nullptr)
{
PC->GetPlayerViewPoint(CamLoc, CamRot);
}
else
{
CamLoc = SourceSocketName != NAME_None ? GetPawnTargetingSourceLocation(SourcePawn) : SourcePawn->GetActorLocation() + FVector(0, 0, SourcePawn->BaseEyeHeight);
CamRot = SourcePawn->Controller->GetControlRotation();
}
// Determine initial focal point to
FVector AimDir = CamRot.Vector().GetSafeNormal();
FocalLoc = CamLoc + (AimDir * FocalDistance);
// Move the start and focal point up in front of pawn
// if (PC)
// {
// const FVector WeaponLoc = GetWeaponTargetingSourceLocation(SourcePawn);
// CamLoc = FocalLoc + (((WeaponLoc - FocalLoc) | AimDir) * AimDir);
// FocalLoc = CamLoc + (AimDir * FocalDistance);
// }
// valid camera and want camera's location
if (Source == EGCS_AbilityTargetingSourceType::CameraTowardsFocus)
{
// If we're camera -> focus then we're done
return FTransform(CamRot, CamLoc);
}
}
// valid camera nd want pawn's location.
if (bFocalBased && Source == EGCS_AbilityTargetingSourceType::PawnTowardsFocus)
{
FVector SourceLoc = GetPawnTargetingSourceLocation(SourcePawn);
// Return a rotator pointing at the focal point from the source
return FTransform((FocalLoc - SourceLoc).Rotation(), SourceLoc);
}
// valid camera and want weapon's location.
if (bFocalBased && Source == EGCS_AbilityTargetingSourceType::WeaponTowardsFocus)
{
FVector SourceLoc = GetWeaponTargetingSourceLocation(SourcePawn);
// Return a rotator pointing at the focal point from the source
return FTransform((FocalLoc - SourceLoc).Rotation(), SourceLoc);
}
// Either we want the weapon's location, or we failed to find a camera
if (Source == EGCS_AbilityTargetingSourceType::WeaponForward || Source == EGCS_AbilityTargetingSourceType::WeaponTowardsFocus)
{
FVector SourceLoc = GetWeaponTargetingSourceLocation(SourcePawn);
return FTransform(SourcePawn->GetActorQuat(), SourceLoc);
}
// Either we want the pawn's location, or we failed to find a camera
if (Source == EGCS_AbilityTargetingSourceType::PawnForward || Source == EGCS_AbilityTargetingSourceType::PawnTowardsFocus)
{
// Either we want the pawn's location, or we failed to find a camera
FVector SourceLoc = GetPawnTargetingSourceLocation(SourcePawn);
return FTransform(SourcePawn->GetActorQuat(), SourceLoc);
}
// If we got here, either we don't have a camera or we don't want to use it, either way go forward
return FTransform(SourcePawn->GetActorQuat(), SourcePawn->GetActorLocation());
}

View File

@@ -0,0 +1,109 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "CombatFlow/GCS_AttackResult.h"
#include "GameplayEffect.h"
#include "GCS_LogChannels.h"
#include "GCS_CombatSystemComponent.h"
#include "CombatFlow/GCS_CombatFlow.h"
#include "Utility/GCS_CombatFunctionLibrary.h"
void FGCS_AttackResult::PostReplicatedAdd(const struct FGCS_AttackResultContainer& InArray)
{
}
FGCS_AttackResultContainer::FGCS_AttackResultContainer(): CombatFlow(nullptr), CombatSystemComponent(nullptr), MaxSize(5)
{
}
FGCS_AttackResultContainer::FGCS_AttackResultContainer(UGCS_CombatFlow* InCombatFlow, int32 InMaxSize): CombatFlow(InCombatFlow), CombatSystemComponent(nullptr), MaxSize(InMaxSize)
{
}
FGCS_AttackResultContainer::FGCS_AttackResultContainer(UGCS_CombatSystemComponent* InCombatSystemComponent, int32 InMaxSize): CombatFlow(nullptr), CombatSystemComponent(InCombatSystemComponent),
MaxSize(InMaxSize)
{
}
void FGCS_AttackResultContainer::AddEntry(FGCS_AttackResult& NewEntry)
{
if (Results.Num() >= 5)
{
Results.RemoveAtSwap(0);
MarkArrayDirty();
}
Results.Add(NewEntry);
check(CombatSystemComponent != nullptr && CombatSystemComponent->GetOwner());
if (CombatSystemComponent->GetCombatFlow())
{
CombatSystemComponent->GetCombatFlow()->HandleAttackResult(NewEntry);
NewEntry.bConsumed = true;
}
else
{
UE_LOG(LogGCS, Error, TEXT("Missing Combat Flow on %s's combat system component."), *CombatSystemComponent->GetOwner()->GetName());
}
MarkItemDirty(NewEntry);
}
void FGCS_AttackResultContainer::PostReplicatedAdd(const TArrayView<int32> AddedIndices, int32 FinalSize)
{
for (int32 Index : AddedIndices)
{
FGCS_AttackResult& Entry = Results[Index];
FGCS_ContextPayload_Combat* CombatPayload = UGCS_CombatFunctionLibrary::EffectContextGetMutableCombatPayload(Results[Index].EffectContextHandle);
if (CombatPayload != nullptr && CombatPayload->PredictionKey.IsLocalClientKey())
{
checkf(!CombatPayload->bIsPredictingContext, TEXT("PredictingContext never should hit this!"))
// PredictionKey will only be valid on the client that predicted it. So if this has a valid PredictionKey, we can assume we already predicted it and shouldn't handle attack results.
if (HasPredictedResultWithPredictedKey(CombatPayload->PredictionKey))
{
GCS_LOG(Verbose, "Found already predicted attack result!")
Entry.bWasPredicated = true;
}
}
Entry.bWasReplicated = true;
CombatSystemComponent->GetCombatFlow()->HandleAttackResult(Entry);
Entry.bConsumed = true;
}
}
void FGCS_AttackResultContainer::PostReplicatedChange(const TArrayView<int32>& ChangedIndices, int32 FinalSize)
{
for (int32 Index : ChangedIndices)
{
if (!Results[Index].bConsumed)
{
CombatSystemComponent->GetCombatFlow()->HandleAttackResult(Results[Index]);
Results[Index].bConsumed = true;
}
}
}
bool FGCS_AttackResultContainer::HasPredictedResultWithPredictedKey(FPredictionKey PredictionKey) const
{
for (const FGCS_AttackResult& Result : Results)
{
if (Result.bConsumed)
{
continue;
}
if (const FGCS_ContextPayload_Combat* CombatPayload = UGCS_CombatFunctionLibrary::EffectContextGetMutableCombatPayload(Result.EffectContextHandle))
{
if (CombatPayload->PredictionKey.IsValidKey() && CombatPayload->PredictionKey == PredictionKey && CombatPayload->PredictionKey.WasReceived() == false)
{
return true;
}
}
}
return false;
}

View File

@@ -0,0 +1,242 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "CombatFlow/GCS_AttackResultProcessor.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemComponent.h"
#include "AbilitySystemGlobals.h"
#include "GameplayCueFunctionLibrary.h"
#include "GameFramework/Pawn.h"
#include "AbilitySystem/GCS_GameplayEffectContext.h"
#include "CombatFlow/GCS_CombatFlow.h"
#include "UObject/ObjectSaveContext.h"
#include "Utilities/GGA_GameplayCueFunctionLibrary.h"
#include "Utility/GCS_CombatFunctionLibrary.h"
bool UGCS_AttackResultProcessor::ProcessIncomingAttackResult(const FGCS_AttackResult& AttackResult)
{
if (!AttackResult.bConsumed)
{
HandleIncomingAttackResult(AttackResult);
return true;
}
return false;
}
EGCS_AttackResultProcessorPolicy UGCS_AttackResultProcessor::GetExecutePolicy_Implementation() const
{
return ExecutePolicy;
}
class UWorld* UGCS_AttackResultProcessor::GetWorld() const
{
if (AActor* OwningActor = GetOwningActor())
{
return OwningActor->GetWorld();
}
return nullptr;
}
AActor* UGCS_AttackResultProcessor::GetOwningActor() const
{
if (UGCS_CombatFlow* Flow = Cast<UGCS_CombatFlow>(GetOuter()))
{
return Flow->GetFlowOwner();
}
return nullptr;
}
UAbilitySystemComponent* UGCS_AttackResultProcessor::GetOwningAbilitySystemComponent() const
{
if (AActor* OwningActor = GetOwningActor())
{
return UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(OwningActor);
}
return nullptr;
}
FString UGCS_AttackResultProcessor::GetEditorFriendlyName_Implementation() const
{
return TEXT("");
}
void UGCS_AttackResultProcessor::HandleIncomingAttackResult_Implementation(const FGCS_AttackResult& AttackResult) const
{
}
#if WITH_EDITORONLY_DATA
void UGCS_AttackResultProcessor::PreSave(FObjectPreSaveContext SaveContext)
{
EditorFriendlyName = GetEditorFriendlyName();
Super::PreSave(SaveContext);
}
#endif
bool UGCS_AttackResultProcessor_WithTagRequirement::ProcessIncomingAttackResult(const FGCS_AttackResult& AttackResult)
{
if (!AttackResult.bConsumed)
{
bool bMatchSourceQuery = SourceTagQuery.IsEmpty() || SourceTagQuery.Matches(AttackResult.AggregatedSourceTags);
bool bMatchTargetQuery = TargetTagQuery.IsEmpty() || TargetTagQuery.Matches(AttackResult.AggregatedTargetTags);
if (bMatchSourceQuery && bMatchTargetQuery)
{
HandleIncomingAttackResult(AttackResult);
return true;
}
}
return false;
}
FString UGCS_AttackResultProcessor_WithTagRequirement::GetSourceTagQueryDesc() const
{
return SourceTagQuery.GetDescription();
}
FString UGCS_AttackResultProcessor_WithTagRequirement::GetTargetTagQueryDesc() const
{
return TargetTagQuery.GetDescription();
}
void UGCS_AttackResultProcessor_Death::HandleIncomingAttackResult_Implementation(const FGCS_AttackResult& AttackResult) const
{
UAbilitySystemComponent* ASC = GetOwningAbilitySystemComponent();
if (ASC)
{
if (true)
{
}
}
Super::HandleIncomingAttackResult_Implementation(AttackResult);
}
void UGCS_AttackResultProcessor_GameplayEvent::HandleIncomingAttackResult_Implementation(const FGCS_AttackResult& AttackResult) const
{
APawn* OwningPawn = Cast<APawn>(GetOwningActor());
if (OwningPawn == nullptr)
{
return;
}
if (OwningPawn->HasAuthority() || OwningPawn->IsLocallyControlled())
{
UAbilitySystemComponent* ASC = bSendToAttacker ? AttackResult.EffectContextHandle.GetInstigatorAbilitySystemComponent() : GetOwningAbilitySystemComponent();
if (IsValid(ASC))
{
FGameplayEventData Payload = FGameplayEventData();
FGameplayTagContainer InstigatorTags = AttackResult.AggregatedSourceTags;
if (const FGCS_ContextPayload_Combat* CombatPayload = UGCS_CombatFunctionLibrary::EffectContextGetMutableCombatPayload(AttackResult.EffectContextHandle))
{
InstigatorTags.AppendTags(CombatPayload->DynamicTags);
}
Payload.Instigator = AttackResult.EffectContextHandle.GetInstigator();
// Payload.OptionalObject = AttackResult.OptionalObject;
Payload.Target = OwningPawn;
Payload.ContextHandle = AttackResult.EffectContextHandle;
Payload.InstigatorTags = InstigatorTags;
Payload.TargetTags = AttackResult.AggregatedTargetTags;
for (const FGameplayTag& Tag : EventTriggers)
{
// UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(OwningPawn, Tag, Payload);
Payload.EventTag = Tag;
// FScopedPredictionWindow NewScopedWindow(ASC, true);
ASC->HandleGameplayEvent(Tag, &Payload);
}
}
}
}
FString UGCS_AttackResultProcessor_GameplayEvent::GetEditorFriendlyName_Implementation() const
{
FString Result;
for (FGameplayTag EventTrigger : EventTriggers)
{
if (EventTrigger.IsValid())
{
TArray<FString> Parts;
EventTrigger.GetTagName().ToString().ParseIntoArray(Parts,TEXT("."));
if (Parts.Num() > 0)
{
Result.Append(FString::Format(TEXT(" ({0}) "), {Parts.Last()}));
}
}
}
return FString::Format(TEXT("Send Event:{0}"), {Result});
}
void UGCS_AttackResultProcessor_GameplayCue::HandleIncomingAttackResult_Implementation(const FGCS_AttackResult& AttackResult) const
{
AActor* OwningActor = GetOwningActor();
if (!OwningActor)
{
return;
}
TArray<FGameplayTag> CuesToTrigger = GameplayCues;
FGCS_AttackDefinition Atk = UGCS_CombatFunctionLibrary::EffectContextGetAttackDefinition(AttackResult.EffectContextHandle);
if (!Atk.TargetGameplayCues.IsEmpty())
{
CuesToTrigger = Atk.TargetGameplayCues;
}
FGameplayCueParameters Params = FGameplayCueParameters();
if (const FHitResult* HitResult = AttackResult.EffectContextHandle.GetHitResult())
{
if (HitResult->GetActor() == OwningActor)
{
Params = UGameplayCueFunctionLibrary::MakeGameplayCueParametersFromHitResult(*HitResult);
}
}
FGameplayTagContainer InstigatorTags = AttackResult.AggregatedSourceTags;
if (const FGCS_ContextPayload_Combat* CombatPayload = UGCS_CombatFunctionLibrary::EffectContextGetMutableCombatPayload(AttackResult.EffectContextHandle))
{
InstigatorTags.AppendTags(CombatPayload->DynamicTags);
Params.RawMagnitude = CombatPayload->GetTaggedValue(RawMagnitudeTag);
Params.NormalizedMagnitude = CombatPayload->GetTaggedValue(NormalizedMagnitudeTag);
}
Params.AggregatedSourceTags = InstigatorTags;
Params.AggregatedTargetTags = AttackResult.AggregatedTargetTags;
Params.EffectCauser = AttackResult.EffectContextHandle.GetEffectCauser();
Params.Instigator = AttackResult.EffectContextHandle.GetInstigator();
Params.EffectContext = AttackResult.EffectContextHandle;
ModifyGameplayCueParametersBeforeExecute(Params);
for (const FGameplayTag& Cue : CuesToTrigger)
{
if (!Cue.IsValid())
{
continue;
}
UGGA_GameplayCueFunctionLibrary::ExecuteGameplayCueLocal(OwningActor, Cue, Params);
}
}
FString UGCS_AttackResultProcessor_GameplayCue::GetEditorFriendlyName_Implementation() const
{
FString Result;
for (FGameplayTag EventTrigger : GameplayCues)
{
if (EventTrigger.IsValid())
{
TArray<FString> Parts;
EventTrigger.GetTagName().ToString().ParseIntoArray(Parts,TEXT("."));
if (Parts.Num() > 0)
{
Result.Append(FString::Format(TEXT(" ({0}) "), {Parts.Last()}));
}
}
}
return FString::Format(TEXT("Execute cues:{0}"), {Result});
}

View File

@@ -0,0 +1,95 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "CombatFlow/GCS_CombatFlow.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "GameFramework/Pawn.h"
#include "Utilities/GGA_AbilitySystemFunctionLibrary.h"
#include "GCS_CombatSystemComponent.h"
#include "GCS_LogChannels.h"
#include "CombatFlow/GCS_AttackResultProcessor.h"
#include "Utility/GCS_CombatFunctionLibrary.h"
UGCS_CombatFlow::UGCS_CombatFlow()
{
Owner = nullptr;
}
void UGCS_CombatFlow::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
}
void UGCS_CombatFlow::Initialize(AActor* NewOwner)
{
Owner = NewOwner;
// ProcessedAttacks.SetCombatFlow(this);
CombatComponent = UGCS_CombatSystemComponent::GetCombatSystemComponent(Owner);
}
void UGCS_CombatFlow::HandlePreGameplayEffectSpecApply_Implementation(const FGameplayEffectSpec& Spec, UAbilitySystemComponent* AbilitySystemComponent,
FGameplayTagContainer& OutDynamicTagsAppendToSpec)
{
}
void UGCS_CombatFlow::HandleGameplayEffectExecute_Implementation(const FGGA_GameplayEffectModCallbackData& Payload)
{
}
void UGCS_CombatFlow::HandleAttackResult_Implementation(const FGCS_AttackResult& InPayload)
{
FGCS_AttackResult ModifiedPayload = InPayload;
bool bPredictingContext = UGCS_CombatFunctionLibrary::EffectContextGetIsPredictingContext(InPayload.EffectContextHandle);
const FGCS_ContextPayload_Combat* CombatPayload = UGCS_CombatFunctionLibrary::EffectContextGetMutableCombatPayload(ModifiedPayload.EffectContextHandle);
if (CombatPayload)
{
ModifiedPayload.AggregatedSourceTags.AppendTags(CombatPayload->DynamicTags);
}
CombatComponent->SetLastProcessedAttackResult(ModifiedPayload);
for (int32 i = 0; i < AttackResultProcessors.Num(); i++)
{
auto& Processor = AttackResultProcessors[i];
if (!IsValid(Processor))
{
continue;
}
bool bShouldExecute = Processor->GetExecutePolicy() == EGCS_AttackResultProcessorPolicy::Default && !bPredictingContext;
if (Processor->GetExecutePolicy() == EGCS_AttackResultProcessorPolicy::LocalPredicted)
{
bShouldExecute = bPredictingContext || !InPayload.bWasPredicated;
if (!bShouldExecute)
{
GCS_OWNED_CLOG(GetFlowOwner(), VeryVerbose, "Skipped local predicted processor at idx(%d) of type(%s)", i, *Processor->GetClass()->GetName())
}
}
if (Processor->GetExecutePolicy() == EGCS_AttackResultProcessorPolicy::ServerOnly)
{
bShouldExecute = !bPredictingContext && !InPayload.bWasReplicated;
if (!bShouldExecute)
{
GCS_OWNED_CLOG(GetFlowOwner(), VeryVerbose, "Skipped server only processor at idx(%d) of type(%s)", i, *Processor->GetClass()->GetName())
}
}
if (bShouldExecute)
{
#if WITH_EDITOR
// Debugging says not enabled.
if (!Processor->GetEditorEnableState())
{
continue;
}
#endif
if (Processor->ProcessIncomingAttackResult(ModifiedPayload))
{
GCS_OWNED_CLOG(GetFlowOwner(), VeryVerbose, "%sExecuted processor at idx(%d) of type(%s)", bPredictingContext?TEXT("Predicate "):TEXT(""), i, *Processor->GetClass()->GetName())
}
}
}
}