474 lines
15 KiB
C++
474 lines
15 KiB
C++
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
|
|
|
|
|
#include "Utility/GCS_CombatFunctionLibrary.h"
|
|
#include "Components/SkeletalMeshComponent.h"
|
|
#include "GCS_CombatEntityInterface.h"
|
|
#include "GCS_CombatSystemSettings.h"
|
|
#include "Team/GCS_CombatTeamAgentInterface.h"
|
|
#include "GCS_LogChannels.h"
|
|
#include "GGA_GameplayEffectContext.h"
|
|
#include "GameFramework/Pawn.h"
|
|
#include "GameFramework/Controller.h"
|
|
#include "AbilitySystem/GCS_GameplayEffectContext.h"
|
|
#include "CombatFlow/GCS_AttackRequest.h"
|
|
#include "GameFramework/Character.h"
|
|
#include "Kismet/KismetMathLibrary.h"
|
|
#include "Utilities/GGA_GameplayEffectFunctionLibrary.h"
|
|
#include "Weapon/GCS_WeaponInterface.h"
|
|
|
|
TScriptInterface<IGCS_CombatTeamAgentInterface> UGCS_CombatFunctionLibrary::GetCombatTeamAgentInterface(AActor* Actor)
|
|
{
|
|
if (IsValid(Actor))
|
|
{
|
|
if (Actor->GetClass()->ImplementsInterface(UGCS_CombatTeamAgentInterface::StaticClass()))
|
|
{
|
|
return Actor;
|
|
}
|
|
TArray<UActorComponent*> Components = Actor->GetComponentsByInterface(UGCS_CombatTeamAgentInterface::StaticClass());
|
|
return Components.IsValidIndex(0) ? Components[0] : nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool UGCS_CombatFunctionLibrary::FindCombatTeamAgentInterface(AActor* Actor, TScriptInterface<IGCS_CombatTeamAgentInterface>& OutInterface)
|
|
{
|
|
OutInterface = GetCombatTeamAgentInterface(Actor);
|
|
return OutInterface != nullptr;
|
|
}
|
|
|
|
TScriptInterface<IGCS_CombatEntityInterface> UGCS_CombatFunctionLibrary::GetCombatEntityInterface(AActor* Actor)
|
|
{
|
|
if (IsValid(Actor))
|
|
{
|
|
if (Actor->GetClass()->ImplementsInterface(UGCS_CombatEntityInterface::StaticClass()))
|
|
{
|
|
return Actor;
|
|
}
|
|
TArray<UActorComponent*> Components = Actor->GetComponentsByInterface(UGCS_CombatEntityInterface::StaticClass());
|
|
return Components.IsValidIndex(0) ? Components[0] : nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
UObject* UGCS_CombatFunctionLibrary::GetCombatEntity(AActor* Actor)
|
|
{
|
|
if (IsValid(Actor))
|
|
{
|
|
if (Actor->GetClass()->ImplementsInterface(UGCS_CombatEntityInterface::StaticClass()))
|
|
{
|
|
return Actor;
|
|
}
|
|
TArray<UActorComponent*> Components = Actor->GetComponentsByInterface(UGCS_CombatEntityInterface::StaticClass());
|
|
return Components.IsValidIndex(0) ? Components[0] : nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
TScriptInterface<IGCS_WeaponInterface> UGCS_CombatFunctionLibrary::GetWeaponInterface(AActor* Actor)
|
|
{
|
|
if (IsValid(Actor))
|
|
{
|
|
if (Actor->GetClass()->ImplementsInterface(UGCS_WeaponInterface::StaticClass()))
|
|
{
|
|
return Actor;
|
|
}
|
|
TArray<UActorComponent*> Components = Actor->GetComponentsByInterface(UGCS_WeaponInterface::StaticClass());
|
|
return Components.IsValidIndex(0) ? Components[0] : nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
USkeletalMeshComponent* UGCS_CombatFunctionLibrary::GetMainCharacterMeshComponent(AActor* Actor, FName OverrideMeshLookupTag)
|
|
{
|
|
if (IsValid(Actor))
|
|
{
|
|
if (OverrideMeshLookupTag != NAME_None)
|
|
{
|
|
TArray<UActorComponent*> Components = Actor->GetComponentsByTag(USkeletalMeshComponent::StaticClass(), OverrideMeshLookupTag);
|
|
if (Components.IsValidIndex(0))
|
|
{
|
|
return Cast<USkeletalMeshComponent>(Components[0]);
|
|
}
|
|
}
|
|
else if (const UGCS_CombatSystemSettings* Settings = UGCS_CombatSystemSettings::Get())
|
|
{
|
|
TArray<UActorComponent*> Components = Actor->GetComponentsByTag(USkeletalMeshComponent::StaticClass(), Settings->CharacterMeshLookupTag);
|
|
if (Components.IsValidIndex(0))
|
|
{
|
|
return Cast<USkeletalMeshComponent>(Components[0]);
|
|
}
|
|
}
|
|
|
|
if (ACharacter* Char = Cast<ACharacter>(Actor))
|
|
{
|
|
return Char->GetMesh();
|
|
}
|
|
|
|
if (USkeletalMeshComponent* Component = Cast<USkeletalMeshComponent>(Actor->GetComponentByClass(USkeletalMeshComponent::StaticClass())))
|
|
{
|
|
return Component;
|
|
}
|
|
|
|
UE_LOG(LogGCS, Warning, TEXT("Failed to find main character mesh component on actor class:%s"), *Actor->GetClass()->GetName());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
UMeshComponent* UGCS_CombatFunctionLibrary::GetMainMeshComponent(AActor* Actor, FName OverrideMeshLookupTag)
|
|
{
|
|
if (IsValid(Actor))
|
|
{
|
|
if (OverrideMeshLookupTag != NAME_None)
|
|
{
|
|
if (UActorComponent* Component = Actor->FindComponentByTag(UMeshComponent::StaticClass(), OverrideMeshLookupTag))
|
|
{
|
|
return Cast<UMeshComponent>(Component);
|
|
}
|
|
}
|
|
else if (const UGCS_CombatSystemSettings* Settings = UGCS_CombatSystemSettings::Get())
|
|
{
|
|
TArray<UActorComponent*> Components = Actor->GetComponentsByTag(UMeshComponent::StaticClass(), Settings->CharacterMeshLookupTag);
|
|
if (Components.IsValidIndex(0))
|
|
{
|
|
return Cast<UMeshComponent>(Components[0]);
|
|
}
|
|
}
|
|
|
|
if (UMeshComponent* Component = Cast<UMeshComponent>(Actor->GetComponentByClass(UMeshComponent::StaticClass())))
|
|
{
|
|
return Component;
|
|
}
|
|
|
|
UE_LOG(LogGCS, Warning, TEXT("Failed to find main mesh component on actor class:%s"), *Actor->GetClass()->GetName());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
TArray<FName> UGCS_CombatFunctionLibrary::GetSocketNamesWithPrefix(const USceneComponent* Component, FString Prefix, ESearchCase::Type SearchCase)
|
|
{
|
|
if (IsValid(Component))
|
|
{
|
|
return Component->GetAllSocketNames().FilterByPredicate([&](const FName& SocketName)
|
|
{
|
|
return SocketName.ToString().StartsWith(Prefix, SearchCase);
|
|
});
|
|
}
|
|
return {};
|
|
}
|
|
|
|
bool UGCS_CombatFunctionLibrary::FindCombatInterface(AActor* Actor, TScriptInterface<IGCS_CombatEntityInterface>& OutInterface)
|
|
{
|
|
OutInterface = GetCombatEntityInterface(Actor);
|
|
return OutInterface.GetObject() != nullptr;
|
|
}
|
|
|
|
bool UGCS_CombatFunctionLibrary::FindWeaponInterface(AActor* Actor, TScriptInterface<IGCS_WeaponInterface>& OutInterface)
|
|
{
|
|
OutInterface = GetWeaponInterface(Actor);
|
|
return OutInterface.GetObject() != nullptr;
|
|
}
|
|
|
|
FRotator UGCS_CombatFunctionLibrary::CalculateAngleBetweenActors(const AActor* From, const AActor* To)
|
|
{
|
|
if (IsValid(From) && IsValid(To))
|
|
{
|
|
return UKismetMathLibrary::NormalizedDeltaRotator(UKismetMathLibrary::FindLookAtRotation(From->GetActorLocation(), To->GetActorLocation()), From->GetActorRotation());
|
|
}
|
|
|
|
// TODO Warning.
|
|
return FRotator::ZeroRotator;
|
|
}
|
|
|
|
bool UGCS_CombatFunctionLibrary::IsSameCombatTeam(const AActor* A, const AActor* B)
|
|
{
|
|
return GetCombatTeamId(A) == GetCombatTeamId(B);
|
|
}
|
|
|
|
FGenericTeamId UGCS_CombatFunctionLibrary::GetCombatTeamId(const AActor* Actor)
|
|
{
|
|
return QueryCombatTeamId(Actor, true, true);
|
|
}
|
|
|
|
FGenericTeamId UGCS_CombatFunctionLibrary::QueryCombatTeamId(const AActor* Actor, bool bCombatAgent, bool bGenericAgent)
|
|
{
|
|
if (!IsValid(Actor))
|
|
{
|
|
return FGenericTeamId::NoTeam;
|
|
}
|
|
|
|
if (bCombatAgent)
|
|
{
|
|
if (Actor->GetClass()->ImplementsInterface(UGCS_CombatTeamAgentInterface::StaticClass()))
|
|
{
|
|
return IGCS_CombatTeamAgentInterface::Execute_GetCombatTeamId(Actor);
|
|
}
|
|
|
|
TArray<UActorComponent*> Components = Actor->GetComponentsByInterface(UGCS_CombatTeamAgentInterface::StaticClass());
|
|
if (Components.IsValidIndex(0))
|
|
{
|
|
return IGCS_CombatTeamAgentInterface::Execute_GetCombatTeamId(Components[0]);
|
|
}
|
|
|
|
if (const APawn* Pawn = Cast<APawn>(Actor))
|
|
{
|
|
if (Pawn->GetController() && Pawn->GetController()->GetClass()->ImplementsInterface(UGCS_CombatTeamAgentInterface::StaticClass()))
|
|
{
|
|
return IGCS_CombatTeamAgentInterface::Execute_GetCombatTeamId(Pawn->GetController());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bGenericAgent)
|
|
{
|
|
if (const IGenericTeamAgentInterface* GenericTeamAgentInterface = Cast<IGenericTeamAgentInterface>(Actor))
|
|
{
|
|
return GenericTeamAgentInterface->GetGenericTeamId();
|
|
}
|
|
|
|
if (const APawn* Pawn = Cast<APawn>(Actor))
|
|
{
|
|
if (const IGenericTeamAgentInterface* GenericTeamAgentInterface = Cast<IGenericTeamAgentInterface>(Pawn->GetController()))
|
|
{
|
|
return GenericTeamAgentInterface->GetGenericTeamId();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return FGenericTeamId::NoTeam;
|
|
}
|
|
|
|
EGCS_Direction UGCS_CombatFunctionLibrary::CalculateDirectionFromAngle(const float Angle)
|
|
{
|
|
if (UKismetMathLibrary::InRange_FloatFloat(Angle, -45.0f, 45.0f))
|
|
{
|
|
return EGCS_Direction::Forward;
|
|
}
|
|
|
|
if (UKismetMathLibrary::InRange_FloatFloat(Angle, 45.0f, 135.f))
|
|
{
|
|
return EGCS_Direction::Right;
|
|
}
|
|
|
|
if (UKismetMathLibrary::InRange_FloatFloat(Angle, -135.f, -45.f))
|
|
{
|
|
return EGCS_Direction::Left;
|
|
}
|
|
return EGCS_Direction::Backward;
|
|
}
|
|
|
|
TSoftObjectPtr<UAnimMontage> UGCS_CombatFunctionLibrary::SelectMontageByDirection(EGCS_Direction Direction, TArray<TSoftObjectPtr<UAnimMontage>> Montages)
|
|
{
|
|
switch (Direction)
|
|
{
|
|
case EGCS_Direction::Forward:
|
|
return Montages.IsValidIndex(0) ? Montages[0] : nullptr;
|
|
case EGCS_Direction::Backward:
|
|
return Montages.IsValidIndex(1) ? Montages[1] : nullptr;
|
|
case EGCS_Direction::Left:
|
|
return Montages.IsValidIndex(2) ? Montages[2] : nullptr;
|
|
case EGCS_Direction::Right:
|
|
return Montages.IsValidIndex(3) ? Montages[3] : nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void UGCS_CombatFunctionLibrary::AddTaggedValue(TArray<FGCS_TaggedValue>& TaggedValues, FGameplayTag Tag, float ValueToAdd)
|
|
{
|
|
bool bFound = false;
|
|
for (FGCS_TaggedValue& TaggedValue : TaggedValues)
|
|
{
|
|
if (TaggedValue.Attribute == Tag)
|
|
{
|
|
TaggedValue.Value += ValueToAdd;
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bFound)
|
|
{
|
|
FGCS_TaggedValue Temp;
|
|
Temp.Attribute = Tag;
|
|
Temp.Value = ValueToAdd;
|
|
TaggedValues.Add(Temp);
|
|
}
|
|
}
|
|
|
|
float UGCS_CombatFunctionLibrary::GetTaggedValue(const TArray<FGCS_TaggedValue> TaggedValues, FGameplayTag Tag)
|
|
{
|
|
for (const FGCS_TaggedValue& TaggedValue : TaggedValues)
|
|
{
|
|
if (TaggedValue.Attribute == Tag)
|
|
{
|
|
return TaggedValue.Value;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
FGameplayTagContainer UGCS_CombatFunctionLibrary::FilterGameplayTagContainer(const FGameplayTagContainer& TagContainer, FGameplayTagContainer OtherContainer)
|
|
{
|
|
return TagContainer.Filter(OtherContainer);
|
|
}
|
|
|
|
FGameplayEffectSpecHandle UGCS_CombatFunctionLibrary::AddAttackHandleToGameplayEffectSpec(FGameplayEffectSpecHandle SpecHandle, FDataTableRowHandle AttackHandle)
|
|
{
|
|
if (SpecHandle.IsValid() && !AttackHandle.IsNull())
|
|
{
|
|
if (FGCS_AttackDefinition* AtkDef = AttackHandle.GetRow<FGCS_AttackDefinition>(TEXT("AddAttackHandleToGameplayEffectSpec")))
|
|
{
|
|
AddAttackDefinitionToGameplayEffectSpec(SpecHandle, *AtkDef);
|
|
}
|
|
|
|
FGameplayEffectContextHandle ContextHandle = SpecHandle.Data->GetEffectContext();
|
|
EffectContextSetAttackDefinitionHandle(ContextHandle, AttackHandle);
|
|
}
|
|
return SpecHandle;
|
|
}
|
|
|
|
FGameplayEffectSpecHandle UGCS_CombatFunctionLibrary::AddAttackDefinitionToGameplayEffectSpec(FGameplayEffectSpecHandle SpecHandle, const FGCS_AttackDefinition& AtkDefinition)
|
|
{
|
|
if (SpecHandle.IsValid())
|
|
{
|
|
SpecHandle.Data->AppendDynamicAssetTags(AtkDefinition.AttackTags);
|
|
|
|
// apply set by callers from atk definition.
|
|
|
|
for (const TTuple<FGameplayTag, float>& ByCallerMagnitude : AtkDefinition.SetByCallerMagnitudes)
|
|
{
|
|
if (ByCallerMagnitude.Key.IsValid() && ByCallerMagnitude.Value > 0)
|
|
{
|
|
SpecHandle.Data->SetSetByCallerMagnitude(ByCallerMagnitude.Key, ByCallerMagnitude.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
return SpecHandle;
|
|
}
|
|
|
|
void UGCS_CombatFunctionLibrary::AddAttackHandleToGameplayEffectContainerSpec(FGGA_GameplayEffectContainerSpec ContainerSpec, FDataTableRowHandle AttackHandle)
|
|
{
|
|
if (!AttackHandle.IsNull())
|
|
{
|
|
if (FGCS_AttackDefinition* AtkDef = AttackHandle.GetRow<FGCS_AttackDefinition>(TEXT("AddAttackHandleToGameplayEffectSpec")))
|
|
{
|
|
for (const FGameplayEffectSpecHandle& SpecHandle : ContainerSpec.TargetGameplayEffectSpecs)
|
|
{
|
|
AddAttackDefinitionToGameplayEffectSpec(SpecHandle, *AtkDef);
|
|
FGameplayEffectContextHandle ContextHandle = SpecHandle.Data->GetEffectContext();
|
|
EffectContextSetAttackDefinitionHandle(ContextHandle, AttackHandle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UGCS_CombatFunctionLibrary::EffectContextSetAttackDefinitionHandle(FGameplayEffectContextHandle EffectContext, FDataTableRowHandle Handle)
|
|
{
|
|
if (FGCS_ContextPayload_Combat* Payload = EffectContextGetMutableCombatPayload(EffectContext))
|
|
{
|
|
Payload->AtkDataTable = Handle.DataTable;
|
|
Payload->AtkRowName = Handle.RowName;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogGCS, Error, TEXT("Can't access GCS_GameplayEffectContext! You need to setup GCS_AbilitySystemGlobals as AbilitySystemGlobalsClassName."))
|
|
}
|
|
}
|
|
|
|
FGCS_ContextPayload_Combat UGCS_CombatFunctionLibrary::EffectContextGetCombatPayload(FGameplayEffectContextHandle EffectContext)
|
|
{
|
|
if (FGGA_GameplayEffectContext* Context = UGGA_GameplayEffectFunctionLibrary::GetEffectContextPtr(EffectContext))
|
|
{
|
|
if (FGCS_ContextPayload_Combat* CombatPayload = Context->FindMutablePayloadByType<FGCS_ContextPayload_Combat>())
|
|
{
|
|
return *CombatPayload;
|
|
}
|
|
}
|
|
return FGCS_ContextPayload_Combat();
|
|
}
|
|
|
|
FGCS_ContextPayload_Combat* UGCS_CombatFunctionLibrary::EffectContextGetMutableCombatPayload(const FGameplayEffectContextHandle& EffectContext)
|
|
{
|
|
if (FGGA_GameplayEffectContext* Context = UGGA_GameplayEffectFunctionLibrary::GetEffectContextPtr(EffectContext))
|
|
{
|
|
if (FGCS_ContextPayload_Combat* CombatPayload = Context->FindOrAddMutablePayloadPtr<FGCS_ContextPayload_Combat>())
|
|
{
|
|
return CombatPayload;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void UGCS_CombatFunctionLibrary::EffectContextAddTagToCombatPayload(FGameplayEffectContextHandle EffectContext, FGameplayTag TagToAdd)
|
|
{
|
|
if (TagToAdd.IsValid())
|
|
{
|
|
if (FGCS_ContextPayload_Combat* Payload = EffectContextGetMutableCombatPayload(EffectContext))
|
|
{
|
|
Payload->DynamicTags.AddTagFast(TagToAdd);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UGCS_CombatFunctionLibrary::EffectContextSetTaggedValueToCombatPayload(FGameplayEffectContextHandle EffectContext, FGameplayTag Tag, float NewValue)
|
|
{
|
|
if (FGCS_ContextPayload_Combat* Payload = EffectContextGetMutableCombatPayload(EffectContext))
|
|
{
|
|
Payload->SetTaggedValue(Tag, NewValue);
|
|
}
|
|
}
|
|
|
|
float UGCS_CombatFunctionLibrary::EffectContextGetTaggedValueFromCombatPayload(FGameplayEffectContextHandle EffectContext, FGameplayTag Tag)
|
|
{
|
|
if (FGCS_ContextPayload_Combat* Payload = EffectContextGetMutableCombatPayload(EffectContext))
|
|
{
|
|
return Payload->GetTaggedValue(Tag);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void UGCS_CombatFunctionLibrary::EffectContextGetDynamicTagsFromCombatPayload(FGameplayEffectContextHandle EffectContext, FGameplayTagContainer& OutTags)
|
|
{
|
|
if (FGCS_ContextPayload_Combat* Payload = EffectContextGetMutableCombatPayload(EffectContext))
|
|
{
|
|
OutTags.Reset();
|
|
OutTags = Payload->DynamicTags;
|
|
}
|
|
}
|
|
|
|
FDataTableRowHandle UGCS_CombatFunctionLibrary::EffectContextGetAttackDefinitionHandle(FGameplayEffectContextHandle EffectContext)
|
|
{
|
|
if (FGCS_ContextPayload_Combat* Payload = EffectContextGetMutableCombatPayload(EffectContext))
|
|
{
|
|
FDataTableRowHandle Handle;
|
|
Handle.DataTable = Payload->AtkDataTable;
|
|
Handle.RowName = Payload->AtkRowName;
|
|
return Handle;
|
|
}
|
|
return FDataTableRowHandle();
|
|
}
|
|
|
|
bool UGCS_CombatFunctionLibrary::EffectContextGetIsPredictingContext(FGameplayEffectContextHandle EffectContext)
|
|
{
|
|
if (FGCS_ContextPayload_Combat* Payload = EffectContextGetMutableCombatPayload(EffectContext))
|
|
{
|
|
return Payload->bIsPredictingContext;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FGCS_AttackDefinition UGCS_CombatFunctionLibrary::EffectContextGetAttackDefinition(FGameplayEffectContextHandle EffectContext)
|
|
{
|
|
FDataTableRowHandle Handle = EffectContextGetAttackDefinitionHandle(EffectContext);
|
|
if (FGCS_AttackDefinition* Def = Handle.GetRow<FGCS_AttackDefinition>(TEXT("EffectContextGetAttackDefinition")))
|
|
{
|
|
return *Def;
|
|
}
|
|
return FGCS_AttackDefinition();
|
|
}
|