第一次提交

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,26 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Abilities/GCS_CombatAbility.h"
#include "GCS_CombatSystemComponent.h"
#include "Utility/GCS_CombatFunctionLibrary.h"
UGCS_CombatSystemComponent* UGCS_CombatAbility::GetCombatSystemFromActorInfo() const
{
if (UGCS_CombatSystemComponent* CSS = UGCS_CombatSystemComponent::GetCombatSystemComponent(GetAvatarActorFromActorInfo()))
{
return CSS;
}
return nullptr;
}
UObject* UGCS_CombatAbility::GetCombatEntityFromActorInfo() const
{
return UGCS_CombatFunctionLibrary::GetCombatEntity(GetAvatarActorFromActorInfo());
}
TScriptInterface<IGCS_CombatEntityInterface> UGCS_CombatAbility::GetCombatEntityInterfaceFromActorInfo() const
{
return UGCS_CombatFunctionLibrary::GetCombatEntityInterface(GetAvatarActorFromActorInfo());
}

View File

@@ -0,0 +1,357 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Abilities/GCS_ComboAbility.h"
#include "AbilitySystemComponent.h"
#include "GCS_CombatEntityInterface.h"
#include "GCS_CombatSystemComponent.h"
#include "GCS_LogChannels.h"
#include "GGA_GameplayTags.h"
#include "Abilities/Tasks/AbilityTask_WaitInputPress.h"
#include "Combo/GCS_ComboDefinition.h"
#include "Utilities/GGA_AbilitySystemFunctionLibrary.h"
#include "Weapon/GCS_WeaponInterface.h"
UGCS_ComboAbility::UGCS_ComboAbility()
{
ReplicationPolicy = EGameplayAbilityReplicationPolicy::ReplicateYes;
NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted;
AbilityTags.AddTagFast(GGA_AbilityTraitTags::ActivationOnSpawn);
}
void UGCS_ComboAbility::PreActivate(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo,
FOnGameplayAbilityEnded::FDelegate* OnGameplayAbilityEndedDelegate, const FGameplayEventData* TriggerEventData)
{
Super::PreActivate(Handle, ActorInfo, ActivationInfo, OnGameplayAbilityEndedDelegate, TriggerEventData);
}
void UGCS_ComboAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo,
const FGameplayEventData* TriggerEventData)
{
UGCS_CombatSystemComponent* CombatSys = GetCombatSystemFromActorInfo();
if (!CombatSys)
{
EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, true, false);
return;
}
UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo();
AbilityEndedDelegateHandle = ASC->OnAbilityEnded.AddUObject(this, &ThisClass::HandleAbilityEnd);
Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
}
bool UGCS_ComboAbility::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags,
const FGameplayTagContainer* TargetTags, FGameplayTagContainer* OptionalRelevantTags) const
{
return Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags);
}
void UGCS_ComboAbility::OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
Super::OnGiveAbility(ActorInfo, Spec);
// GiveSubAbilities(Spec);
}
void UGCS_ComboAbility::OnRemoveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
Super::OnRemoveAbility(ActorInfo, Spec);
// RemoveSubAbilities();
}
void UGCS_ComboAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility,
bool bWasCancelled)
{
UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo();
if (IsValid(ASC) && AbilityEndedDelegateHandle.IsValid())
{
ASC->OnAbilityEnded.Remove(AbilityEndedDelegateHandle);
AbilityEndedDelegateHandle.Reset();
}
// final check to make current ability ends(no callback will fired.)!
if (CurrentAbility.IsValid() && !bCurrentAbilityEnded)
{
ASC->CancelAbilityHandle(CurrentAbility);
CurrentAbility = FGameplayAbilitySpecHandle();
CurrentAbilityClass = nullptr;
}
ResetCombo();
Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
}
bool UGCS_ComboAbility::AllowAdvanceCombo_Implementation() const
{
return true;
}
void UGCS_ComboAbility::StartCombo_Implementation(const FGameplayEventData& ComboEvent)
{
HandleComboExecution(ComboEvent);
}
void UGCS_ComboAbility::AdvanceCombo_Implementation(const FGameplayEventData& ComboEventData)
{
if (!AllowAdvanceCombo())
{
GCS_CLOG(Verbose, "Can't advanced combo due to AllowAdvanceCombo.")
return;
}
HandleComboExecution(ComboEventData);
}
void UGCS_ComboAbility::ResetCombo_Implementation()
{
DesiredComboStep = INDEX_NONE;
bCurrentAbilityEnded = false;
UGCS_CombatSystemComponent* CombatSys = GetCombatSystemFromActorInfo();
if (IsValid(CombatSys))
{
CombatSys->ResetComboState();
}
}
void UGCS_ComboAbility::HandleAbilityEnd(const FAbilityEndedData& AbilityEndedData)
{
//Already ends
if (bCurrentAbilityEnded)
{
return;
}
//no ability running.
if (!CurrentAbility.IsValid() || CurrentAbility != AbilityEndedData.AbilitySpecHandle)
{
return;
}
// GCS_CLOG(Verbose, "Current ability:%s ended. bWasCancelled:%d", *CurrentAbilityClass->GetName(), AbilityEndedData.bWasCancelled)
bCurrentAbilityEnded = true;
CurrentAbility = FGameplayAbilitySpecHandle();
CurrentAbilityClass = nullptr;
// No next combo.
if (DesiredComboStep == INDEX_NONE)
{
ResetCombo();
}
}
bool UGCS_ComboAbility::SelectComboDefinition(const FGameplayEventData& ComboEventData, int32 CurrentStep, FGCS_ComboDefinition& OutDefinition)
{
UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo();
UObject* Combat = GetCombatEntityFromActorInfo();
if (!ASC || !Combat) return false;
TObjectPtr<const UDataTable> ComboDefinitionTable = IGCS_CombatEntityInterface::Execute_GetComboDefinitionTable(Combat);
if (ComboDefinitionTable == nullptr)
{
GCS_CLOG(Error, "No combo definition table found from combat interface:%s, check your GetComboDefinitionTable implementation!", *GetNameSafe(Combat));
return false;
}
FGameplayTagContainer CurrentTags;
ASC->GetOwnedGameplayTags(CurrentTags);
// Find best matching row in data table
for (const auto& RowPair : ComboDefinitionTable->GetRowMap())
{
const FGCS_ComboDefinition* Row = reinterpret_cast<const FGCS_ComboDefinition*>(RowPair.Value);
if (!Row || Row->AbilityClass.IsNull()) continue;
if (!Row->TagQuery.IsEmpty() && !Row->TagQuery.Matches(CurrentTags))
{
continue;
}
// skip if event tag doesn't match.
if (ComboEventData.EventTag.IsValid() && Row->EventTag.IsValid() && ComboEventData.EventTag != Row->EventTag)
{
continue;
}
// skip if event instigatorTags
if (!Row->EventInstigatorTagQuery.IsEmpty() && !Row->EventInstigatorTagQuery.Matches(ComboEventData.InstigatorTags))
{
continue;
}
if (CurrentStep > 0 && Row->MinComboStep == 0)
{
continue;
}
// skip if MinComboStep < CurrentStep(only for non resetting combo)
if (!Row->bResetComboStep && Row->MinComboStep > 0 && Row->MinComboStep < CurrentStep) continue;
// skip if ability class is invalid.
TSubclassOf<UGameplayAbility> TempAbilityClass = Row->AbilityClass.LoadSynchronous();
if (TempAbilityClass == nullptr)
{
GCS_CLOG(Verbose, "invalid ability class found on row name:%s", *RowPair.Key.ToString());
continue;
}
FGameplayAbilitySpecHandle FoundAbility;
if (!UGGA_AbilitySystemFunctionLibrary::FindAbilityFromClass(ASC, FoundAbility, TempAbilityClass, GetCurrentSourceObject()))
{
GCS_CLOG(Verbose, "skipped ability of class(%s) as it is not exists on ASC.", *TempAbilityClass->GetName());
continue;
}
if (Row->bRunActivationTest)
{
FGameplayTagContainer RelevantTags;
if (!UGGA_AbilitySystemFunctionLibrary::CanActivateAbility(ASC, FoundAbility, RelevantTags))
{
if (Row->bAbortIfActivationTestFailed)
{
GCS_CLOG(Verbose, "Activation test failed for ability:%s, reason:%s. Combo will be aborted.", *TempAbilityClass->GetName(), *RelevantTags.ToString());
return false;
}
{
GCS_CLOG(Verbose, "skipped ability of class(%s) due to activation test failed, reason:%s", *TempAbilityClass->GetName(), *RelevantTags.ToString());
continue;
}
}
}
// skip if custom rule doesn't met.
if (!CanSelectedComboDefinition(ComboEventData, CurrentStep, OutDefinition))
{
continue;
}
GCS_CLOG(Verbose, "selected ability of class(%s) as next combo(%d).", *TempAbilityClass->GetName(), CurrentStep);
OutDefinition = *Row;
return true;
}
return false;
}
bool UGCS_ComboAbility::CanSelectedComboDefinition_Implementation(const FGameplayEventData& ComboEvent, int32 CurrentStep, const FGCS_ComboDefinition& ComboDefinition) const
{
// Example code.
// FYourCustomData Data = ComboDefinition.Extension.Get<FYourCustomData>();
// UObject* Weapon = IGCS_CombatInterface::Execute_GCS_GetWeapon(GetCombatInterfaceFromActorInfo(), nullptr);
// return IGCS_WeaponInterface::Execute_GetWeaponTags(Weapon).MatchesQuery(Data.WeaponTagRequirements);
return true;
}
void UGCS_ComboAbility::HandleComboExecution(const FGameplayEventData& ComboEventData)
{
if (!IsLocallyControlled())
{
return;
}
UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo();
if (!ASC) return;
UGCS_CombatSystemComponent* CombatSys = GetCombatSystemFromActorInfo();
int32 CurrentStep = CombatSys ? CombatSys->GetComboStep() : 0;
bool bStartingCombo = CurrentStep == 0;
FGCS_ComboDefinition ComboDefinition;
if (!SelectComboDefinition(ComboEventData, CurrentStep, ComboDefinition))
{
GCS_CLOG(Verbose, "No next combo definition with current combo step:%d", CurrentStep);
DesiredComboStep = INDEX_NONE; // mark for no next action.
return;
}
TSubclassOf<UGameplayAbility> FoundAbilityClass = ComboDefinition.AbilityClass.Get();
FGameplayAbilitySpecHandle FoundAbility;
UGGA_AbilitySystemFunctionLibrary::FindAbilityFromClass(ASC, FoundAbility, FoundAbilityClass, GetCurrentSourceObject());
check(FoundAbility.IsValid())
bool bShouldResetComboStep = !bStartingCombo && ComboDefinition.MinComboStep > 0 && ComboDefinition.bResetComboStep;
// Mark the desired step.
DesiredComboStep = bShouldResetComboStep ? 0 : CombatSys->GetComboStep() + 1;
// Cancel current ability if still running.
if (CurrentAbility.IsValid() && !bCurrentAbilityEnded)
{
ASC->CancelAbilityHandle(CurrentAbility);
CurrentAbility = FGameplayAbilitySpecHandle();
CurrentAbilityClass = nullptr;
}
bCurrentAbilityEnded = false;
//Combo Ability本身是预测激活的.
if (ASC->TryActivateAbility(FoundAbility, true))
{
CurrentAbility = FoundAbility;
CurrentAbilityClass = FoundAbilityClass;
CombatSys->UpdateComboStep(DesiredComboStep);
GCS_CLOG(Verbose, "combo advanced from step %d to %d with ability:%s", CurrentStep, DesiredComboStep, *FoundAbilityClass->GetName());
DesiredComboStep = INDEX_NONE;
}
else
{
GCS_CLOG(Verbose, "combo reset as the new ability(%s) can't be activated when advancing from step %d to %d.", *FoundAbilityClass->GetName(), CurrentStep, DesiredComboStep);
ResetCombo();
}
}
// void UGCS_ComboAbility::GiveSubAbilities(const FGameplayAbilitySpec& CurrentSpec)
// {
// UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo();
// if (!IsValid(ASC) || !ASC->IsOwnerActorAuthoritative())
// {
// return;
// }
//
// // Find best matching row in data table
// for (const auto& RowPair : ComboDefinitionTable->GetRowMap())
// {
// const FGCS_ComboDefinition* Row = reinterpret_cast<const FGCS_ComboDefinition*>(RowPair.Value);
// if (!Row || Row->AbilityClass.IsNull()) continue;
// TSubclassOf<UGameplayAbility> AbilityClass = Row->AbilityClass.LoadSynchronous();
//
// if (FGameplayAbilitySpec* ExistingSpec = UGGA_AbilitySystemFunctionLibrary::FindAbilitySpecFromClass(ASC, AbilityClass, CurrentSpec.SourceObject.Get()))
// {
// GCS_CLOG(Warning, "Found existing ability with same class(%s) and source object(%s),skipped.", *AbilityClass->GetName(), *GetNameSafe(CurrentSpec.SourceObject.Get()))
// continue;
// }
//
// FGameplayAbilitySpec NewSpec = ASC->BuildAbilitySpecFromClass(AbilityClass, Row->MinComboStep > 0 ? Row->MinComboStep : 0);
// NewSpec.SourceObject = CurrentSpec.SourceObject;
// const FGameplayAbilitySpecHandle& AbilitySpecHandle = ASC->GiveAbility(NewSpec);
// if (AbilitySpecHandle.IsValid())
// {
// AvailableAbilities.Add(AbilitySpecHandle);
// }
// }
// }
//
// void UGCS_ComboAbility::RemoveSubAbilities()
// {
// UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo();
// if (!IsValid(ASC))
// {
// return;
// }
// for (FGameplayAbilitySpecHandle SubAbilityHandle : AvailableAbilities)
// {
// ASC->ClearAbility(SubAbilityHandle);
// }
// AvailableAbilities.Empty();
// }
#if WITH_EDITOR
EDataValidationResult UGCS_ComboAbility::IsDataValid(class FDataValidationContext& Context) const
{
return Super::IsDataValid(Context);
}
#endif

