第一次提交
This commit is contained in:
@@ -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());
|
||||
}
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user