第一次提交
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "CombatFlow/GCS_AttackDefinition.h"
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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});
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user