View File

@@ -0,0 +1,255 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "AbilitySystem/Attributes/AS_Poise.h"
#include "Net/UnrealNetwork.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "GameplayEffectExtension.h"
#include "GGA_GameplayAttributesHelper.h"
#include "GGA_AttributeSystemComponent.h"
namespace AS_Poise
{
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Poise, TEXT("GGF.Attribute.PoiseSet.Poise"), "Current Poise value of an actor.(actor的当前抗打击值)")
UE_DEFINE_GAMEPLAY_TAG_COMMENT(MaxPoise, TEXT("GGF.Attribute.PoiseSet.MaxPoise"), "Max Poise value of an actor.(actor的最大抗打击值)")
UE_DEFINE_GAMEPLAY_TAG_COMMENT(PoiseRecover, TEXT("GGF.Attribute.PoiseSet.PoiseRecover"), "How many Poise to recover per second.(每秒恢复抗打击值)")
}
UAS_Poise::UAS_Poise()
{
UGGA_GameplayAttributesHelper::RegisterTagToAttribute(AS_Poise::Poise,GetPoiseAttribute());
UGGA_GameplayAttributesHelper::RegisterTagToAttribute(AS_Poise::MaxPoise,GetMaxPoiseAttribute());
UGGA_GameplayAttributesHelper::RegisterTagToAttribute(AS_Poise::PoiseRecover,GetPoiseRecoverAttribute());
}
void UAS_Poise::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION_NOTIFY(ThisClass, Poise, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(ThisClass, MaxPoise, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(ThisClass, PoiseRecover, COND_None, REPNOTIFY_Always);
}
void UAS_Poise::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
Super::PreAttributeChange(Attribute, NewValue);
if (Attribute == GetPoiseAttribute())
{
NewValue = FMath::Clamp(NewValue,0,GetMaxPoise());
}
if (AActor* Actor = GetOwningActor())
{
if (UGGA_AttributeSystemComponent* ASS = Actor->FindComponentByClass<UGGA_AttributeSystemComponent>())
{
ASS->ReceivePreAttributeChange(this,Attribute,NewValue);
}
}
}
bool UAS_Poise::PreGameplayEffectExecute(FGameplayEffectModCallbackData& Data)
{
if (AActor* Actor = GetOwningActor())
{
if (UGGA_AttributeSystemComponent* ASS = Actor->FindComponentByClass<UGGA_AttributeSystemComponent>())
{
return ASS->ReceivePreGameplayEffectExecute(this, Data);
}
}
return Super::PreGameplayEffectExecute(Data);
}
void UAS_Poise::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
{
Super::PostAttributeChange(Attribute, OldValue, NewValue);
if (Attribute == GetMaxPoiseAttribute())
{
AdjustAttributeForMaxChange(Poise, OldValue, NewValue, GetPoiseAttribute());
}
if (AActor* Actor = GetOwningActor())
{
if (UGGA_AttributeSystemComponent* ASS = Actor->FindComponentByClass<UGGA_AttributeSystemComponent>())
{
ASS->ReceivePostAttributeChange(this, Attribute, OldValue, NewValue);
}
}
}
void UAS_Poise::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
if (Data.EvaluatedData.Attribute == GetPoiseAttribute())
{
SetPoise(FMath::Clamp(GetPoise(),0,GetMaxPoise()));
}
if (AActor* Actor = GetOwningActor())
{
if (UGGA_AttributeSystemComponent* ASS = Actor->FindComponentByClass<UGGA_AttributeSystemComponent>())
{
ASS->ReceivePostGameplayEffectExecute(this,Data);
}
}
}
void UAS_Poise::AdjustAttributeForMaxChange(FGameplayAttributeData& AffectedAttribute, const FGameplayAttributeData& MaxAttribute, float NewMaxValue,
const FGameplayAttribute& AffectedAttributeProperty)
{
UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent();
const float CurrentMaxValue = MaxAttribute.GetCurrentValue();
if (!FMath::IsNearlyEqual(CurrentMaxValue, NewMaxValue) && AbilityComp)
{
// Change current value to maintain the current Val / Max percent
const float CurrentValue = AffectedAttribute.GetCurrentValue();
float NewDelta = (CurrentMaxValue > 0.f) ? (CurrentValue * NewMaxValue / CurrentMaxValue) - CurrentValue : NewMaxValue;
AbilityComp->ApplyModToAttributeUnsafe(AffectedAttributeProperty, EGameplayModOp::Additive, NewDelta);
}
}
FGameplayAttribute UAS_Poise::Bp_GetPoiseAttribute()
{
return ThisClass::GetPoiseAttribute();
}
float UAS_Poise::Bp_GetPoise() const
{
return GetPoise();
}
void UAS_Poise::Bp_SetPoise(float NewValue)
{
SetPoise(NewValue);
}
void UAS_Poise::Bp_InitPoise(float NewValue)
{
InitPoise(NewValue);
}
void UAS_Poise::OnRep_Poise(const FGameplayAttributeData& OldValue)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(ThisClass, Poise, OldValue);
if (AActor* Actor = GetOwningActor())
{
if (UGGA_AttributeSystemComponent* ASS = Actor->FindComponentByClass<UGGA_AttributeSystemComponent>())
{
ASS->ReceiveAttributeChange(this,GetPoiseAttribute(),GetPoise(),OldValue.GetCurrentValue());
}
}
}
FGameplayAttribute UAS_Poise::Bp_GetMaxPoiseAttribute()
{
return ThisClass::GetMaxPoiseAttribute();
}
float UAS_Poise::Bp_GetMaxPoise() const
{
return GetMaxPoise();
}
void UAS_Poise::Bp_SetMaxPoise(float NewValue)
{
SetMaxPoise(NewValue);
}
void UAS_Poise::Bp_InitMaxPoise(float NewValue)
{
InitMaxPoise(NewValue);
}
void UAS_Poise::OnRep_MaxPoise(const FGameplayAttributeData& OldValue)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(ThisClass, MaxPoise, OldValue);
if (AActor* Actor = GetOwningActor())
{
if (UGGA_AttributeSystemComponent* ASS = Actor->FindComponentByClass<UGGA_AttributeSystemComponent>())
{
ASS->ReceiveAttributeChange(this,GetMaxPoiseAttribute(),GetMaxPoise(),OldValue.GetCurrentValue());
}
}
}
FGameplayAttribute UAS_Poise::Bp_GetPoiseRecoverAttribute()
{
return ThisClass::GetPoiseRecoverAttribute();
}
float UAS_Poise::Bp_GetPoiseRecover() const
{
return GetPoiseRecover();
}
void UAS_Poise::Bp_SetPoiseRecover(float NewValue)
{
SetPoiseRecover(NewValue);
}
void UAS_Poise::Bp_InitPoiseRecover(float NewValue)
{
InitPoiseRecover(NewValue);
}
void UAS_Poise::OnRep_PoiseRecover(const FGameplayAttributeData& OldValue)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(ThisClass, PoiseRecover, OldValue);
if (AActor* Actor = GetOwningActor())
{
if (UGGA_AttributeSystemComponent* ASS = Actor->FindComponentByClass<UGGA_AttributeSystemComponent>())
{
ASS->ReceiveAttributeChange(this,GetPoiseRecoverAttribute(),GetPoiseRecover(),OldValue.GetCurrentValue());
}
}
}

