// 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 UGCS_CombatFunctionLibrary::GetCombatTeamAgentInterface(AActor* Actor) { if (IsValid(Actor)) { if (Actor->GetClass()->ImplementsInterface(UGCS_CombatTeamAgentInterface::StaticClass())) { return Actor; } TArray Components = Actor->GetComponentsByInterface(UGCS_CombatTeamAgentInterface::StaticClass()); return Components.IsValidIndex(0) ? Components[0] : nullptr; } return nullptr; } bool UGCS_CombatFunctionLibrary::FindCombatTeamAgentInterface(AActor* Actor, TScriptInterface& OutInterface) { OutInterface = GetCombatTeamAgentInterface(Actor); return OutInterface != nullptr; } TScriptInterface UGCS_CombatFunctionLibrary::GetCombatEntityInterface(AActor* Actor) { if (IsValid(Actor)) { if (Actor->GetClass()->ImplementsInterface(UGCS_CombatEntityInterface::StaticClass())) { return Actor; } TArray 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 Components = Actor->GetComponentsByInterface(UGCS_CombatEntityInterface::StaticClass()); return Components.IsValidIndex(0) ? Components[0] : nullptr; } return nullptr; } TScriptInterface UGCS_CombatFunctionLibrary::GetWeaponInterface(AActor* Actor) { if (IsValid(Actor)) { if (Actor->GetClass()->ImplementsInterface(UGCS_WeaponInterface::StaticClass())) { return Actor; } TArray 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 Components = Actor->GetComponentsByTag(USkeletalMeshComponent::StaticClass(), OverrideMeshLookupTag); if (Components.IsValidIndex(0)) { return Cast(Components[0]); } } else if (const UGCS_CombatSystemSettings* Settings = UGCS_CombatSystemSettings::Get()) { TArray Components = Actor->GetComponentsByTag(USkeletalMeshComponent::StaticClass(), Settings->CharacterMeshLookupTag); if (Components.IsValidIndex(0)) { return Cast(Components[0]); } } if (ACharacter* Char = Cast(Actor)) { return Char->GetMesh(); } if (USkeletalMeshComponent* Component = Cast(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(Component); } } else if (const UGCS_CombatSystemSettings* Settings = UGCS_CombatSystemSettings::Get()) { TArray Components = Actor->GetComponentsByTag(UMeshComponent::StaticClass(), Settings->CharacterMeshLookupTag); if (Components.IsValidIndex(0)) { return Cast(Components[0]); } } if (UMeshComponent* Component = Cast(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 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& OutInterface) { OutInterface = GetCombatEntityInterface(Actor); return OutInterface.GetObject() != nullptr; } bool UGCS_CombatFunctionLibrary::FindWeaponInterface(AActor* Actor, TScriptInterface& 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 Components = Actor->GetComponentsByInterface(UGCS_CombatTeamAgentInterface::StaticClass()); if (Components.IsValidIndex(0)) { return IGCS_CombatTeamAgentInterface::Execute_GetCombatTeamId(Components[0]); } if (const APawn* Pawn = Cast(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(Actor)) { return GenericTeamAgentInterface->GetGenericTeamId(); } if (const APawn* Pawn = Cast(Actor)) { if (const IGenericTeamAgentInterface* GenericTeamAgentInterface = Cast(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 UGCS_CombatFunctionLibrary::SelectMontageByDirection(EGCS_Direction Direction, TArray> 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& 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 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(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& 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(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()) { 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()) { 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(TEXT("EffectContextGetAttackDefinition"))) { return *Def; } return FGCS_AttackDefinition(); }