View File

@@ -0,0 +1,9 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "AbilitySystem/DEPRECATED_GCS_AbilitySystemGlobals.h"
FGameplayEffectContext* UDEPRECATED_GCS_AbilitySystemGlobals::AllocGameplayEffectContext() const
{
return Super::AllocGameplayEffectContext();
}

View File

@@ -0,0 +1,29 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "AbilitySystem/Effects/GCS_GEComponent_PredictivelyExecute.h"
#include "GameplayEffect.h"
#include "GCS_LogChannels.h"
#include "Utility/GCS_CombatFunctionLibrary.h"
void UGCS_GEComponent_PredictivelyExecute::OnGameplayEffectApplied(FActiveGameplayEffectsContainer& ActiveGEContainer, FGameplayEffectSpec& GESpec, FPredictionKey& PredictionKey) const
{
FGCS_ContextPayload_Combat* Payload = UGCS_CombatFunctionLibrary::EffectContextGetMutableCombatPayload(GESpec.GetEffectContext());
if (Payload)
{
Payload->PredictionKey = PredictionKey;
}
if (GESpec.GetEffectContext().GetInstigator()->GetLocalRole() == ROLE_AutonomousProxy)
{
if (Payload)
{
Payload->bIsPredictingContext = true;
}
GCS_LOG(Verbose, "Ctx:%s %s predictively execute:%s", *GetClientServerContextString(GESpec.GetEffectContext().GetInstigator()), *GetNameSafe(GESpec.GetEffectContext().GetInstigator()),
*GetNameSafe(GESpec.Def))
ActiveGEContainer.PredictivelyExecuteEffectSpec(GESpec, PredictionKey, bPredictGameplayCues);
}
}

View File

@@ -0,0 +1,44 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "AbilitySystem/GCS_GameplayEffectContext.h"
void FGCS_ContextPayload_Combat::SetTaggedValue(const FGameplayTag& Tag, float NewValue)
{
if (Tag.IsValid())
{
bool bFound = false;
for (FGCS_TaggedValue& TaggedValue : TaggedValues)
{
if (TaggedValue.Attribute == Tag)
{
TaggedValue.Value = NewValue;
bFound = true;
break;
}
}
if (!bFound)
{
FGCS_TaggedValue Temp;
Temp.Attribute = Tag;
Temp.Value = NewValue;
TaggedValues.Add(Temp);
}
}
}
float FGCS_ContextPayload_Combat::GetTaggedValue(const FGameplayTag& Tag) const
{
if (Tag.IsValid())
{
for (const FGCS_TaggedValue& TaggedValue : TaggedValues)
{
if (TaggedValue.Attribute == Tag)
{
return TaggedValue.Value;
}
}
}
return 0.0f;
}

View File

@@ -0,0 +1,122 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "GenericCombatSystem/Public/AbilitySystem/Tasks/GCS_AbilityTask_CollisionTrace.h"
#include "GCS_LogChannels.h"
#include "Collision/GCS_TraceSystemComponent.h"
#include "Collision/DEPRECATED_GCS_CollisionTraceInstance.h"
#include "CombatFlow/GCS_AttackRequest.h"
#include "Utility/GCS_CombatFunctionLibrary.h"
UGCS_AbilityTask_CollisionTrace::UGCS_AbilityTask_CollisionTrace()
{
// make sure this task runs on simulated proxy.
bSimulatedTask = true;
}
UGCS_AbilityTask_CollisionTrace* UGCS_AbilityTask_CollisionTrace::HandleCollisionTraces(UGameplayAbility* OwningAbility, FName TaskInstanceName, bool bAdjustVisibilityBasedAnimTickOption)
{
UGCS_AbilityTask_CollisionTrace* MyTask = NewAbilityTask<UGCS_AbilityTask_CollisionTrace>(OwningAbility, TaskInstanceName);
MyTask->bAdjustAnimTickOption = bAdjustVisibilityBasedAnimTickOption;
return MyTask;
}
void UGCS_AbilityTask_CollisionTrace::Activate()
{
Super::Activate();
if (bAdjustAnimTickOption && GetAvatarActor()->GetNetMode() == NM_DedicatedServer)
{
if (USkeletalMeshComponent* SkeletalMeshComponent = UGCS_CombatFunctionLibrary::GetMainCharacterMeshComponent(GetAvatarActor()))
{
if (SkeletalMeshComponent->VisibilityBasedAnimTickOption != EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones)
{
PrevAnimTickOption = SkeletalMeshComponent->VisibilityBasedAnimTickOption;
SkeletalMeshComponent->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::AlwaysTickPoseAndRefreshBones;
bAdjustAnimTickOption = true;
}
}
}
if (UGCS_TraceSystemComponent* TSC = UGCS_TraceSystemComponent::GetTraceSystemComponent(GetAvatarActor()))
{
TSC->OnTraceHitEvent.AddDynamic(this, &ThisClass::TraceHitCallback);
}
}
void UGCS_AbilityTask_CollisionTrace::OnDestroy(bool bInOwnerFinished)
{
if (bAdjustAnimTickOption && bAdjustedAnimTickOption)
{
if (USkeletalMeshComponent* SkeletalMeshComponent = UGCS_CombatFunctionLibrary::GetMainCharacterMeshComponent(GetAvatarActor()))
{
SkeletalMeshComponent->VisibilityBasedAnimTickOption = PrevAnimTickOption;
}
}
if (UGCS_TraceSystemComponent* TSC = UGCS_TraceSystemComponent::GetTraceSystemComponent(GetAvatarActor()))
{
TSC->OnTraceHitEvent.RemoveDynamic(this, &ThisClass::TraceHitCallback);
for (auto& MeleeRequest : MeleeRequests)
{
TSC->StopTraces(MeleeRequest.Value);
}
}
MeleeRequests.Empty();
Super::OnDestroy(bInOwnerFinished);
}
void UGCS_AbilityTask_CollisionTrace::TraceHitCallback(const FGCS_TraceHandle& TraceHandle, const FHitResult& HitResult)
{
if (ShouldBroadcastAbilityTaskDelegates() && !MeleeRequests.IsEmpty() && TraceHandle.IsValidHandle())
{
TObjectPtr<const UGCS_AttackRequest_Melee> Req = nullptr;
bool bFound = false;
for (auto& MeleeRequest : MeleeRequests)
{
if (!MeleeRequest.Value.Contains(TraceHandle))
{
continue;
}
Req = MeleeRequest.Key;
bFound = true;
break;
}
if (bFound)
{
OnTargetsFound.Broadcast(Req, TraceHandle, HitResult);
}
}
}
void UGCS_AbilityTask_CollisionTrace::AddMeleeRequest(const UGCS_AttackRequest_Melee* Request, UObject* SourceObject)
{
if (IsValid(Request) && !MeleeRequests.Contains(Request))
{
const FGameplayTagContainer& TracesToControl = Request->TracesToControl;
if (UGCS_TraceSystemComponent* TSC = UGCS_TraceSystemComponent::GetTraceSystemComponent(GetAvatarActor()))
{
TArray<FGCS_TraceHandle> Handles = TSC->StartTraces(TracesToControl, SourceObject);
if (Handles.IsEmpty())
{
GCS_LOG(Warning, "Ability:(%s), No any trace started by melee request(%s) with source object(%s)", *GetNameSafe(Ability.Get()), *Request->GetPathName(), *GetNameSafe(SourceObject));
}
MeleeRequests.Emplace(Request, Handles);
}
}
}
void UGCS_AbilityTask_CollisionTrace::RemoveMeleeRequest(const UGCS_AttackRequest_Melee* Request)
{
if (IsValid(Request) && MeleeRequests.Contains(Request))
{
if (UGCS_TraceSystemComponent* TSC = UGCS_TraceSystemComponent::GetTraceSystemComponent(GetAvatarActor()))
{
TSC->StopTraces(MeleeRequests[Request]);
}
MeleeRequests.Remove(Request);
}
}