├── .gitattributes ├── README.md ├── Resources └── Icon128.png ├── Source └── ModularGameplayAbilities │ ├── Private │ ├── ModularGameplayAbilitiesConfig.cpp │ ├── ModularGameplayAbilitiesLogChannels.cpp │ ├── GameplayAbilities │ │ ├── ModularAbilitySourceInterface.cpp │ │ ├── ModularAbilitySimpleFailureMessage.h │ │ ├── Abilities │ │ │ ├── ModularAbilityCost_PlayerTagStack.cpp │ │ │ └── ModularGameplayAbility_Reset.cpp │ │ ├── ModularAbilityTagRelationshipMapping.cpp │ │ ├── ModularAbilitySet.cpp │ │ ├── ModularGlobalAbilitySystem.cpp │ │ └── ModularGameplayAbility.cpp │ ├── DataAsset │ │ ├── IAbilityPawnDataInterface.cpp │ │ └── ModularAbilityData.cpp │ ├── ModularGameplayAbilities.cpp │ ├── ModularAbilityPlayerState.cpp │ ├── Animation │ │ └── GameplayTagsAnimInstance.cpp │ ├── ModularAbilityTags.cpp │ └── ActorComponent │ │ ├── ModularAbilityExtensionComponent.cpp │ │ └── ModularAbilitySystemComponent.cpp │ ├── Public │ ├── ModularGameplayAbilitiesLogChannels.h │ ├── ModularGameplayAbilitiesConfig.h │ ├── ModularGameplayAbilitiesLogs.h │ ├── ModularGameplayAbilities.h │ ├── DataAsset │ │ ├── IAbilityPawnDataInterface.h │ │ └── ModularAbilityData.h │ ├── GameplayAbilities │ │ ├── Abilities │ │ │ ├── ModularGameplayAbility_Reset.h │ │ │ └── ModularAbilityCost_PlayerTagStack.h │ │ ├── ModularAbilitySourceInterface.h │ │ ├── ModularAbilityCost.h │ │ ├── ModularAbilityTagRelationshipMapping.h │ │ ├── ModularGlobalAbilitySystem.h │ │ ├── ModularAbilitySet.h │ │ └── ModularGameplayAbility.h │ ├── ModularAbilityTags.h │ ├── Animation │ │ └── GameplayTagsAnimInstance.h │ ├── ModularAbilityPlayerState.h │ └── ActorComponent │ │ ├── ModularAbilityExtensionComponent.h │ │ └── ModularAbilitySystemComponent.h │ └── ModularGameplayAbilities.Build.cs ├── ModularGameplayAbilities.uplugin └── .gitignore /.gitattributes: -------------------------------------------------------------------------------- 1 | Content/** filter=lfs diff=lfs merge=lfs -text -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PROTOTYPE CODE PROVIDED AS IS UNDER MIT LICENSE. 2 | -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chronicler-software/ModularGameplayAbilities/HEAD/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/ModularGameplayAbilitiesConfig.cpp: -------------------------------------------------------------------------------- 1 | #include "ModularGameplayAbilitiesConfig.h" 2 | 3 | UModularGameplayAbilitiesConfig::UModularGameplayAbilitiesConfig() 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/ModularGameplayAbilitiesLogChannels.h: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #pragma once 4 | 5 | MODULARGAMEPLAYABILITIES_API DECLARE_LOG_CATEGORY_EXTERN(LogModularGameplayAbilities, Log, All); 6 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/ModularGameplayAbilitiesLogChannels.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #include "ModularGameplayAbilitiesLogChannels.h" 4 | 5 | #include "ModularGameplayAbilitiesLogs.h" 6 | 7 | DEFINE_LOG_CATEGORY(LogModularGameplayAbilities); 8 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/GameplayAbilities/ModularAbilitySourceInterface.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "GameplayAbilities/ModularAbilitySourceInterface.h" 4 | 5 | #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularAbilitySourceInterface) 6 | 7 | UModularAbilitySourceInterface::UModularAbilitySourceInterface(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | {} 10 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/ModularGameplayAbilitiesConfig.h: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #pragma once 4 | 5 | #include "DataAsset/ModularAbilityData.h" 6 | 7 | #include "ModularGameplayAbilitiesConfig.generated.h" 8 | 9 | UCLASS(Config = Game) 10 | class UModularGameplayAbilitiesConfig : public UDeveloperSettings 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | UModularGameplayAbilitiesConfig(); 16 | 17 | 18 | // Global ability data asset to use. 19 | UPROPERTY(Config) 20 | TSoftObjectPtr ModularAbilityDataPath; 21 | }; 22 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/DataAsset/IAbilityPawnDataInterface.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #include "DataAsset/IAbilityPawnDataInterface.h" 4 | 5 | #include "GameplayAbilities/ModularAbilityTagRelationshipMapping.h" 6 | 7 | #include UE_INLINE_GENERATED_CPP_BY_NAME(IAbilityPawnDataInterface) 8 | 9 | 10 | TArray IAbilityPawnDataInterface::GetAbilitySet() const 11 | { 12 | return {}; 13 | } 14 | 15 | UModularAbilityTagRelationshipMapping* IAbilityPawnDataInterface::GetTagRelationshipMapping() const 16 | { 17 | return nullptr; 18 | } 19 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/DataAsset/ModularAbilityData.cpp: -------------------------------------------------------------------------------- 1 | #include "DataAsset/ModularAbilityData.h" 2 | 3 | #include "ModularGameplayAbilitiesConfig.h" 4 | #include "DataAsset/ModularAssetManager.h" 5 | 6 | #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularAbilityData) 7 | 8 | UModularAbilityData::UModularAbilityData() 9 | { 10 | } 11 | 12 | const UModularAbilityData& UModularAbilityData::UModularAbilityData::Get() 13 | { 14 | const UModularGameplayAbilitiesConfig* ModularGameplayAbilitiesConfig = GetDefault(); 15 | return UModularAssetManager::Get().GetOrLoadTypedGameData(ModularGameplayAbilitiesConfig->ModularAbilityDataPath); 16 | } 17 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/GameplayAbilities/ModularAbilitySimpleFailureMessage.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameplayTagContainer.h" 7 | #include "NativeGameplayTags.h" 8 | 9 | #include "ModularAbilitySimpleFailureMessage.generated.h" 10 | 11 | UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_ABILITY_SIMPLE_FAILURE_MESSAGE); 12 | 13 | USTRUCT(BlueprintType) 14 | struct FModularAbilitySimpleFailureMessage 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | UPROPERTY(BlueprintReadWrite) 20 | TObjectPtr PlayerController = nullptr; 21 | 22 | UPROPERTY(BlueprintReadWrite) 23 | FGameplayTagContainer FailureTags; 24 | 25 | UPROPERTY(BlueprintReadWrite) 26 | FText UserFacingReason; 27 | }; 28 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/ModularGameplayAbilities.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "ModularGameplayAbilities.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FModularGameplayAbilitiesModule" 6 | 7 | void FModularGameplayAbilitiesModule::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | } 11 | 12 | void FModularGameplayAbilitiesModule::ShutdownModule() 13 | { 14 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 15 | // we call this function before unloading the module. 16 | } 17 | 18 | #undef LOCTEXT_NAMESPACE 19 | 20 | IMPLEMENT_MODULE(FModularGameplayAbilitiesModule, ModularGameplayAbilities) 21 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/ModularGameplayAbilitiesLogs.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Logging/LogMacros.h" 6 | 7 | inline FString GetClientServerContextString(UObject* ContextObject) 8 | { 9 | ENetRole Role = ROLE_None; 10 | 11 | if (AActor* Actor = Cast(ContextObject)) 12 | { 13 | Role = Actor->GetLocalRole(); 14 | } 15 | else if (UActorComponent* Component = Cast(ContextObject)) 16 | { 17 | Role = Component->GetOwnerRole(); 18 | } 19 | 20 | if (Role != ROLE_None) 21 | { 22 | return (Role == ROLE_Authority) ? TEXT("Server") : TEXT("Client"); 23 | } 24 | else 25 | { 26 | #if WITH_EDITOR 27 | if (GIsEditor) 28 | { 29 | extern ENGINE_API FString GPlayInEditorContextString; 30 | return GPlayInEditorContextString; 31 | } 32 | #endif 33 | } 34 | 35 | return TEXT("[]"); 36 | } 37 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/ModularGameplayAbilities.h: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | /** 9 | * Adds data-driven modular Experience and Data capabilities to the Gameplay Abilities System. 10 | * 11 | * Typical use case: 12 | * 1. Add the ModularAbilitySystemComponent to the PlayerState Actor. 13 | * 2. Add the ModularAbilityExtensionComponent to your ModularExperienceCharacter Pawn. 14 | * 3. Implement the PawnAbilityDataInterface into your game's PawnData class. 15 | * 4. Add PawnData Assets to your experience as usual, now including ability configurations. 16 | */ 17 | class FModularGameplayAbilitiesModule final : public IModuleInterface 18 | { 19 | public: 20 | 21 | /** IModuleInterface implementation */ 22 | virtual void StartupModule() override; 23 | virtual void ShutdownModule() override; 24 | }; 25 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/DataAsset/IAbilityPawnDataInterface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "GameplayAbilities/ModularAbilitySet.h" 4 | #include "GameplayAbilities/ModularAbilityTagRelationshipMapping.h" 5 | 6 | #include "IAbilityPawnDataInterface.generated.h" 7 | 8 | UINTERFACE(MinimalAPI, NotBlueprintable) 9 | class UAbilityPawnDataInterface: public UInterface 10 | { 11 | GENERATED_BODY() 12 | }; 13 | 14 | /** 15 | * Interface for extending PawnData with Ability information. 16 | */ 17 | class MODULARGAMEPLAYABILITIES_API IAbilityPawnDataInterface 18 | { 19 | GENERATED_BODY() 20 | 21 | public: 22 | /** 23 | * Returns the pawn data's ability set property. 24 | * 25 | * @code TArray> AbilitySets; 26 | * 27 | * @return UModularAbilitySet*. 28 | */ 29 | virtual TArray GetAbilitySet() const; 30 | 31 | /** 32 | * Returns the pawn data's tag relationship mapping property. 33 | * 34 | * @code TObjectPtr TagRelationshipMapping; 35 | * 36 | * @return UModularAbilityTagRelationshipMapping*. 37 | */ 38 | virtual UModularAbilityTagRelationshipMapping* GetTagRelationshipMapping() const; 39 | }; 40 | -------------------------------------------------------------------------------- /ModularGameplayAbilities.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "ModularGameplayAbilities", 6 | "Description": "Adds data-driven modular Experience and Data capabilities to the Gameplay Abilities System.", 7 | "Category": "Other", 8 | "CreatedBy": "Chronicler", 9 | "CreatedByURL": "https://www.chroniclersoftware.com", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": true, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "ModularGameplayAbilities", 20 | "Type": "Runtime", 21 | "LoadingPhase": "Default" 22 | } 23 | ], 24 | "Plugins": [ 25 | { 26 | "Name": "GameplayAbilities", 27 | "Enabled": true 28 | }, 29 | { 30 | "Name": "GameplayMessageRouter", 31 | "Enabled": true 32 | }, 33 | { 34 | "Name": "ModalCamera", 35 | "Enabled": true 36 | }, 37 | { 38 | "Name": "ModularGameplayActors", 39 | "Enabled": true 40 | }, 41 | { 42 | "Name": "ModularGameplayData", 43 | "Enabled": true 44 | }, 45 | { 46 | "Name": "ModularGameplayExperiences", 47 | "Enabled": true 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 user specific files 2 | .vs/ 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | *.ipa 33 | 34 | # These project files can be generated by the engine 35 | *.xcodeproj 36 | *.xcworkspace 37 | *.sln 38 | *.suo 39 | *.opensdf 40 | *.sdf 41 | *.VC.db 42 | *.VC.opendb 43 | 44 | # Precompiled Assets 45 | SourceArt/**/*.png 46 | SourceArt/**/*.tga 47 | 48 | # Binary Files 49 | Binaries/* 50 | Plugins/*/Binaries/* 51 | 52 | # Builds 53 | Build/* 54 | 55 | # Whitelist PakBlacklist-.txt files 56 | !Build/*/ 57 | Build/*/** 58 | !Build/*/PakBlacklist*.txt 59 | 60 | # Don't ignore icon files in Build 61 | !Build/**/*.ico 62 | 63 | # Built data for maps 64 | *_BuiltData.uasset 65 | 66 | # Configuration files generated by the Editor 67 | Saved/* 68 | 69 | # Compiled source files for the engine to use 70 | Intermediate/* 71 | Plugins/*/Intermediate/* 72 | 73 | # Cache files for the editor to use 74 | DerivedDataCache/* 75 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/ModularGameplayAbilities.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class ModularGameplayAbilities : ModuleRules 6 | { 7 | public ModularGameplayAbilities(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | } 14 | ); 15 | 16 | 17 | PrivateIncludePaths.AddRange( 18 | new string[] { 19 | } 20 | ); 21 | 22 | 23 | PublicDependencyModuleNames.AddRange( 24 | new string[] 25 | { 26 | "Core", 27 | "GameFeatures", 28 | "GameplayAbilities", 29 | "ModalCamera", 30 | "ModularGameplay", 31 | "ModularGameplayExperiences" 32 | } 33 | ); 34 | 35 | 36 | PrivateDependencyModuleNames.AddRange( 37 | new string[] 38 | { 39 | "CoreUObject", 40 | "DeveloperSettings", 41 | "Engine", 42 | "EnhancedInput", 43 | "GameplayMessageRuntime", 44 | "GameplayTags", 45 | "GameplayTasks", 46 | "ModularGameplayActors", 47 | "ModularGameplayData", 48 | "ModularGameplayExperiences", 49 | "NetCore", 50 | "Slate", 51 | "SlateCore", 52 | } 53 | ); 54 | 55 | 56 | DynamicallyLoadedModuleNames.AddRange( 57 | new string[] 58 | { 59 | } 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/DataAsset/ModularAbilityData.h: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #pragma once 4 | 5 | #include "GameplayEffect.h" 6 | 7 | #include "ModularAbilityData.generated.h" 8 | 9 | /** 10 | * Global data asset that contains modular ability data. 11 | */ 12 | UCLASS(BlueprintType, Const, meta=(DisplayName="Modular Ability Data", ShortTooltip="Global data asset containing modular ability data.")) 13 | class UModularAbilityData : public UPrimaryDataAsset 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | UModularAbilityData(); 19 | 20 | static const UModularAbilityData& Get(); 21 | 22 | // Gameplay effect used to apply damage. Uses SetByCaller for the damage magnitude. 23 | UPROPERTY(EditDefaultsOnly, Category = "Default Gameplay Effects", meta = (DisplayName = "Damage Gameplay Effect (SetByCaller)")) 24 | TSoftClassPtr DamageGameplayEffect_SetByCaller; 25 | 26 | // Gameplay effect used to apply healing. Uses SetByCaller for the healing magnitude. 27 | UPROPERTY(EditDefaultsOnly, Category = "Default Gameplay Effects", meta = (DisplayName = "Heal Gameplay Effect (SetByCaller)")) 28 | TSoftClassPtr HealGameplayEffect_SetByCaller; 29 | 30 | // Gameplay effect used to add and remove dynamic tags. 31 | UPROPERTY(EditDefaultsOnly, Category = "Default Gameplay Effects") 32 | TSoftClassPtr DynamicTagGameplayEffect; 33 | }; 34 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/GameplayAbilities/Abilities/ModularGameplayAbility_Reset.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "GameplayAbilities/ModularGameplayAbility.h" 6 | 7 | #include "ModularGameplayAbility_Reset.generated.h" 8 | 9 | class AActor; 10 | class UObject; 11 | struct FGameplayAbilityActorInfo; 12 | struct FGameplayEventData; 13 | 14 | /** 15 | * UModularGameplayAbility_Reset 16 | * 17 | * Gameplay ability used for handling quickly resetting the player back to initial spawn state. 18 | * Ability is activated automatically via the "GameplayEvent.RequestReset" ability trigger tag (server only). 19 | */ 20 | UCLASS() 21 | class MODULARGAMEPLAYABILITIES_API UModularGameplayAbility_Reset : public UModularGameplayAbility 22 | { 23 | GENERATED_BODY() 24 | 25 | public: 26 | UModularGameplayAbility_Reset(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); 27 | 28 | protected: 29 | 30 | virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override; 31 | }; 32 | 33 | 34 | 35 | USTRUCT(BlueprintType) 36 | struct FModularPlayerResetMessage 37 | { 38 | GENERATED_BODY() 39 | 40 | UPROPERTY(BlueprintReadOnly) 41 | TObjectPtr OwnerPlayerState = nullptr; 42 | }; 43 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/GameplayAbilities/ModularAbilitySourceInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #pragma once 4 | 5 | #include "UObject/Interface.h" 6 | 7 | #include "ModularAbilitySourceInterface.generated.h" 8 | 9 | class UObject; 10 | class UPhysicalMaterial; 11 | struct FGameplayTagContainer; 12 | 13 | /** Base interface for anything acting as a ability calculation source */ 14 | UINTERFACE() 15 | class UModularAbilitySourceInterface : public UInterface 16 | { 17 | GENERATED_UINTERFACE_BODY() 18 | }; 19 | 20 | class IModularAbilitySourceInterface 21 | { 22 | GENERATED_IINTERFACE_BODY() 23 | 24 | /** 25 | * Compute the multiplier for effect falloff with distance 26 | * 27 | * @param Distance Distance from source to target for ability calculations (distance bullet traveled for a gun, etc...) 28 | * @param SourceTags Aggregated Tags from the source 29 | * @param TargetTags Aggregated Tags currently on the target 30 | * 31 | * @return Multiplier to apply to the base attribute value due to distance 32 | */ 33 | virtual float GetDistanceAttenuation(float Distance, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr) const = 0; 34 | 35 | virtual float GetPhysicalMaterialAttenuation(const UPhysicalMaterial* PhysicalMaterial, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr) const = 0; 36 | }; 37 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/ModularAbilityPlayerState.cpp: -------------------------------------------------------------------------------- 1 | #include "ModularAbilityPlayerState.h" 2 | 3 | #include "ActorComponent/ModularAbilitySystemComponent.h" 4 | #include "GameplayTagStack.h" 5 | 6 | #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularAbilityPlayerState) 7 | 8 | AModularAbilityPlayerState::AModularAbilityPlayerState(const FObjectInitializer& ObjectInitializer) 9 | : Super(ObjectInitializer) 10 | { 11 | ModularAbilitySystemComponent = CreateDefaultSubobject("AbilitySystemComponent"); 12 | } 13 | 14 | UModularAbilitySystemComponent* AModularAbilityPlayerState::GetAbilitySystemComponent() const 15 | { 16 | return GetModularAbilitySystemComponent(); 17 | } 18 | 19 | void AModularAbilityPlayerState::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const 20 | { 21 | Super::GetLifetimeReplicatedProps(OutLifetimeProps); 22 | } 23 | 24 | void AModularAbilityPlayerState::AddStatTagStack(FGameplayTag Tag, int32 StackCount) 25 | { 26 | StatTags.AddStack(Tag, StackCount); 27 | } 28 | 29 | void AModularAbilityPlayerState::RemoveStatTagStack(FGameplayTag Tag, int32 StackCount) 30 | { 31 | StatTags.RemoveStack(Tag, StackCount); 32 | } 33 | 34 | int32 AModularAbilityPlayerState::GetStatTagStackCount(FGameplayTag Tag) const 35 | { 36 | return StatTags.GetStackCount(Tag); 37 | } 38 | 39 | bool AModularAbilityPlayerState::HasStatTag(FGameplayTag Tag) const 40 | { 41 | return StatTags.ContainsTag(Tag); 42 | } 43 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/Animation/GameplayTagsAnimInstance.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #include "Animation/GameplayTagsAnimInstance.h" 4 | 5 | #if WITH_EDITOR 6 | #include "Misc/DataValidation.h" 7 | #endif 8 | 9 | #include "AbilitySystemGlobals.h" 10 | #include "GameFramework/CharacterMovementComponent.h" 11 | 12 | #include UE_INLINE_GENERATED_CPP_BY_NAME(GameplayTagsAnimInstance) 13 | 14 | 15 | UGameplayTagsAnimInstance::UGameplayTagsAnimInstance(const FObjectInitializer& ObjectInitializer) 16 | : Super(ObjectInitializer) 17 | { 18 | } 19 | 20 | void UGameplayTagsAnimInstance::InitializeWithAbilitySystem(UAbilitySystemComponent* ASC) 21 | { 22 | check(ASC); 23 | 24 | GameplayTagPropertyMap.Initialize(this, ASC); 25 | } 26 | 27 | #if WITH_EDITOR 28 | EDataValidationResult UGameplayTagsAnimInstance::IsDataValid(FDataValidationContext& Context) const 29 | { 30 | Super::IsDataValid(Context); 31 | 32 | GameplayTagPropertyMap.IsDataValid(this, Context); 33 | 34 | return ((Context.GetNumErrors() > 0) ? EDataValidationResult::Invalid : EDataValidationResult::Valid); 35 | } 36 | #endif 37 | 38 | void UGameplayTagsAnimInstance::NativeInitializeAnimation() 39 | { 40 | Super::NativeInitializeAnimation(); 41 | 42 | if (AActor* OwningActor = GetOwningActor()) 43 | { 44 | if (UAbilitySystemComponent* AbilityComponent = UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(OwningActor)) 45 | { 46 | InitializeWithAbilitySystem(AbilityComponent); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/ModularAbilityTags.h: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | 4 | #pragma once 5 | 6 | #include "NativeGameplayTags.h" 7 | 8 | namespace ModularAbilityTags 9 | { 10 | MODULARGAMEPLAYABILITIES_API FGameplayTag FindTagByString(const FString& TagString, bool bMatchPartialString = false); 11 | 12 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_ActivateFail_IsDead); 13 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_ActivateFail_Cooldown); 14 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_ActivateFail_Cost); 15 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_ActivateFail_TagsBlocked); 16 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_ActivateFail_TagsMissing); 17 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_ActivateFail_Networking); 18 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_ActivateFail_ActivationGroup); 19 | 20 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Behavior_SurvivesDeath); 21 | 22 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Status_Crouching); 23 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Status_AutoRunning); 24 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Status_Death); 25 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Status_Death_Dying); 26 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Status_Death_Dead); 27 | }; 28 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/GameplayAbilities/Abilities/ModularAbilityCost_PlayerTagStack.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "GameplayTagContainer.h" 6 | #include "GameplayAbilities/ModularAbilityCost.h" 7 | #include "ScalableFloat.h" 8 | 9 | #include "ModularAbilityCost_PlayerTagStack.generated.h" 10 | 11 | struct FGameplayAbilityActivationInfo; 12 | struct FGameplayAbilitySpecHandle; 13 | 14 | class UModularGameplayAbility; 15 | class UObject; 16 | struct FGameplayAbilityActorInfo; 17 | 18 | /** 19 | * Represents a cost that requires expending a quantity of a tag stack on the player state 20 | */ 21 | UCLASS(meta=(DisplayName="Player Tag Stack")) 22 | class MODULARGAMEPLAYABILITIES_API UModularAbilityCost_PlayerTagStack : public UModularAbilityCost 23 | { 24 | GENERATED_BODY() 25 | 26 | public: 27 | UModularAbilityCost_PlayerTagStack(); 28 | 29 | //~UModularAbilityCost interface 30 | virtual bool CheckCost( 31 | const UModularGameplayAbility* Ability, 32 | const FGameplayAbilitySpecHandle Handle, 33 | const FGameplayAbilityActorInfo* ActorInfo, 34 | FGameplayTagContainer* OptionalRelevantTags) const override; 35 | virtual void ApplyCost( 36 | const UModularGameplayAbility* Ability, 37 | const FGameplayAbilitySpecHandle Handle, 38 | const FGameplayAbilityActorInfo* ActorInfo, 39 | const FGameplayAbilityActivationInfo ActivationInfo) override; 40 | //~End of UModularAbilityCost interface 41 | 42 | protected: 43 | /** How much of the tag to spend (keyed on ability level) */ 44 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Costs) 45 | FScalableFloat Quantity; 46 | 47 | /** Which tag to spend some of */ 48 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Costs) 49 | FGameplayTag Tag; 50 | }; 51 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/Animation/GameplayTagsAnimInstance.h: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #pragma once 4 | 5 | #include "Animation/AnimInstance.h" 6 | #include "GameplayEffectTypes.h" 7 | #include "GameplayTagsAnimInstance.generated.h" 8 | 9 | /** 10 | * Adds support for gameplay tags to the base game animation instance class. 11 | * 12 | * This should be used to avoid polling for Pawn gameplay tags in animation blueprints. 13 | * 14 | * FGameplayTagBlueprintPropertyMap has a hard dependency on the Ability System Component, since the component 15 | * also tracks the gameplay tags. 16 | * 17 | * @todo This should be relocated to a separate GameplayTags plugin once the ASC dependency is extricated. 18 | * 19 | * @see https://dev.epicgames.com/community/learning/tutorials/n2nJ/unreal-engine-fgameplaytagblueprintpropertymap-the-tag-watcher 20 | */ 21 | UCLASS(Config = Game) 22 | class UGameplayTagsAnimInstance : public UAnimInstance 23 | { 24 | GENERATED_BODY() 25 | 26 | public: 27 | 28 | UGameplayTagsAnimInstance(const FObjectInitializer& ObjectInitializer); 29 | 30 | /** 31 | * Initializes the animation instance with the ability system component. 32 | * 33 | * @param AbilityComponent The ability system component to initialize with. 34 | * 35 | * @todo Remove this. 36 | */ 37 | virtual void InitializeWithAbilitySystem(UAbilitySystemComponent* AbilityComponent); 38 | 39 | protected: 40 | 41 | #if WITH_EDITOR 42 | virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override; 43 | #endif 44 | 45 | virtual void NativeInitializeAnimation() override; 46 | 47 | protected: 48 | 49 | // Gameplay tags that can be mapped to blueprint variables. The variables will automatically update as the tags are added or removed. 50 | // These should be used instead of manually querying for the gameplay tags. 51 | UPROPERTY(EditDefaultsOnly, Category = "GameplayTags") 52 | FGameplayTagBlueprintPropertyMap GameplayTagPropertyMap; 53 | 54 | UPROPERTY(BlueprintReadOnly, Category = "Character State Data") 55 | float GroundDistance = -1.0f; 56 | }; 57 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/ModularAbilityPlayerState.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "GameplayTagStack.h" 4 | #include "ModularPlayerState.h" 5 | #include "ActorComponent/ModularAbilitySystemComponent.h" 6 | #include "Player/ModularExperiencePlayerState.h" 7 | 8 | #include "ModularAbilityPlayerState.generated.h" 9 | 10 | UCLASS(Config = "Game") 11 | class MODULARGAMEPLAYABILITIES_API AModularAbilityPlayerState : public AModularExperiencePlayerState 12 | { 13 | GENERATED_BODY() 14 | 15 | public: 16 | AModularAbilityPlayerState(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); 17 | 18 | UFUNCTION(BlueprintCallable, Category = "Lyra|PlayerState") 19 | UModularAbilitySystemComponent* GetModularAbilitySystemComponent() const { return ModularAbilitySystemComponent; } 20 | virtual UModularAbilitySystemComponent* GetAbilitySystemComponent() const; 21 | 22 | // Adds a specified number of stacks to the tag (does nothing if StackCount is below 1). 23 | UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "ModularAbility") 24 | void AddStatTagStack(FGameplayTag Tag, int32 StackCount); 25 | 26 | // Removes a specified number of stacks from the tag (does nothing if StackCount is below 1). 27 | UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "ModularAbility") 28 | void RemoveStatTagStack(FGameplayTag Tag, int32 StackCount); 29 | 30 | // Returns the stack count of the specified tag (or 0 if the tag is not present). 31 | UFUNCTION(BlueprintCallable, Category = "ModularAbility") 32 | int32 GetStatTagStackCount(FGameplayTag Tag) const; 33 | 34 | // Returns true if there is at least one stack of the specified tag. 35 | UFUNCTION(BlueprintCallable, Category = "ModularAbility") 36 | bool HasStatTag(FGameplayTag Tag) const; 37 | 38 | virtual void GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const override; 39 | 40 | private: 41 | // The ability system component sub-object used by player characters. 42 | UPROPERTY(VisibleAnywhere, Category = "ModularAbility|PlayerState") 43 | TObjectPtr ModularAbilitySystemComponent; 44 | 45 | //UPROPERTY(Replicated) 46 | //FGameplayTagStackContainer StatTags; 47 | }; 48 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/GameplayAbilities/Abilities/ModularAbilityCost_PlayerTagStack.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "GameplayAbilities/Abilities/ModularAbilityCost_PlayerTagStack.h" 4 | 5 | #include "ModularAbilityPlayerState.h" 6 | #include "GameFramework/Controller.h" 7 | #include "GameplayAbilities/ModularGameplayAbility.h" 8 | 9 | #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularAbilityCost_PlayerTagStack) 10 | 11 | class AModularAbilityPlayerState; 12 | 13 | UModularAbilityCost_PlayerTagStack::UModularAbilityCost_PlayerTagStack() 14 | { 15 | Quantity.SetValue(1.0f); 16 | } 17 | 18 | bool UModularAbilityCost_PlayerTagStack::CheckCost(const UModularGameplayAbility* Ability, const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, FGameplayTagContainer* OptionalRelevantTags) const 19 | { 20 | if (const AController* PlayerController = Ability->GetControllerFromActorInfo()) 21 | { 22 | if (const AModularAbilityPlayerState* PlayerState = Cast(PlayerController->PlayerState)) 23 | { 24 | const int32 AbilityLevel = Ability->GetAbilityLevel(Handle, ActorInfo); 25 | 26 | const float NumStacksReal = Quantity.GetValueAtLevel(AbilityLevel); 27 | const int32 NumStacks = FMath::TruncToInt(NumStacksReal); 28 | 29 | return PlayerState->GetStatTagStackCount(Tag) >= NumStacks; 30 | } 31 | } 32 | return false; 33 | } 34 | 35 | void UModularAbilityCost_PlayerTagStack::ApplyCost(const UModularGameplayAbility* Ability, const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) 36 | { 37 | if (ActorInfo->IsNetAuthority()) 38 | { 39 | if (const AController* PlayerController = Ability->GetControllerFromActorInfo()) 40 | { 41 | if (AModularAbilityPlayerState* PlayerState = Cast(PlayerController->PlayerState)) 42 | { 43 | const int32 AbilityLevel = Ability->GetAbilityLevel(Handle, ActorInfo); 44 | 45 | const float NumStacksReal = Quantity.GetValueAtLevel(AbilityLevel); 46 | const int32 NumStacks = FMath::TruncToInt(NumStacksReal); 47 | 48 | PlayerState->RemoveStatTagStack(Tag, NumStacks); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/GameplayAbilities/ModularAbilityCost.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameplayAbilitySpec.h" 7 | #include "Abilities/GameplayAbility.h" 8 | #include "ModularAbilityCost.generated.h" 9 | 10 | class UModularGameplayAbility; 11 | /** 12 | * UModularAbilityCost 13 | * 14 | * Base class for costs that a ModularGameplayAbility has (e.g., ammo or charges) 15 | */ 16 | UCLASS(DefaultToInstanced, EditInlineNew, Abstract) 17 | class MODULARGAMEPLAYABILITIES_API UModularAbilityCost : public UObject 18 | { 19 | GENERATED_BODY() 20 | 21 | public: 22 | UModularAbilityCost() 23 | { 24 | } 25 | 26 | /** 27 | * Checks if we can afford this cost. 28 | * 29 | * A failure reason tag can be added to OptionalRelevantTags (if non-null), which can be queried 30 | * elsewhere to determine how to provide user feedback (e.g., a clicking noise if a weapon is out of ammo) 31 | * 32 | * Ability and ActorInfo are guaranteed to be non-null on entry, but OptionalRelevantTags can be nullptr. 33 | * 34 | * @return true if we can pay for the ability, false otherwise. 35 | */ 36 | virtual bool CheckCost(const UModularGameplayAbility* Ability, 37 | const FGameplayAbilitySpecHandle Handle, 38 | const FGameplayAbilityActorInfo* ActorInfo, 39 | FGameplayTagContainer* OptionalRelevantTags) const 40 | { 41 | return true; 42 | } 43 | 44 | /** 45 | * Applies the ability's cost to the target 46 | * 47 | * Notes: 48 | * - Your implementation don't need to check ShouldOnlyApplyCostOnHit(), the caller does that for you. 49 | * - Ability and ActorInfo are guaranteed to be non-null on entry. 50 | */ 51 | virtual void ApplyCost(const UModularGameplayAbility* Ability, 52 | const FGameplayAbilitySpecHandle Handle, 53 | const FGameplayAbilityActorInfo* ActorInfo, 54 | const FGameplayAbilityActivationInfo ActivationInfo) 55 | { 56 | } 57 | 58 | /** If true, this cost should only be applied if this ability hits successfully */ 59 | bool ShouldOnlyApplyCostOnHit() const { return bOnlyApplyCostOnHit; } 60 | 61 | protected: 62 | /** If true, this cost should only be applied if this ability hits successfully */ 63 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Costs) 64 | bool bOnlyApplyCostOnHit = false; 65 | }; 66 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/GameplayAbilities/ModularAbilityTagRelationshipMapping.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "GameplayAbilities/ModularAbilityTagRelationshipMapping.h" 4 | 5 | 6 | #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularAbilityTagRelationshipMapping) 7 | 8 | void UModularAbilityTagRelationshipMapping::GetAbilityTagsToBlockAndCancel(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer* OutTagsToBlock, FGameplayTagContainer* OutTagsToCancel) const 9 | { 10 | // Simple iteration for now 11 | for (int32 i = 0; i < AbilityTagRelationships.Num(); i++) 12 | { 13 | if (const auto& [AbilityTag, AbilityTagsToBlock, AbilityTagsToCancel, ActivationRequiredTags, ActivationBlockedTags] = AbilityTagRelationships[i]; 14 | AbilityTags.HasTag(AbilityTag)) 15 | { 16 | if (OutTagsToBlock) 17 | { 18 | OutTagsToBlock->AppendTags(AbilityTagsToBlock); 19 | } 20 | if (OutTagsToCancel) 21 | { 22 | OutTagsToCancel->AppendTags(AbilityTagsToCancel); 23 | } 24 | } 25 | } 26 | } 27 | 28 | void UModularAbilityTagRelationshipMapping::GetRequiredAndBlockedActivationTags(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer* OutActivationRequired, FGameplayTagContainer* OutActivationBlocked) const 29 | { 30 | // Simple iteration for now 31 | for (int32 i = 0; i < AbilityTagRelationships.Num(); i++) 32 | { 33 | if (const auto& [AbilityTag, AbilityTagsToBlock, AbilityTagsToCancel, ActivationRequiredTags, ActivationBlockedTags] = AbilityTagRelationships[i]; 34 | AbilityTags.HasTag(AbilityTag)) 35 | { 36 | if (OutActivationRequired) 37 | { 38 | OutActivationRequired->AppendTags(ActivationRequiredTags); 39 | } 40 | if (OutActivationBlocked) 41 | { 42 | OutActivationBlocked->AppendTags(ActivationBlockedTags); 43 | } 44 | } 45 | } 46 | } 47 | 48 | bool UModularAbilityTagRelationshipMapping::IsAbilityCancelledByTag(const FGameplayTagContainer& AbilityTags, const FGameplayTag& ActionTag) const 49 | { 50 | // Simple iteration for now 51 | for (int32 i = 0; i < AbilityTagRelationships.Num(); i++) 52 | { 53 | if (const auto& [AbilityTag, AbilityTagsToBlock, AbilityTagsToCancel, ActivationRequiredTags, ActivationBlockedTags] = AbilityTagRelationships[i]; 54 | AbilityTag == ActionTag && AbilityTagsToCancel.HasAny(AbilityTags)) 55 | { 56 | return true; 57 | } 58 | } 59 | 60 | return false; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/GameplayAbilities/Abilities/ModularGameplayAbility_Reset.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "GameplayAbilities/Abilities/ModularGameplayAbility_Reset.h" 4 | 5 | #include "ModularAbilityTags.h" 6 | #include "ActorComponent/ModularAbilitySystemComponent.h" 7 | #include "GameFramework/GameplayMessageSubsystem.h" 8 | #include "ModularGameplayTags.h" 9 | 10 | #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularGameplayAbility_Reset) 11 | 12 | UModularGameplayAbility_Reset::UModularGameplayAbility_Reset(const FObjectInitializer& ObjectInitializer) 13 | : Super(ObjectInitializer) 14 | { 15 | InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor; 16 | NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::ServerInitiated; 17 | 18 | if (HasAnyFlags(RF_ClassDefaultObject)) 19 | { 20 | // Add the ability trigger tag as default to the CDO. 21 | FAbilityTriggerData TriggerData; 22 | TriggerData.TriggerTag = ModularGameplayTags::GameplayEvent_RequestReset; 23 | TriggerData.TriggerSource = EGameplayAbilityTriggerSource::GameplayEvent; 24 | AbilityTriggers.Add(TriggerData); 25 | } 26 | } 27 | 28 | void UModularGameplayAbility_Reset::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) 29 | { 30 | check(ActorInfo); 31 | 32 | UModularAbilitySystemComponent* AbilityComponent = CastChecked(ActorInfo->AbilitySystemComponent.Get()); 33 | 34 | FGameplayTagContainer AbilityTypesToIgnore; 35 | AbilityTypesToIgnore.AddTag(ModularAbilityTags::Ability_Behavior_SurvivesDeath); 36 | 37 | // Cancel all abilities and block others from starting. 38 | AbilityComponent->CancelAbilities(nullptr, &AbilityTypesToIgnore, this); 39 | 40 | SetCanBeCanceled(false); 41 | 42 | // Execute the reset from the character 43 | if (AModularCharacter* ModularChar = Cast(CurrentActorInfo->AvatarActor.Get())) 44 | { 45 | ModularChar->Reset(); 46 | } 47 | 48 | // Let others know a reset has occurred 49 | FModularPlayerResetMessage Message; 50 | Message.OwnerPlayerState = CurrentActorInfo->OwnerActor.Get(); 51 | UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(this); 52 | MessageSystem.BroadcastMessage(ModularGameplayTags::GameplayEvent_Reset, Message); 53 | 54 | Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); 55 | 56 | constexpr bool bReplicateEndAbility = true; 57 | constexpr bool bWasCanceled = false; 58 | EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicateEndAbility, bWasCanceled); 59 | } 60 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/GameplayAbilities/ModularAbilityTagRelationshipMapping.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Engine/DataAsset.h" 6 | #include "GameplayTagContainer.h" 7 | 8 | #include "ModularAbilityTagRelationshipMapping.generated.h" 9 | 10 | class UObject; 11 | 12 | /** Struct that defines the relationship between different ability tags */ 13 | USTRUCT() 14 | struct FModularAbilityTagRelationship 15 | { 16 | GENERATED_BODY() 17 | 18 | /** The tag that this container relationship is about. Single tag, but abilities can have multiple of these */ 19 | UPROPERTY(EditAnywhere, Category = Ability, meta = (Categories = "Gameplay.Action")) 20 | FGameplayTag AbilityTag; 21 | 22 | /** The other ability tags that will be blocked by any ability using this tag */ 23 | UPROPERTY(EditAnywhere, Category = Ability) 24 | FGameplayTagContainer AbilityTagsToBlock; 25 | 26 | /** The other ability tags that will be canceled by any ability using this tag */ 27 | UPROPERTY(EditAnywhere, Category = Ability) 28 | FGameplayTagContainer AbilityTagsToCancel; 29 | 30 | /** If an ability has the tag, this is implicitly added to the activation required tags of the ability */ 31 | UPROPERTY(EditAnywhere, Category = Ability) 32 | FGameplayTagContainer ActivationRequiredTags; 33 | 34 | /** If an ability has the tag, this is implicitly added to the activation blocked tags of the ability */ 35 | UPROPERTY(EditAnywhere, Category = Ability) 36 | FGameplayTagContainer ActivationBlockedTags; 37 | }; 38 | 39 | 40 | /** Mapping of how ability tags block or cancel other abilities */ 41 | UCLASS() 42 | class UModularAbilityTagRelationshipMapping : public UDataAsset 43 | { 44 | GENERATED_BODY() 45 | 46 | private: 47 | /** The list of relationships between different gameplay tags (which ones block or cancel others) */ 48 | UPROPERTY(EditAnywhere, Category = Ability, meta=(TitleProperty="AbilityTag")) 49 | TArray AbilityTagRelationships; 50 | 51 | public: 52 | /** Given a set of ability tags, parse the tag relationship and fill out tags to block and cancel */ 53 | void GetAbilityTagsToBlockAndCancel(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer* OutTagsToBlock, FGameplayTagContainer* OutTagsToCancel) const; 54 | 55 | /** Given a set of ability tags, add additional required and blocking tags */ 56 | void GetRequiredAndBlockedActivationTags(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer* OutActivationRequired, FGameplayTagContainer* OutActivationBlocked) const; 57 | 58 | /** Returns true if the specified ability tags are canceled by the passed in action tag */ 59 | bool IsAbilityCancelledByTag(const FGameplayTagContainer& AbilityTags, const FGameplayTag& ActionTag) const; 60 | }; 61 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/GameplayAbilities/ModularGlobalAbilitySystem.h: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #pragma once 4 | 5 | #include "ActiveGameplayEffectHandle.h" 6 | #include "Subsystems/WorldSubsystem.h" 7 | #include "GameplayAbilitySpecHandle.h" 8 | #include "ActorComponent/ModularAbilitySystemComponent.h" 9 | #include "Templates/SubclassOf.h" 10 | 11 | #include "ModularGlobalAbilitySystem.generated.h" 12 | 13 | USTRUCT() 14 | struct FGlobalAppliedAbilityList 15 | { 16 | GENERATED_BODY() 17 | 18 | UPROPERTY() 19 | TMap, FGameplayAbilitySpecHandle> Handles; 20 | 21 | void AddToAbilityComponent(TSubclassOf Ability, UModularAbilitySystemComponent* AbilityComponent); 22 | void RemoveFromAbilityComponent(UModularAbilitySystemComponent* AbilityComponent); 23 | void RemoveFromAll(); 24 | }; 25 | 26 | USTRUCT() 27 | struct FGlobalAppliedEffectList 28 | { 29 | GENERATED_BODY() 30 | 31 | UPROPERTY() 32 | TMap, FActiveGameplayEffectHandle> Handles; 33 | 34 | void AddToAbilityComponent(TSubclassOf Effect, UModularAbilitySystemComponent* AbilityComponent); 35 | void RemoveFromAbilityComponent(UModularAbilitySystemComponent* AbilityComponent); 36 | void RemoveFromAll(); 37 | }; 38 | 39 | UCLASS() 40 | class UModularGlobalAbilitySystem : public UWorldSubsystem 41 | { 42 | GENERATED_BODY() 43 | 44 | public: 45 | UModularGlobalAbilitySystem(); 46 | 47 | UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="Modular") 48 | void ApplyAbilityToAll(TSubclassOf Ability); 49 | 50 | UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="Modular") 51 | void ApplyEffectToAll(TSubclassOf Effect); 52 | 53 | UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Modular") 54 | void RemoveAbilityFromAll(TSubclassOf Ability); 55 | 56 | UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Modular") 57 | void RemoveEffectFromAll(TSubclassOf Effect); 58 | 59 | /** Register an AbilityComponent with global system and apply any active global effects/abilities. */ 60 | void RegisterAbilityComponent(UModularAbilitySystemComponent* AbilityComponent); 61 | 62 | /** Removes an AbilityComponent from the global system, along with any active global effects/abilities. */ 63 | void UnregisterAbilityComponent(UModularAbilitySystemComponent* AbilityComponent); 64 | 65 | private: 66 | UPROPERTY() 67 | TMap, FGlobalAppliedAbilityList> AppliedAbilities; 68 | 69 | UPROPERTY() 70 | TMap, FGlobalAppliedEffectList> AppliedEffects; 71 | 72 | UPROPERTY() 73 | TArray> RegisteredAbilityComponents; 74 | }; 75 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/ModularAbilityTags.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | 4 | #include "ModularAbilityTags.h" 5 | 6 | namespace ModularAbilityTags 7 | { 8 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_ActivateFail_IsDead, "Ability.ActivateFail.IsDead", "Ability failed to activate because its owner is dead."); 9 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_ActivateFail_Cooldown, "Ability.ActivateFail.Cooldown", "Ability failed to activate because it is on cool down."); 10 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_ActivateFail_Cost, "Ability.ActivateFail.Cost", "Ability failed to activate because it did not pass the cost checks."); 11 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_ActivateFail_TagsBlocked, "Ability.ActivateFail.TagsBlocked", "Ability failed to activate because tags are blocking it."); 12 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_ActivateFail_TagsMissing, "Ability.ActivateFail.TagsMissing", "Ability failed to activate because tags are missing."); 13 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_ActivateFail_Networking, "Ability.ActivateFail.Networking", "Ability failed to activate because it did not pass the network checks."); 14 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_ActivateFail_ActivationGroup, "Ability.ActivateFail.ActivationGroup", "Ability failed to activate because of its activation group."); 15 | 16 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_Behavior_SurvivesDeath, "Ability.Behavior.SurvivesDeath", "An ability with this type tag should not be canceled due to death."); 17 | 18 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Status_Crouching, "Status.Crouching", "Target is crouching."); 19 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Status_AutoRunning, "Status.AutoRunning", "Target is auto-running."); 20 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Status_Death, "Status.Death", "Target has the death status."); 21 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Status_Death_Dying, "Status.Death.Dying", "Target has begun the death process."); 22 | UE_DEFINE_GAMEPLAY_TAG_COMMENT(Status_Death_Dead, "Status.Death.Dead", "Target has finished the death process."); 23 | 24 | FGameplayTag FindTagByString(const FString& TagString, bool bMatchPartialString) 25 | { 26 | const UGameplayTagsManager& Manager = UGameplayTagsManager::Get(); 27 | FGameplayTag Tag = Manager.RequestGameplayTag(FName(*TagString), false); 28 | 29 | if (!Tag.IsValid() && bMatchPartialString) 30 | { 31 | FGameplayTagContainer AllTags; 32 | Manager.RequestAllGameplayTags(AllTags, true); 33 | 34 | for (const FGameplayTag& TestTag : AllTags) 35 | { 36 | if (TestTag.ToString().Contains(TagString)) 37 | { 38 | UE_LOG(LogGameplayTags, Display, TEXT("Could not find exact match for tag [%s] but found partial match on tag [%s]."), *TagString, *TestTag.ToString()); 39 | Tag = TestTag; 40 | break; 41 | } 42 | } 43 | } 44 | 45 | return Tag; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/ActorComponent/ModularAbilityExtensionComponent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ModularAbilitySystemComponent.h" 3 | 4 | #include "ModularAbilityExtensionComponent.generated.h" 5 | 6 | /** 7 | * Gameplay Feature System Pawn component extending Pawns. 8 | * 9 | * Adds activation groups with blockers and cancel tags, and input bindings for activating abilities. 10 | * 11 | * The GAS component also completely manages tags and tag-related queries for the owning actor, 12 | * which muddies the separation of concerns. 13 | * 14 | * Partially derived from ULyraHeroComponent, with the remaining features split to Modular Experience's InputComponent. 15 | * 16 | * @todo Refactor the parent class's tag management into a separate component. 17 | * 18 | * @see LyraGame/Character/LyraHeroComponent 19 | */ 20 | UCLASS(ClassGroup=AbilitySystem, hidecategories=(Object,LOD,Lighting,Transform,Sockets,TextureStreaming), editinlinenew, meta=(BlueprintSpawnableComponent)) 21 | class MODULARGAMEPLAYABILITIES_API UModularAbilityExtensionComponent : public UPawnComponent, public IGameFrameworkInitStateInterface 22 | { 23 | GENERATED_BODY() 24 | 25 | public: 26 | UModularAbilityExtensionComponent(const FObjectInitializer& ObjectInitializer); 27 | 28 | void InitializeAbilitySystem(UModularAbilitySystemComponent* InASC, AActor* InOwnerActor); 29 | 30 | void UninitializeAbilitySystem(); 31 | 32 | /** 33 | * Gets the current ability system component, which may be owned by a different actor. 34 | */ 35 | UFUNCTION(BlueprintPure, Category = "AbilitySystem|Pawn") 36 | UModularAbilitySystemComponent* GetModularAbilitySystemComponent() const { return AbilitySystemComponent; } 37 | 38 | void HandleControllerChanged(); 39 | void InitializePlayerInput(UInputComponent* PlayerInputComponent); 40 | void InputActionMapping(UInputComponent* PlayerInputComponent, const APawn* Pawn); 41 | void Input_AbilityInputTagPressed(FGameplayTag InputTag); 42 | void Input_AbilityInputTagReleased(FGameplayTag InputTag); 43 | 44 | /** 45 | * @ingroup IGameFrameworkInitStateInterface 46 | * @{ 47 | */ 48 | 49 | virtual FName GetFeatureName() const override { return NAME_ActorFeatureName; } 50 | virtual bool CanChangeInitState(UGameFrameworkComponentManager* Manager, FGameplayTag CurrentState, FGameplayTag DesiredState) const override; 51 | virtual void HandleChangeInitState(UGameFrameworkComponentManager* Manager, FGameplayTag CurrentState, FGameplayTag DesiredState) override; 52 | virtual void OnActorInitStateChanged(const FActorInitStateChangedParams& Params) override; 53 | virtual void CheckDefaultInitialization() override; 54 | 55 | static const FName NAME_ActorFeatureName; 56 | /** 57 | * @} 58 | */ 59 | 60 | protected: 61 | virtual void OnRegister() override; 62 | virtual void BeginPlay() override; 63 | virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; 64 | 65 | /** 66 | * Pointer to the ability system component that is cached for convenience. 67 | * 68 | * @todo Look at Unreal service/singleton model to replace this convoluted startup. 69 | */ 70 | UPROPERTY() 71 | TObjectPtr AbilitySystemComponent; 72 | 73 | /** Delegate fired when our pawn becomes the ability system's avatar actor */ 74 | FSimpleMulticastDelegate OnAbilitySystemInitialized; 75 | 76 | /** Delegate fired when our pawn is removed as the ability system's avatar actor */ 77 | FSimpleMulticastDelegate OnAbilitySystemUninitialized; 78 | }; 79 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/GameplayAbilities/ModularAbilitySet.h: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #pragma once 4 | 5 | #include "ActiveGameplayEffectHandle.h" 6 | #include "AttributeSet.h" 7 | #include "GameplayAbilitySpecHandle.h" 8 | #include "GameplayTagContainer.h" 9 | #include "Abilities/GameplayAbility.h" 10 | #include "ActorComponent/ModularAbilitySystemComponent.h" 11 | 12 | #include "ModularAbilitySet.generated.h" 13 | 14 | /** 15 | * FModularAbilitySet_AttributeSet 16 | * 17 | * Data used by the ability set to grant attribute sets. 18 | */ 19 | USTRUCT(BlueprintType) 20 | struct FModularAbilitySet_AttributeSet 21 | { 22 | GENERATED_BODY() 23 | 24 | public: 25 | // Gameplay effect to grant. 26 | UPROPERTY(EditDefaultsOnly) 27 | TSubclassOf AttributeSet; 28 | 29 | }; 30 | 31 | /** 32 | * Data used by the ability set to grant gameplay abilities. 33 | */ 34 | USTRUCT(BlueprintType) 35 | struct FModularAbilitySet_GameplayAbility 36 | { 37 | GENERATED_BODY() 38 | 39 | public: 40 | 41 | // Gameplay ability to grant. 42 | UPROPERTY(EditDefaultsOnly) 43 | TSubclassOf Ability = nullptr; 44 | 45 | // Level of ability to grant. 46 | UPROPERTY(EditDefaultsOnly) 47 | int32 AbilityLevel = 1; 48 | 49 | // Tag used to process input for the ability. 50 | UPROPERTY(EditDefaultsOnly, Meta = (Categories = "InputTag")) 51 | FGameplayTag InputTag; 52 | }; 53 | 54 | 55 | /** 56 | * Data used by the ability set to grant gameplay effects. 57 | */ 58 | USTRUCT(BlueprintType) 59 | struct FModularAbilitySet_GameplayEffect 60 | { 61 | GENERATED_BODY() 62 | 63 | public: 64 | // Gameplay effect to grant. 65 | UPROPERTY(EditDefaultsOnly) 66 | TSubclassOf GameplayEffect = nullptr; 67 | 68 | // Level of gameplay effect to grant. 69 | UPROPERTY(EditDefaultsOnly) 70 | float EffectLevel = 1.0f; 71 | }; 72 | 73 | /** 74 | * Data used to store handles to what has been granted by the ability set. 75 | */ 76 | USTRUCT(BlueprintType) 77 | struct FModularAbilitySet_GrantedHandles 78 | { 79 | GENERATED_BODY() 80 | 81 | public: 82 | 83 | void AddAbilitySpecHandle(const FGameplayAbilitySpecHandle& Handle); 84 | void AddGameplayEffectHandle(const FActiveGameplayEffectHandle& Handle); 85 | void AddAttributeSet(UAttributeSet* Set); 86 | 87 | void TakeFromAbilitySystem(UModularAbilitySystemComponent* AbilitySystemComponent); 88 | 89 | protected: 90 | 91 | // Handles to the granted abilities. 92 | UPROPERTY() 93 | TArray AbilitySpecHandles; 94 | 95 | // Handles to the granted gameplay effects. 96 | UPROPERTY() 97 | TArray GameplayEffectHandles; 98 | 99 | // Pointers to the granted attribute sets 100 | UPROPERTY() 101 | TArray> GrantedAttributeSets; 102 | }; 103 | 104 | /** 105 | * Implementation of a GameplayAbilitySet enabling modular and data Experience features. 106 | * 107 | * @see Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/GameplayAbilitySet.h 108 | */ 109 | UCLASS(BlueprintType, Const) 110 | class MODULARGAMEPLAYABILITIES_API UModularAbilitySet : public UPrimaryDataAsset 111 | { 112 | GENERATED_BODY() 113 | 114 | public: 115 | UModularAbilitySet(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); 116 | 117 | /** 118 | * Grants the ability set to the specified ability system component. 119 | * The returned handles can be used later to take away anything that was granted. 120 | */ 121 | void GiveToAbilitySystem(UModularAbilitySystemComponent* ModularASC, FModularAbilitySet_GrantedHandles* OutGrantedHandles, UObject* SourceObject) const; 122 | 123 | protected: 124 | // Gameplay abilities to grant when this ability set is granted. 125 | UPROPERTY(EditDefaultsOnly, Category = "Gameplay Abilities", meta=(TitleProperty=Ability)) 126 | TArray GrantedGameplayAbilities; 127 | 128 | // Gameplay effects to grant when this ability set is granted. 129 | UPROPERTY(EditDefaultsOnly, Category = "Gameplay Effects", meta=(TitleProperty=GameplayEffect)) 130 | TArray GrantedGameplayEffects; 131 | 132 | // Attribute sets to grant when this ability set is granted. 133 | UPROPERTY(EditDefaultsOnly, Category = "Attribute Sets", meta=(TitleProperty=AttributeSet)) 134 | TArray GrantedAttributes; 135 | }; 136 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/GameplayAbilities/ModularAbilitySet.cpp: -------------------------------------------------------------------------------- 1 | #include "GameplayAbilities/ModularAbilitySet.h" 2 | 3 | #include "ModularGameplayAbilitiesLogChannels.h" 4 | #include "ActorComponent/ModularAbilitySystemComponent.h" 5 | 6 | #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularAbilitySet) 7 | 8 | void FModularAbilitySet_GrantedHandles::AddAbilitySpecHandle(const FGameplayAbilitySpecHandle& Handle) 9 | { 10 | if (Handle.IsValid()) 11 | { 12 | AbilitySpecHandles.Add(Handle); 13 | } 14 | } 15 | 16 | void FModularAbilitySet_GrantedHandles::AddGameplayEffectHandle(const FActiveGameplayEffectHandle& Handle) 17 | { 18 | if (Handle.IsValid()) 19 | { 20 | GameplayEffectHandles.Add(Handle); 21 | } 22 | } 23 | 24 | void FModularAbilitySet_GrantedHandles::AddAttributeSet(UAttributeSet* Set) 25 | { 26 | GrantedAttributeSets.Add(Set); 27 | } 28 | 29 | void FModularAbilitySet_GrantedHandles::TakeFromAbilitySystem(UModularAbilitySystemComponent* ModularASC) 30 | { 31 | check(ModularASC); 32 | 33 | if (!ModularASC->IsOwnerActorAuthoritative()) 34 | { 35 | // Must be authoritative to give or take ability sets. 36 | return; 37 | } 38 | 39 | for (const FGameplayAbilitySpecHandle& Handle : AbilitySpecHandles) 40 | { 41 | if (Handle.IsValid()) 42 | { 43 | ModularASC->ClearAbility(Handle); 44 | } 45 | } 46 | 47 | for (const FActiveGameplayEffectHandle& Handle : GameplayEffectHandles) 48 | { 49 | if (Handle.IsValid()) 50 | { 51 | ModularASC->RemoveActiveGameplayEffect(Handle); 52 | } 53 | } 54 | 55 | for (UAttributeSet* Set : GrantedAttributeSets) 56 | { 57 | ModularASC->RemoveSpawnedAttribute(Set); 58 | } 59 | 60 | AbilitySpecHandles.Reset(); 61 | GameplayEffectHandles.Reset(); 62 | GrantedAttributeSets.Reset(); 63 | } 64 | 65 | UModularAbilitySet::UModularAbilitySet(const FObjectInitializer& ObjectInitializer) 66 | : Super(ObjectInitializer) 67 | { 68 | } 69 | 70 | void UModularAbilitySet::GiveToAbilitySystem(UModularAbilitySystemComponent* ModularASC, FModularAbilitySet_GrantedHandles* OutGrantedHandles, UObject* SourceObject) const 71 | { 72 | check(ModularASC); 73 | 74 | if (!ModularASC->IsOwnerActorAuthoritative()) 75 | { 76 | // Must be authoritative to give or take ability sets. 77 | return; 78 | } 79 | 80 | // Grant the gameplay abilities. 81 | for (int32 AbilityIndex = 0; AbilityIndex < GrantedGameplayAbilities.Num(); ++AbilityIndex) 82 | { 83 | const FModularAbilitySet_GameplayAbility& AbilityToGrant = GrantedGameplayAbilities[AbilityIndex]; 84 | 85 | if (!IsValid(AbilityToGrant.Ability)) 86 | { 87 | UE_LOG(LogModularGameplayAbilities, Error, TEXT("GrantedGameplayAbilities[%d] on ability set [%s] is not valid."), AbilityIndex, *GetNameSafe(this)); 88 | continue; 89 | } 90 | 91 | UModularGameplayAbility* AbilityCDO = AbilityToGrant.Ability->GetDefaultObject(); 92 | 93 | FGameplayAbilitySpec AbilitySpec(AbilityCDO, AbilityToGrant.AbilityLevel); 94 | AbilitySpec.SourceObject = SourceObject; 95 | AbilitySpec.GetDynamicSpecSourceTags().AddTag(AbilityToGrant.InputTag); 96 | 97 | const FGameplayAbilitySpecHandle AbilitySpecHandle = ModularASC->GiveAbility(AbilitySpec); 98 | 99 | if (OutGrantedHandles) 100 | { 101 | OutGrantedHandles->AddAbilitySpecHandle(AbilitySpecHandle); 102 | } 103 | } 104 | 105 | // Grant the gameplay effects. 106 | for (int32 EffectIndex = 0; EffectIndex < GrantedGameplayEffects.Num(); ++EffectIndex) 107 | { 108 | const FModularAbilitySet_GameplayEffect& EffectToGrant = GrantedGameplayEffects[EffectIndex]; 109 | 110 | if (!IsValid(EffectToGrant.GameplayEffect)) 111 | { 112 | UE_LOG(LogModularGameplayAbilities, Error, TEXT("GrantedGameplayEffects[%d] on ability set [%s] is not valid"), EffectIndex, *GetNameSafe(this)); 113 | continue; 114 | } 115 | 116 | const UGameplayEffect* GameplayEffect = EffectToGrant.GameplayEffect->GetDefaultObject(); 117 | const FActiveGameplayEffectHandle GameplayEffectHandle = ModularASC->ApplyGameplayEffectToSelf(GameplayEffect, EffectToGrant.EffectLevel, ModularASC->MakeEffectContext()); 118 | 119 | if (OutGrantedHandles) 120 | { 121 | OutGrantedHandles->AddGameplayEffectHandle(GameplayEffectHandle); 122 | } 123 | } 124 | 125 | // Grant the attribute sets. 126 | for (int32 SetIndex = 0; SetIndex < GrantedAttributes.Num(); ++SetIndex) 127 | { 128 | const FModularAbilitySet_AttributeSet& SetToGrant = GrantedAttributes[SetIndex]; 129 | 130 | if (!IsValid(SetToGrant.AttributeSet)) 131 | { 132 | UE_LOG(LogModularGameplayAbilities, Error, TEXT("GrantedAttributes[%d] on ability set [%s] is not valid"), SetIndex, *GetNameSafe(this)); 133 | continue; 134 | } 135 | 136 | UAttributeSet* NewSet = NewObject(ModularASC->GetOwner(), SetToGrant.AttributeSet); 137 | ModularASC->AddAttributeSetSubobject(NewSet); 138 | 139 | if (OutGrantedHandles) 140 | { 141 | OutGrantedHandles->AddAttributeSet(NewSet); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/GameplayAbilities/ModularGlobalAbilitySystem.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #include "GameplayAbilities/ModularGlobalAbilitySystem.h" 4 | 5 | #include "ActorComponent/ModularAbilitySystemComponent.h" 6 | 7 | #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularGlobalAbilitySystem) 8 | 9 | void FGlobalAppliedAbilityList::AddToAbilityComponent(TSubclassOf Ability, UModularAbilitySystemComponent* AbilityComponent) 10 | { 11 | if (Handles.Find(AbilityComponent)) 12 | { 13 | RemoveFromAbilityComponent(AbilityComponent); 14 | } 15 | 16 | UGameplayAbility* AbilityCDO = Ability->GetDefaultObject(); 17 | const FGameplayAbilitySpec AbilitySpec(AbilityCDO); 18 | const FGameplayAbilitySpecHandle AbilitySpecHandle = AbilityComponent->GiveAbility(AbilitySpec); 19 | Handles.Add(AbilityComponent, AbilitySpecHandle); 20 | } 21 | 22 | void FGlobalAppliedAbilityList::RemoveFromAbilityComponent(UModularAbilitySystemComponent* AbilityComponent) 23 | { 24 | if (const FGameplayAbilitySpecHandle* SpecHandle = Handles.Find(AbilityComponent)) 25 | { 26 | AbilityComponent->ClearAbility(*SpecHandle); 27 | Handles.Remove(AbilityComponent); 28 | } 29 | } 30 | 31 | void FGlobalAppliedAbilityList::RemoveFromAll() 32 | { 33 | for (auto& Handle : Handles) 34 | { 35 | if (Handle.Key != nullptr) 36 | { 37 | Handle.Key->ClearAbility(Handle.Value); 38 | } 39 | } 40 | Handles.Empty(); 41 | } 42 | 43 | void FGlobalAppliedEffectList::AddToAbilityComponent(TSubclassOf Effect, UModularAbilitySystemComponent* AbilityComponent) 44 | { 45 | if (FActiveGameplayEffectHandle* EffectHandle = Handles.Find(AbilityComponent)) 46 | { 47 | RemoveFromAbilityComponent(AbilityComponent); 48 | } 49 | 50 | const UGameplayEffect* GameplayEffectCDO = Effect->GetDefaultObject(); 51 | // @todo: Level should be a parameter. 52 | const FActiveGameplayEffectHandle GameplayEffectHandle = AbilityComponent->ApplyGameplayEffectToSelf(GameplayEffectCDO, 1, AbilityComponent->MakeEffectContext()); 53 | Handles.Add(AbilityComponent, GameplayEffectHandle); 54 | } 55 | 56 | void FGlobalAppliedEffectList::RemoveFromAbilityComponent(UModularAbilitySystemComponent* AbilityComponent) 57 | { 58 | if (const FActiveGameplayEffectHandle* EffectHandle = Handles.Find(AbilityComponent)) 59 | { 60 | AbilityComponent->RemoveActiveGameplayEffect(*EffectHandle); 61 | Handles.Remove(AbilityComponent); 62 | } 63 | } 64 | 65 | void FGlobalAppliedEffectList::RemoveFromAll() 66 | { 67 | for (auto& Handle : Handles) 68 | { 69 | if (Handle.Key != nullptr) 70 | { 71 | Handle.Key->RemoveActiveGameplayEffect(Handle.Value); 72 | } 73 | } 74 | Handles.Empty(); 75 | } 76 | 77 | UModularGlobalAbilitySystem::UModularGlobalAbilitySystem() 78 | { 79 | } 80 | 81 | void UModularGlobalAbilitySystem::ApplyAbilityToAll(TSubclassOf Ability) 82 | { 83 | if ((Ability.Get() != nullptr) && (!AppliedAbilities.Contains(Ability))) 84 | { 85 | FGlobalAppliedAbilityList& Entry = AppliedAbilities.Add(Ability); 86 | for (UModularAbilitySystemComponent* AbilityComponent : RegisteredAbilityComponents) 87 | { 88 | Entry.AddToAbilityComponent(Ability, AbilityComponent); 89 | } 90 | } 91 | } 92 | 93 | void UModularGlobalAbilitySystem::ApplyEffectToAll(TSubclassOf Effect) 94 | { 95 | if ((Effect.Get() != nullptr) && (!AppliedEffects.Contains(Effect))) 96 | { 97 | FGlobalAppliedEffectList& Entry = AppliedEffects.Add(Effect); 98 | for (UModularAbilitySystemComponent* AbilityComponent : RegisteredAbilityComponents) 99 | { 100 | Entry.AddToAbilityComponent(Effect, AbilityComponent); 101 | } 102 | } 103 | } 104 | 105 | void UModularGlobalAbilitySystem::RemoveAbilityFromAll(TSubclassOf Ability) 106 | { 107 | if ((Ability.Get() != nullptr) && AppliedAbilities.Contains(Ability)) 108 | { 109 | FGlobalAppliedAbilityList& Entry = AppliedAbilities[Ability]; 110 | Entry.RemoveFromAll(); 111 | AppliedAbilities.Remove(Ability); 112 | } 113 | } 114 | 115 | void UModularGlobalAbilitySystem::RemoveEffectFromAll(TSubclassOf Effect) 116 | { 117 | if ((Effect.Get() != nullptr) && AppliedEffects.Contains(Effect)) 118 | { 119 | FGlobalAppliedEffectList& Entry = AppliedEffects[Effect]; 120 | Entry.RemoveFromAll(); 121 | AppliedEffects.Remove(Effect); 122 | } 123 | } 124 | 125 | void UModularGlobalAbilitySystem::RegisterAbilityComponent(UModularAbilitySystemComponent* AbilityComponent) 126 | { 127 | check(AbilityComponent); 128 | 129 | for (auto& Entry : AppliedAbilities) 130 | { 131 | Entry.Value.AddToAbilityComponent(Entry.Key, AbilityComponent); 132 | } 133 | for (auto& Entry : AppliedEffects) 134 | { 135 | Entry.Value.AddToAbilityComponent(Entry.Key, AbilityComponent); 136 | } 137 | 138 | RegisteredAbilityComponents.AddUnique(AbilityComponent); 139 | } 140 | 141 | void UModularGlobalAbilitySystem::UnregisterAbilityComponent(UModularAbilitySystemComponent* AbilityComponent) 142 | { 143 | check(AbilityComponent); 144 | for (auto& Entry : AppliedAbilities) 145 | { 146 | Entry.Value.RemoveFromAbilityComponent(AbilityComponent); 147 | } 148 | for (auto& Entry : AppliedEffects) 149 | { 150 | Entry.Value.RemoveFromAbilityComponent(AbilityComponent); 151 | } 152 | 153 | RegisteredAbilityComponents.Remove(AbilityComponent); 154 | } 155 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/ActorComponent/ModularAbilitySystemComponent.h: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #pragma once 4 | 5 | #include "AbilitySystemComponent.h" 6 | #include "NativeGameplayTags.h" 7 | #include "GameplayAbilities/ModularAbilityTagRelationshipMapping.h" 8 | #include "GameplayAbilities/ModularGameplayAbility.h" 9 | 10 | #include "ModularAbilitySystemComponent.generated.h" 11 | 12 | MODULARGAMEPLAYABILITIES_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Gameplay_AbilityInputBlocked); 13 | 14 | /** 15 | * Gameplay Feature System component extending the Gameplay Ability System Component with Enhanced Input. 16 | * 17 | * Adds activation groups with blockers and cancel tags, and input tags for activating abilities. 18 | * Requires the ModularAbilityExtensionComponent if you want to bind inputs to the input tags on dynamic load. 19 | * The GAS component also completely manages tags and tag-related queries for the owning actor, 20 | * which muddies the separation of concerns. 21 | * For runtime binding, add to both the ModularPlayerCharacter and ModularPlayerState. 22 | * Note: ModularAbilityExtensionComponent will automatically add this system component from the possessing player state. 23 | * 24 | * @todo Refactor the parent class's tag management into a separate component. 25 | */ 26 | UCLASS(ClassGroup=AbilitySystem, hidecategories=(Object,LOD,Lighting,Transform,Sockets,TextureStreaming), editinlinenew, meta=(BlueprintSpawnableComponent)) 27 | class MODULARGAMEPLAYABILITIES_API UModularAbilitySystemComponent : public UAbilitySystemComponent 28 | { 29 | GENERATED_BODY() 30 | 31 | public: 32 | 33 | UModularAbilitySystemComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); 34 | 35 | //~UActorComponent interface 36 | virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; 37 | //~End of UActorComponent interface 38 | 39 | /** 40 | * @ingroup UAbilitySystemComponent 41 | * @{ 42 | */ 43 | virtual void InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor) override; 44 | /** 45 | * @} 46 | */ 47 | 48 | typedef TFunctionRef TShouldCancelAbilityFunc; 49 | void CancelAbilitiesByFunc(TShouldCancelAbilityFunc ShouldCancelFunc, bool bReplicateCancelAbility); 50 | 51 | void CancelInputActivatedAbilities(bool bReplicateCancelAbility); 52 | 53 | void AbilityInputTagPressed(const FGameplayTag& InputTag); 54 | void AbilityInputTagReleased(const FGameplayTag& InputTag); 55 | bool IsInputTagActive(const FGameplayTag& InputTag) const; 56 | void SetInputTagActive(const FGameplayTag& InputTag, bool bActive); 57 | 58 | 59 | void ProcessAbilityInput(float DeltaTime, bool bGamePaused); 60 | void ClearAbilityInput(); 61 | 62 | bool IsActivationGroupBlocked(EModularAbilityActivationGroup Group) const; 63 | void AddAbilityToActivationGroup(EModularAbilityActivationGroup Group, UModularGameplayAbility* ModularAbility); 64 | void RemoveAbilityFromActivationGroup(EModularAbilityActivationGroup Group, UModularGameplayAbility* ModularAbility); 65 | void CancelActivationGroupAbilities(EModularAbilityActivationGroup Group, UModularGameplayAbility* IgnoreModularAbility, bool bReplicateCancelAbility); 66 | 67 | // Uses a gameplay effect to add the specified dynamic granted tag. 68 | void AddDynamicTagGameplayEffect(const FGameplayTag& Tag); 69 | 70 | // Removes all active instances of the gameplay effect that was used to add the specified dynamic granted tag. 71 | void RemoveDynamicTagGameplayEffect(const FGameplayTag& Tag); 72 | 73 | /** Gets the ability target data associated with the given ability handle and activation info */ 74 | void GetAbilityTargetData(const FGameplayAbilitySpecHandle AbilityHandle, FGameplayAbilityActivationInfo ActivationInfo, FGameplayAbilityTargetDataHandle& OutTargetDataHandle); 75 | 76 | /** Sets the current tag relationship mapping, if null it will clear it out */ 77 | void SetTagRelationshipMapping(UModularAbilityTagRelationshipMapping* NewMapping); 78 | 79 | /** Looks at ability tags and gathers additional required and blocking tags */ 80 | void GetAdditionalActivationTagRequirements(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer& OutActivationRequired, FGameplayTagContainer& OutActivationBlocked) const; 81 | 82 | protected: 83 | 84 | void TryActivateAbilitiesOnSpawn(); 85 | 86 | virtual void AbilitySpecInputPressed(FGameplayAbilitySpec& Spec) override; 87 | virtual void AbilitySpecInputReleased(FGameplayAbilitySpec& Spec) override; 88 | 89 | virtual void NotifyAbilityActivated( 90 | const FGameplayAbilitySpecHandle Handle, 91 | UGameplayAbility* Ability) override; 92 | virtual void NotifyAbilityFailed( 93 | const FGameplayAbilitySpecHandle Handle, 94 | UGameplayAbility* Ability, 95 | const FGameplayTagContainer& FailureReason) override; 96 | virtual void NotifyAbilityEnded( 97 | FGameplayAbilitySpecHandle Handle, 98 | UGameplayAbility* Ability, 99 | bool bWasCancelled) override; 100 | virtual void ApplyAbilityBlockAndCancelTags( 101 | const FGameplayTagContainer& AbilityTags, 102 | UGameplayAbility* RequestingAbility, 103 | bool bEnableBlockTags, 104 | const FGameplayTagContainer& BlockTags, 105 | bool bExecuteCancelTags, 106 | const FGameplayTagContainer& CancelTags) override; 107 | virtual void HandleChangeAbilityCanBeCanceled( 108 | const FGameplayTagContainer& AbilityTags, 109 | UGameplayAbility* RequestingAbility, 110 | bool bCanBeCanceled) override; 111 | 112 | /** Notify client that an ability failed to activate */ 113 | UFUNCTION(Client, Unreliable) 114 | void ClientNotifyAbilityFailed(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason); 115 | 116 | void HandleAbilityFailed(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason); 117 | protected: 118 | 119 | // If set, this table is used to look up tag relationships for activate and cancel 120 | UPROPERTY() 121 | TObjectPtr TagRelationshipMapping; 122 | 123 | // Handles to abilities that had their input pressed this frame. 124 | TArray InputPressedSpecHandles; 125 | 126 | // Handles to abilities that had their input released this frame. 127 | TArray InputReleasedSpecHandles; 128 | 129 | // Handles to abilities that have their input held. 130 | TArray InputHeldSpecHandles; 131 | 132 | // Number of abilities running in each activation group. 133 | int32 ActivationGroupCounts[static_cast(EModularAbilityActivationGroup::MAX)]; 134 | 135 | private: 136 | 137 | FGameplayTagContainer ActiveInputTags; 138 | }; 139 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Public/GameplayAbilities/ModularGameplayAbility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ModalCameraMode.h" 3 | #include "ModularAbilityCost.h" 4 | #include "ModularAbilitySourceInterface.h" 5 | #include "ModularCharacter.h" 6 | #include "ModularPlayerController.h" 7 | #include "Abilities/GameplayAbility.h" 8 | #include "ActorComponent/ModularPawnComponent.h" 9 | 10 | #include "ModularGameplayAbility.generated.h" 11 | 12 | /** 13 | * EModularAbilityActivationPolicy 14 | * 15 | * Defines how an ability is meant to activate. 16 | */ 17 | UENUM(BlueprintType) 18 | enum class EModularAbilityActivationPolicy : uint8 19 | { 20 | // Try to activate the ability when the input is triggered. 21 | OnInputTriggered, 22 | 23 | // Continually try to activate the ability while the input is active. 24 | WhileInputActive, 25 | 26 | // Try to activate the ability when an avatar is assigned. 27 | OnSpawn 28 | }; 29 | 30 | /** 31 | * EModularAbilityActivationGroup 32 | * 33 | * Defines how an ability activates in relation to other abilities. 34 | */ 35 | UENUM(BlueprintType) 36 | enum class EModularAbilityActivationGroup : uint8 37 | { 38 | // Ability runs independently of all other abilities. 39 | Independent, 40 | 41 | // Ability is canceled and replaced by other exclusive abilities. 42 | Exclusive_Replaceable, 43 | 44 | // Ability blocks all other exclusive abilities from activating. 45 | Exclusive_Blocking, 46 | 47 | MAX UMETA(Hidden) 48 | }; 49 | 50 | /** Failure reason that can be used to play an animation montage when a failure occurs */ 51 | USTRUCT(BlueprintType) 52 | struct FModularAbilityMontageFailureMessage 53 | { 54 | GENERATED_BODY() 55 | 56 | public: 57 | 58 | UPROPERTY(BlueprintReadWrite) 59 | TObjectPtr PlayerController = nullptr; 60 | 61 | // All the reasons why this ability has failed 62 | UPROPERTY(BlueprintReadWrite) 63 | FGameplayTagContainer FailureTags; 64 | 65 | UPROPERTY(BlueprintReadWrite) 66 | TObjectPtr FailureMontage = nullptr; 67 | }; 68 | 69 | /** 70 | * Extends the GameplayAbility class with grouping, activation policies, and utility functions. 71 | */ 72 | UCLASS(Abstract, HideCategories=("Input"), Meta=(ShortTooltip="Extends the GameplayAbility class with grouping, activation policies, and utility functions.")) 73 | class MODULARGAMEPLAYABILITIES_API UModularGameplayAbility : public UGameplayAbility 74 | { 75 | GENERATED_BODY() 76 | 77 | /** @todo This is an anti-pattern, replace with a design that does not need friend access. */ 78 | friend class UModularAbilitySystemComponent; 79 | 80 | public: 81 | UModularGameplayAbility(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); 82 | 83 | UFUNCTION(BlueprintCallable, Category = "Ability") 84 | UModularAbilitySystemComponent* GetModularAbilitySystemComponentFromActorInfo() const; 85 | 86 | UFUNCTION(BlueprintCallable, Category = "Ability") 87 | AModularPlayerController* GetModularPlayerControllerFromActorInfo() const; 88 | 89 | UFUNCTION(BlueprintCallable, Category = "Ability") 90 | AController* GetControllerFromActorInfo() const; 91 | 92 | UFUNCTION(BlueprintCallable, Category = "Ability") 93 | AModularCharacter* GetModularCharacterFromActorInfo() const; 94 | 95 | UFUNCTION(BlueprintCallable, Category = "Ability") 96 | UModularPawnComponent* GetHeroComponentFromActorInfo() const; 97 | 98 | EModularAbilityActivationPolicy GetActivationPolicy() const { return ActivationPolicy; } 99 | EModularAbilityActivationGroup GetActivationGroup() const { return ActivationGroup; } 100 | 101 | void TryActivateAbilityOnSpawn(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) const; 102 | 103 | // Returns true if the requested activation group is a valid transition. 104 | UFUNCTION(BlueprintCallable, BlueprintPure = false, Category = "Ability", Meta = (ExpandBoolAsExecs = "ReturnValue")) 105 | bool CanChangeActivationGroup(EModularAbilityActivationGroup NewGroup) const; 106 | 107 | // Tries to change the activation group. Returns true if it successfully changed. 108 | UFUNCTION(BlueprintCallable, BlueprintPure = false, Category = "Ability", Meta = (ExpandBoolAsExecs = "ReturnValue")) 109 | bool ChangeActivationGroup(EModularAbilityActivationGroup NewGroup); 110 | 111 | // Sets the ability's camera mode. 112 | UFUNCTION(BlueprintCallable, Category = "Ability") 113 | void SetCameraMode(TSubclassOf CameraMode); 114 | 115 | // Clears the ability's camera mode. Automatically called if needed when the ability ends. 116 | UFUNCTION(BlueprintCallable, Category = "Ability") 117 | void ClearCameraMode(); 118 | 119 | void OnAbilityFailedToActivate(const FGameplayTagContainer& FailedReason) const 120 | { 121 | NativeOnAbilityFailedToActivate(FailedReason); 122 | ScriptOnAbilityFailedToActivate(FailedReason); 123 | } 124 | 125 | protected: 126 | 127 | // Called when the ability fails to activate 128 | virtual void NativeOnAbilityFailedToActivate(const FGameplayTagContainer& FailedReason) const; 129 | 130 | // Called when the ability fails to activate 131 | UFUNCTION(BlueprintImplementableEvent) 132 | void ScriptOnAbilityFailedToActivate(const FGameplayTagContainer& FailedReason) const; 133 | 134 | //~UGameplayAbility interface 135 | virtual bool CanActivateAbility( 136 | const FGameplayAbilitySpecHandle Handle, 137 | const FGameplayAbilityActorInfo* ActorInfo, 138 | const FGameplayTagContainer* SourceTags, 139 | const FGameplayTagContainer* TargetTags, 140 | FGameplayTagContainer* OptionalRelevantTags) const override; 141 | virtual void SetCanBeCanceled(bool bCanBeCanceled) override; 142 | virtual void OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) override; 143 | virtual void OnRemoveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) override; 144 | virtual void ActivateAbility( 145 | const FGameplayAbilitySpecHandle Handle, 146 | const FGameplayAbilityActorInfo* ActorInfo, 147 | const FGameplayAbilityActivationInfo ActivationInfo, 148 | const FGameplayEventData* TriggerEventData) override; 149 | virtual void EndAbility( 150 | const FGameplayAbilitySpecHandle Handle, 151 | const FGameplayAbilityActorInfo* ActorInfo, 152 | const FGameplayAbilityActivationInfo ActivationInfo, 153 | bool bReplicateEndAbility, bool bWasCancelled) override; 154 | virtual bool CheckCost( 155 | const FGameplayAbilitySpecHandle Handle, 156 | const FGameplayAbilityActorInfo* ActorInfo, 157 | OUT FGameplayTagContainer* OptionalRelevantTags = nullptr) const override; 158 | virtual void ApplyCost( 159 | const FGameplayAbilitySpecHandle Handle, 160 | const FGameplayAbilityActorInfo* ActorInfo, 161 | const FGameplayAbilityActivationInfo ActivationInfo) const override; 162 | /** 163 | * @todo Lyra implements a GameplayEffectContext that adds an AbilitySourceObject for tracking. Do we need that? 164 | */ 165 | virtual FGameplayEffectContextHandle MakeEffectContext( 166 | const FGameplayAbilitySpecHandle Handle, 167 | const FGameplayAbilityActorInfo* ActorInfo) const override; 168 | virtual void ApplyAbilityTagsToGameplayEffectSpec(FGameplayEffectSpec& Spec, FGameplayAbilitySpec* AbilitySpec) const override; 169 | virtual bool DoesAbilitySatisfyTagRequirements( 170 | const UAbilitySystemComponent& AbilitySystemComponent, 171 | const FGameplayTagContainer* SourceTags = nullptr, 172 | const FGameplayTagContainer* TargetTags = nullptr, 173 | OUT FGameplayTagContainer* OptionalRelevantTags = nullptr) const override; 174 | //~End of UGameplayAbility interface 175 | 176 | virtual void OnPawnAvatarSet(); 177 | 178 | virtual void GetAbilitySource(FGameplayAbilitySpecHandle Handle, 179 | const FGameplayAbilityActorInfo* ActorInfo, 180 | float& OutSourceLevel, 181 | const IModularAbilitySourceInterface*& OutAbilitySource, 182 | AActor*& OutEffectCauser) const; 183 | 184 | /** Called when this ability is granted to the ability system component. */ 185 | UFUNCTION(BlueprintImplementableEvent, Category = Ability, DisplayName = "OnAbilityAdded") 186 | void K2_OnAbilityAdded(); 187 | 188 | /** Called when this ability is removed from the ability system component. */ 189 | UFUNCTION(BlueprintImplementableEvent, Category = Ability, DisplayName = "OnAbilityRemoved") 190 | void K2_OnAbilityRemoved(); 191 | 192 | /** Called when the ability system is initialized with a pawn avatar. */ 193 | UFUNCTION(BlueprintImplementableEvent, Category = Ability, DisplayName = "OnPawnAvatarSet") 194 | void K2_OnPawnAvatarSet(); 195 | 196 | protected: 197 | 198 | // Defines how this ability is meant to activate. 199 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability Activation") 200 | EModularAbilityActivationPolicy ActivationPolicy; 201 | 202 | // Defines the relationship between this ability activating and other abilities activating. 203 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Ability Activation") 204 | EModularAbilityActivationGroup ActivationGroup; 205 | 206 | // Additional costs that must be paid to activate this ability 207 | UPROPERTY(EditDefaultsOnly, Instanced, Category = Costs) 208 | TArray> AdditionalCosts; 209 | 210 | // Map of failure tags to simple error messages 211 | UPROPERTY(EditDefaultsOnly, Category = "Advanced") 212 | TMap FailureTagToUserFacingMessages; 213 | 214 | // Map of failure tags to anim montages that should be played with them 215 | UPROPERTY(EditDefaultsOnly, Category = "Advanced") 216 | TMap> FailureTagToAnimMontage; 217 | 218 | // If true, extra information should be logged when this ability is canceled. This is temporary, used for tracking a bug. 219 | UPROPERTY(EditDefaultsOnly, Category = "Advanced") 220 | bool bLogCancellation; 221 | 222 | // Current camera mode set by the ability. 223 | TSubclassOf ActiveCameraMode; 224 | }; 225 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/ActorComponent/ModularAbilityExtensionComponent.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #include "ActorComponent/ModularAbilityExtensionComponent.h" 4 | 5 | #include "AbilitySystemInterface.h" 6 | #include "ModularAbilityTags.h" 7 | #include "ModularGameplayAbilitiesLogChannels.h" 8 | #include "ModularGameplayTags.h" 9 | #include "ModularPlayerState.h" 10 | #include "Actor/ModularExperienceCharacter.h" 11 | #include "ActorComponent/ModularAbilitySystemComponent.h" 12 | #include "ActorComponent/ModularInputComponent.h" 13 | #include "ActorComponent/ModularInputConfigComponent.h" 14 | #include "Components/GameFrameworkComponentManager.h" 15 | #include "DataAsset/IAbilityPawnDataInterface.h" 16 | #include "Misc/UObjectToken.h" 17 | 18 | #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularAbilityExtensionComponent) 19 | 20 | const FName UModularAbilityExtensionComponent::NAME_ActorFeatureName("ModularAbilityExtension"); 21 | 22 | UModularAbilityExtensionComponent::UModularAbilityExtensionComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 23 | { 24 | PrimaryComponentTick.bStartWithTickEnabled = false; 25 | PrimaryComponentTick.bCanEverTick = false; 26 | 27 | SetIsReplicatedByDefault(true); 28 | 29 | AbilitySystemComponent = nullptr; 30 | } 31 | 32 | void UModularAbilityExtensionComponent::InitializeAbilitySystem(UModularAbilitySystemComponent* InASC, AActor* InOwnerActor) 33 | { 34 | check(InASC); 35 | check(InOwnerActor); 36 | 37 | if (AbilitySystemComponent == InASC) 38 | { 39 | // The ability system component hasn't changed. 40 | return; 41 | } 42 | 43 | if (AbilitySystemComponent) 44 | { 45 | // Clean up the old ability system component. 46 | UninitializeAbilitySystem(); 47 | } 48 | 49 | AModularExperienceCharacter* Pawn = GetPawnChecked(); 50 | const AActor* ExistingAvatar = InASC->GetAvatarActor(); 51 | 52 | UE_LOG(LogModularGameplayAbilities, Verbose, TEXT("Setting up ASC [%s] on pawn [%s] owner [%s], existing [%s] "), *GetNameSafe(InASC), *GetNameSafe(Pawn), *GetNameSafe(InOwnerActor), *GetNameSafe(ExistingAvatar)); 53 | 54 | if ((ExistingAvatar != nullptr) && (ExistingAvatar != Pawn)) 55 | { 56 | UE_LOG(LogModularGameplayAbilities, Log, TEXT("Existing avatar (authority=%d)"), ExistingAvatar->HasAuthority() ? 1 : 0); 57 | 58 | // There is already a pawn acting as the ASC's avatar, so we need to kick it out 59 | // This can happen on clients if they're lagged: their new pawn is spawned + possessed before the dead one is removed 60 | ensure(!ExistingAvatar->HasAuthority()); 61 | 62 | if (UModularAbilityExtensionComponent* OtherExtensionComponent = ExistingAvatar->FindComponentByClass()) 63 | { 64 | OtherExtensionComponent->UninitializeAbilitySystem(); 65 | } 66 | } 67 | 68 | AbilitySystemComponent = InASC; 69 | AbilitySystemComponent->InitAbilityActorInfo(InOwnerActor, Pawn); 70 | 71 | const UModularPawnComponent* ModularPawnComponent = Pawn->GetComponentByClass(); 72 | if (const IAbilityPawnDataInterface* PawnData = ModularPawnComponent->GetPawnData(); 73 | ensure(PawnData)) 74 | { 75 | InASC->SetTagRelationshipMapping(PawnData->GetTagRelationshipMapping()); 76 | } 77 | 78 | OnAbilitySystemInitialized.Broadcast(); 79 | } 80 | 81 | void UModularAbilityExtensionComponent::UninitializeAbilitySystem() 82 | { 83 | if (!AbilitySystemComponent) 84 | { 85 | return; 86 | } 87 | 88 | // Uninitialize the ASC if we're still the avatar actor (otherwise another pawn already did it when they became the avatar actor) 89 | if (AbilitySystemComponent->GetAvatarActor() == GetOwner()) 90 | { 91 | FGameplayTagContainer AbilityTypesToIgnore; 92 | AbilityTypesToIgnore.AddTag(ModularAbilityTags::Ability_Behavior_SurvivesDeath); 93 | 94 | AbilitySystemComponent->CancelAbilities(nullptr, &AbilityTypesToIgnore); 95 | AbilitySystemComponent->ClearAbilityInput(); 96 | AbilitySystemComponent->RemoveAllGameplayCues(); 97 | 98 | if (AbilitySystemComponent->GetOwnerActor() != nullptr) 99 | { 100 | AbilitySystemComponent->SetAvatarActor(nullptr); 101 | } 102 | else 103 | { 104 | // If the ASC doesn't have a valid owner, we need to clear *all* actor info, not just the avatar pairing 105 | AbilitySystemComponent->ClearActorInfo(); 106 | } 107 | 108 | OnAbilitySystemUninitialized.Broadcast(); 109 | } 110 | 111 | AbilitySystemComponent = nullptr; 112 | } 113 | 114 | void UModularAbilityExtensionComponent::HandleControllerChanged() 115 | { 116 | if (AbilitySystemComponent && (AbilitySystemComponent->GetAvatarActor() == GetPawnChecked())) 117 | { 118 | ensure(AbilitySystemComponent->AbilityActorInfo->OwnerActor == AbilitySystemComponent->GetOwnerActor()); 119 | if (AbilitySystemComponent->GetOwnerActor() == nullptr) 120 | { 121 | UninitializeAbilitySystem(); 122 | } 123 | else 124 | { 125 | AbilitySystemComponent->RefreshAbilityActorInfo(); 126 | } 127 | } 128 | 129 | CheckDefaultInitialization(); 130 | } 131 | 132 | void UModularAbilityExtensionComponent::InitializePlayerInput(UInputComponent* PlayerInputComponent) 133 | { 134 | const APawn* Pawn = GetPawn(); 135 | if (!Pawn) return; 136 | 137 | if (const APlayerController* PlayerController = GetController(); !PlayerController) return; 138 | 139 | InputActionMapping(PlayerInputComponent, Pawn); 140 | } 141 | 142 | void UModularAbilityExtensionComponent::InputActionMapping(UInputComponent* PlayerInputComponent, const APawn* Pawn) 143 | { 144 | const UModularPawnComponent* PawnExtComp = UModularPawnComponent::FindModularPawnComponent(Pawn); 145 | if (!PawnExtComp) return; 146 | const UModularPawnData* PawnData = PawnExtComp->GetPawnData(); 147 | if (!PawnData) return; 148 | const UModularInputConfig* InputConfig = PawnData->InputConfig; 149 | if (!InputConfig) return; 150 | if (UModularInputConfigComponent* InputConfigComponent = Cast(PlayerInputComponent); 151 | ensureMsgf(InputConfigComponent, TEXT("Unexpected Input Component class! The Gameplay Abilities will not be bound to their inputs. Change the input component to UInputConfigComponent or a subclass of it."))) 152 | { 153 | // This is where we actually bind and input action to a gameplay tag, which means that Gameplay Ability Blueprints will 154 | // be triggered directly by these input actions Triggered events. 155 | TArray BindHandles; 156 | InputConfigComponent->BindAbilityActions(InputConfig, this, &ThisClass::Input_AbilityInputTagPressed, &ThisClass::Input_AbilityInputTagReleased, /*out*/ BindHandles); 157 | } 158 | } 159 | 160 | void UModularAbilityExtensionComponent::Input_AbilityInputTagPressed(FGameplayTag InputTag) 161 | { 162 | if (const APawn* Pawn = GetPawn(); 163 | !Pawn) 164 | { 165 | return; 166 | } 167 | if (UModularAbilitySystemComponent* ModularASC = GetModularAbilitySystemComponent()) 168 | { 169 | ModularASC->AbilityInputTagPressed(InputTag); 170 | } 171 | } 172 | 173 | void UModularAbilityExtensionComponent::Input_AbilityInputTagReleased(FGameplayTag InputTag) 174 | { 175 | if (const APawn* Pawn = GetPawn(); 176 | !Pawn) 177 | { 178 | return; 179 | } 180 | if (UModularAbilitySystemComponent* ModularASC = GetModularAbilitySystemComponent()) 181 | { 182 | ModularASC->AbilityInputTagReleased(InputTag); 183 | } 184 | } 185 | 186 | /** 187 | * @ingroup IGameFrameworkInitStateInterface 188 | * @{ 189 | */ 190 | 191 | bool UModularAbilityExtensionComponent::CanChangeInitState(UGameFrameworkComponentManager* Manager, 192 | FGameplayTag CurrentState, FGameplayTag DesiredState) const 193 | { 194 | check(Manager); 195 | 196 | AModularExperienceCharacter* Pawn = GetPawnChecked(); 197 | if (!CurrentState.IsValid() 198 | && DesiredState == ModularGameplayTags::InitState_Spawned) 199 | { 200 | // As long as we are on a valid pawn, we count as spawned 201 | if (Pawn) 202 | { 203 | return true; 204 | } 205 | } 206 | if (CurrentState == ModularGameplayTags::InitState_Spawned 207 | && DesiredState == ModularGameplayTags::InitState_DataAvailable) 208 | { 209 | // The player state is required. 210 | if (!GetPlayerState()) 211 | { 212 | return false; 213 | } 214 | 215 | // If we're authority or autonomous, we need to wait for a controller with registered ownership of the player state. 216 | if (Pawn->GetLocalRole() != ROLE_SimulatedProxy) 217 | { 218 | AController* Controller = GetController(); 219 | 220 | const bool bHasControllerPairedWithPS = (Controller != nullptr) && \ 221 | (Controller->PlayerState != nullptr) && \ 222 | (Controller->PlayerState->GetOwner() == Controller); 223 | 224 | if (!bHasControllerPairedWithPS) 225 | { 226 | return false; 227 | } 228 | } 229 | 230 | const bool bIsLocallyControlled = Pawn->IsLocallyControlled(); 231 | const bool bIsBot = Pawn->IsBotControlled(); 232 | 233 | if (bIsLocallyControlled && !bIsBot) 234 | { 235 | AModularPlayerController* ModularPC = GetController(); 236 | 237 | // The input component and local player is required when locally controlled. 238 | if (!Pawn->InputComponent || !ModularPC || !ModularPC->GetLocalPlayer()) 239 | { 240 | return false; 241 | } 242 | } 243 | 244 | return true; 245 | } 246 | else if (CurrentState == ModularGameplayTags::InitState_DataAvailable 247 | && DesiredState == ModularGameplayTags::InitState_DataInitialized) 248 | { 249 | // Wait for player state ASC and extension component. 250 | const IAbilitySystemInterface* ModularPS = Cast(GetPlayerState()); 251 | 252 | return ModularPS->GetAbilitySystemComponent() && Manager->HasFeatureReachedInitState(Pawn, UModularPawnComponent::NAME_ActorFeatureName, ModularGameplayTags::InitState_DataInitialized); 253 | } 254 | else if (CurrentState == ModularGameplayTags::InitState_DataInitialized 255 | && DesiredState == ModularGameplayTags::InitState_GameplayReady) 256 | { 257 | // @todo add ability initialization checks? 258 | return true; 259 | } 260 | 261 | return false; 262 | } 263 | 264 | void UModularAbilityExtensionComponent::HandleChangeInitState(UGameFrameworkComponentManager* Manager, 265 | const FGameplayTag CurrentState, 266 | const FGameplayTag DesiredState) 267 | { 268 | if (CurrentState == ModularGameplayTags::InitState_DataAvailable 269 | && DesiredState == ModularGameplayTags::InitState_DataInitialized) 270 | { 271 | const APawn* Pawn = GetPawn(); 272 | AModularPlayerState* ModularPS = GetPlayerState(); 273 | const IAbilitySystemInterface* ModularAbilityPS = Cast(ModularPS); 274 | UAbilitySystemComponent* ModularASC = ModularAbilityPS->GetAbilitySystemComponent(); 275 | if (!ensure(Pawn && ModularPS && ModularASC)) 276 | { 277 | return; 278 | } 279 | // The player state holds the persistent data for this player (state that persists across deaths and multiple pawns). 280 | // The ability system component and attribute sets live on the player state. 281 | InitializeAbilitySystem(Cast(ModularASC), ModularPS); 282 | if (Pawn->InputComponent != nullptr) 283 | { 284 | InitializePlayerInput(Pawn->InputComponent); 285 | } 286 | } 287 | } 288 | 289 | void UModularAbilityExtensionComponent::OnActorInitStateChanged(const FActorInitStateChangedParams& Params) 290 | { 291 | if (Params.FeatureName == UModularPawnComponent::NAME_ActorFeatureName) 292 | { 293 | if (Params.FeatureState == ModularGameplayTags::InitState_DataInitialized) 294 | { 295 | // If the extension component says all all other components are initialized, try to progress to next state. 296 | CheckDefaultInitialization(); 297 | } 298 | } 299 | } 300 | 301 | void UModularAbilityExtensionComponent::CheckDefaultInitialization() 302 | { 303 | static const TArray StateChain = 304 | { 305 | ModularGameplayTags::InitState_Spawned, 306 | ModularGameplayTags::InitState_DataAvailable, 307 | ModularGameplayTags::InitState_DataInitialized, 308 | ModularGameplayTags::InitState_GameplayReady 309 | }; 310 | 311 | // This will try to progress from spawned (which is only set in BeginPlay) through the data initialization stages 312 | // until it gets to gameplay ready. 313 | ContinueInitStateChain(StateChain); 314 | } 315 | 316 | /** 317 | * @} 318 | */ 319 | 320 | void UModularAbilityExtensionComponent::OnRegister() 321 | { 322 | Super::OnRegister(); 323 | 324 | if (!GetPawn()) 325 | { 326 | UE_LOG(LogModularGameplayAbilities, Error, 327 | TEXT("[UModularAbilityExtensionComponent::OnRegister] This component has been added to a blueprint whose base class is not a Pawn. To use this component, it MUST be placed on a Pawn Blueprint.")); 328 | 329 | #if WITH_EDITOR 330 | if (GIsEditor) 331 | { 332 | static const FText Message = NSLOCTEXT("ModularAbilityExtensionComponent", "NotOnPawnError", "has been added to a blueprint whose base class is not a Pawn. To use this component, it MUST be placed on a Pawn Blueprint. This will cause a crash if you PIE!"); 333 | static const FName MessageLogName = TEXT("ModularAbilityExtensionComponent"); 334 | 335 | FMessageLog(MessageLogName).Error() 336 | ->AddToken(FUObjectToken::Create(this, FText::FromString(GetNameSafe(this)))) 337 | ->AddToken(FTextToken::Create(Message)); 338 | 339 | FMessageLog(MessageLogName).Open(); 340 | } 341 | #endif 342 | } 343 | else 344 | { 345 | // Register with the init state system early, this will only work if this is a game world. 346 | RegisterInitStateFeature(); 347 | } 348 | } 349 | 350 | void UModularAbilityExtensionComponent::BeginPlay() 351 | { 352 | Super::BeginPlay(); 353 | 354 | // Listen for when the pawn extension component changes init state 355 | BindOnActorInitStateChanged(UModularInputComponent::NAME_ActorFeatureName, FGameplayTag(), false); 356 | 357 | // Notifies that we are done spawning, then try the rest of initialization 358 | ensure(TryToChangeInitState(ModularGameplayTags::InitState_Spawned)); 359 | CheckDefaultInitialization(); 360 | } 361 | 362 | void UModularAbilityExtensionComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) 363 | { 364 | UnregisterInitStateFeature(); 365 | 366 | Super::EndPlay(EndPlayReason); 367 | } 368 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/GameplayAbilities/ModularGameplayAbility.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Chronicler. 2 | 3 | #include "GameplayAbilities/ModularGameplayAbility.h" 4 | 5 | #include "AbilitySystemBlueprintLibrary.h" 6 | #include "AbilitySystemGlobals.h" 7 | #include "AbilitySystemLog.h" 8 | #include "ModalCameraComponent.h" 9 | #include "ModularAbilitySimpleFailureMessage.h" 10 | #include "ModularAbilityTags.h" 11 | #include "ModularGameplayAbilitiesLogChannels.h" 12 | #include "Actor/ModularExperienceCharacter.h" 13 | #include "ActorComponent/ModularAbilitySystemComponent.h" 14 | #include "GameFramework/GameplayMessageSubsystem.h" 15 | #include "GameplayAbilities/ModularAbilityCost.h" 16 | 17 | #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularGameplayAbility) 18 | 19 | #define ENSURE_ABILITY_IS_INSTANTIATED_OR_RETURN(FunctionName, ReturnValue) \ 20 | { \ 21 | if (!ensure(IsInstantiated())) \ 22 | { \ 23 | ABILITY_LOG(Error, TEXT("%s: " #FunctionName " cannot be called on a non-instanced ability. Check the instancing policy."), *GetPathName()); \ 24 | return ReturnValue; \ 25 | } \ 26 | } 27 | 28 | UE_DEFINE_GAMEPLAY_TAG(TAG_ABILITY_SIMPLE_FAILURE_MESSAGE, "Ability.UserFacingSimpleActivateFail.Message"); 29 | UE_DEFINE_GAMEPLAY_TAG(TAG_ABILITY_PLAY_MONTAGE_FAILURE_MESSAGE, "Ability.PlayMontageOnActivateFail.Message"); 30 | 31 | UModularGameplayAbility::UModularGameplayAbility(const FObjectInitializer& ObjectInitializer) 32 | : Super(ObjectInitializer) 33 | { 34 | ReplicationPolicy = EGameplayAbilityReplicationPolicy::ReplicateNo; 35 | InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor; 36 | NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted; 37 | NetSecurityPolicy = EGameplayAbilityNetSecurityPolicy::ClientOrServer; 38 | 39 | ActivationPolicy = EModularAbilityActivationPolicy::OnInputTriggered; 40 | ActivationGroup = EModularAbilityActivationGroup::Independent; 41 | 42 | bLogCancellation = false; 43 | 44 | ActiveCameraMode = nullptr; 45 | } 46 | 47 | UModularAbilitySystemComponent* UModularGameplayAbility::GetModularAbilitySystemComponentFromActorInfo() const 48 | { 49 | return (CurrentActorInfo ? Cast(CurrentActorInfo->AbilitySystemComponent.Get()) : nullptr); 50 | } 51 | 52 | AModularPlayerController* UModularGameplayAbility::GetModularPlayerControllerFromActorInfo() const 53 | { 54 | return (CurrentActorInfo ? Cast(CurrentActorInfo->PlayerController.Get()) : nullptr); 55 | } 56 | 57 | AController* UModularGameplayAbility::GetControllerFromActorInfo() const 58 | { 59 | if (CurrentActorInfo) 60 | { 61 | if (AController* PC = CurrentActorInfo->PlayerController.Get()) 62 | { 63 | return PC; 64 | } 65 | 66 | // Look for a player controller or pawn in the owner chain. 67 | AActor* TestActor = CurrentActorInfo->OwnerActor.Get(); 68 | while (TestActor) 69 | { 70 | if (AController* C = Cast(TestActor)) 71 | { 72 | return C; 73 | } 74 | 75 | if (APawn* Pawn = Cast(TestActor)) 76 | { 77 | return Pawn->GetController(); 78 | } 79 | 80 | TestActor = TestActor->GetOwner(); 81 | } 82 | } 83 | 84 | return nullptr; 85 | } 86 | 87 | AModularCharacter* UModularGameplayAbility::GetModularCharacterFromActorInfo() const 88 | { 89 | return (CurrentActorInfo ? Cast(CurrentActorInfo->AvatarActor.Get()) : nullptr); 90 | } 91 | 92 | UModularPawnComponent* UModularGameplayAbility::GetHeroComponentFromActorInfo() const 93 | { 94 | return (CurrentActorInfo ? UModularPawnComponent::FindModularPawnComponent(CurrentActorInfo->AvatarActor.Get()) : nullptr); 95 | } 96 | 97 | void UModularGameplayAbility::NativeOnAbilityFailedToActivate(const FGameplayTagContainer& FailedReason) const 98 | { 99 | bool bSimpleFailureFound = false; 100 | for (FGameplayTag Reason : FailedReason) 101 | { 102 | if (!bSimpleFailureFound) 103 | { 104 | if (const FText* pUserFacingMessage = FailureTagToUserFacingMessages.Find(Reason)) 105 | { 106 | FModularAbilitySimpleFailureMessage Message; 107 | Message.PlayerController = GetActorInfo().PlayerController.Get(); 108 | Message.FailureTags = FailedReason; 109 | Message.UserFacingReason = *pUserFacingMessage; 110 | 111 | UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(GetWorld()); 112 | MessageSystem.BroadcastMessage(TAG_ABILITY_SIMPLE_FAILURE_MESSAGE, Message); 113 | bSimpleFailureFound = true; 114 | } 115 | } 116 | 117 | if (UAnimMontage* pMontage = FailureTagToAnimMontage.FindRef(Reason)) 118 | { 119 | FModularAbilityMontageFailureMessage Message; 120 | Message.PlayerController = GetActorInfo().PlayerController.Get(); 121 | Message.FailureTags = FailedReason; 122 | Message.FailureMontage = pMontage; 123 | 124 | UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(GetWorld()); 125 | MessageSystem.BroadcastMessage(TAG_ABILITY_PLAY_MONTAGE_FAILURE_MESSAGE, Message); 126 | } 127 | } 128 | } 129 | 130 | bool UModularGameplayAbility::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, FGameplayTagContainer* OptionalRelevantTags) const 131 | { 132 | if (!ActorInfo || !ActorInfo->AbilitySystemComponent.IsValid()) 133 | { 134 | return false; 135 | } 136 | 137 | if (!Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags)) 138 | { 139 | return false; 140 | } 141 | 142 | //@TODO Possibly remove after setting up tag relationships 143 | if (const UModularAbilitySystemComponent* AbilityComponent = CastChecked(ActorInfo->AbilitySystemComponent.Get()); 144 | AbilityComponent->IsActivationGroupBlocked(ActivationGroup)) 145 | { 146 | if (OptionalRelevantTags) 147 | { 148 | OptionalRelevantTags->AddTag(ModularAbilityTags::Ability_ActivateFail_ActivationGroup); 149 | } 150 | return false; 151 | } 152 | 153 | return true; 154 | } 155 | 156 | void UModularGameplayAbility::SetCanBeCanceled(bool bCanBeCanceled) 157 | { 158 | // The ability can not block canceling if it's replaceable. 159 | if (!bCanBeCanceled && (ActivationGroup == EModularAbilityActivationGroup::Exclusive_Replaceable)) 160 | { 161 | UE_LOG(LogModularGameplayAbilities, Error, TEXT("SetCanBeCanceled: Ability [%s] can not block canceling because its activation group is replaceable."), *GetName()); 162 | return; 163 | } 164 | 165 | Super::SetCanBeCanceled(bCanBeCanceled); 166 | } 167 | 168 | void UModularGameplayAbility::OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) 169 | { 170 | Super::OnGiveAbility(ActorInfo, Spec); 171 | 172 | K2_OnAbilityAdded(); 173 | 174 | TryActivateAbilityOnSpawn(ActorInfo, Spec); 175 | } 176 | 177 | void UModularGameplayAbility::OnRemoveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) 178 | { 179 | K2_OnAbilityRemoved(); 180 | 181 | Super::OnRemoveAbility(ActorInfo, Spec); 182 | } 183 | 184 | void UModularGameplayAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) 185 | { 186 | Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData); 187 | } 188 | 189 | void UModularGameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) 190 | { 191 | ClearCameraMode(); 192 | 193 | Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); 194 | } 195 | 196 | bool UModularGameplayAbility::CheckCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, OUT FGameplayTagContainer* OptionalRelevantTags) const 197 | { 198 | if (!Super::CheckCost(Handle, ActorInfo, OptionalRelevantTags) || !ActorInfo) 199 | { 200 | return false; 201 | } 202 | 203 | // Verify we can afford any additional costs 204 | for (TObjectPtr AdditionalCost : AdditionalCosts) 205 | { 206 | if (AdditionalCost != nullptr) 207 | { 208 | if (!AdditionalCost->CheckCost(this, Handle, ActorInfo, /*inout*/ OptionalRelevantTags)) 209 | { 210 | return false; 211 | } 212 | } 213 | } 214 | 215 | return true; 216 | } 217 | 218 | void UModularGameplayAbility::ApplyCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const 219 | { 220 | Super::ApplyCost(Handle, ActorInfo, ActivationInfo); 221 | 222 | check(ActorInfo); 223 | 224 | // Used to determine if the ability actually hit a target (as some costs are only spent on successful attempts) 225 | auto DetermineIfAbilityHitTarget = [&]() 226 | { 227 | if (ActorInfo->IsNetAuthority()) 228 | { 229 | if (UModularAbilitySystemComponent* ASC = Cast(ActorInfo->AbilitySystemComponent.Get())) 230 | { 231 | FGameplayAbilityTargetDataHandle TargetData; 232 | ASC->GetAbilityTargetData(Handle, ActivationInfo, TargetData); 233 | for (int32 TargetDataIdx = 0; TargetDataIdx < TargetData.Data.Num(); ++TargetDataIdx) 234 | { 235 | if (UAbilitySystemBlueprintLibrary::TargetDataHasHitResult(TargetData, TargetDataIdx)) 236 | { 237 | return true; 238 | } 239 | } 240 | } 241 | } 242 | 243 | return false; 244 | }; 245 | 246 | // Pay any additional costs 247 | bool bAbilityHitTarget = false; 248 | bool bHasDeterminedIfAbilityHitTarget = false; 249 | for (TObjectPtr AdditionalCost : AdditionalCosts) 250 | { 251 | if (AdditionalCost != nullptr) 252 | { 253 | if (AdditionalCost->ShouldOnlyApplyCostOnHit()) 254 | { 255 | if (!bHasDeterminedIfAbilityHitTarget) 256 | { 257 | bAbilityHitTarget = DetermineIfAbilityHitTarget(); 258 | bHasDeterminedIfAbilityHitTarget = true; 259 | } 260 | 261 | if (!bAbilityHitTarget) 262 | { 263 | continue; 264 | } 265 | } 266 | 267 | AdditionalCost->ApplyCost(this, Handle, ActorInfo, ActivationInfo); 268 | } 269 | } 270 | } 271 | 272 | FGameplayEffectContextHandle UModularGameplayAbility::MakeEffectContext(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo) const 273 | { 274 | FGameplayEffectContextHandle ContextHandle = Super::MakeEffectContext(Handle, ActorInfo); 275 | 276 | FGameplayEffectContext* EffectContext = nullptr; 277 | if (FGameplayEffectContext* BaseEffectContext = ContextHandle.Get(); 278 | (BaseEffectContext != nullptr) 279 | && BaseEffectContext->GetScriptStruct()->IsChildOf(FGameplayEffectContext::StaticStruct())) 280 | { 281 | EffectContext = BaseEffectContext; 282 | } 283 | check(EffectContext); 284 | 285 | check(ActorInfo); 286 | 287 | AActor* EffectCauser = nullptr; 288 | const IModularAbilitySourceInterface* AbilitySource = nullptr; 289 | float SourceLevel = 0.0f; 290 | GetAbilitySource(Handle, ActorInfo, /*out*/ SourceLevel, /*out*/ AbilitySource, /*out*/ EffectCauser); 291 | 292 | const UObject* SourceObject = GetSourceObject(Handle, ActorInfo); 293 | 294 | AActor* Instigator = ActorInfo ? ActorInfo->OwnerActor.Get() : nullptr; 295 | 296 | TWeakObjectPtrAbilitySourceObject = MakeWeakObjectPtr(Cast(AbilitySource)); 297 | EffectContext->AddInstigator(Instigator, EffectCauser); 298 | EffectContext->AddSourceObject(SourceObject); 299 | 300 | return ContextHandle; 301 | } 302 | 303 | void UModularGameplayAbility::ApplyAbilityTagsToGameplayEffectSpec(FGameplayEffectSpec& Spec, FGameplayAbilitySpec* AbilitySpec) const 304 | { 305 | Super::ApplyAbilityTagsToGameplayEffectSpec(Spec, AbilitySpec); 306 | 307 | // @todo Lyra applies ability tags based on the gameplay tags of the hit material, do we need that? 308 | } 309 | 310 | bool UModularGameplayAbility::DoesAbilitySatisfyTagRequirements(const UAbilitySystemComponent& AbilitySystemComponent, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, OUT FGameplayTagContainer* OptionalRelevantTags) const 311 | { 312 | // Specialized version to handle death exclusion and AbilityTags expansion via ASC 313 | 314 | bool bBlocked = false; 315 | bool bMissing = false; 316 | 317 | const UAbilitySystemGlobals& AbilitySystemGlobals = UAbilitySystemGlobals::Get(); 318 | const FGameplayTag& BlockedTag = AbilitySystemGlobals.ActivateFailTagsBlockedTag; 319 | const FGameplayTag& MissingTag = AbilitySystemGlobals.ActivateFailTagsMissingTag; 320 | 321 | // Check if any of this ability's tags are currently blocked 322 | if (AbilitySystemComponent.AreAbilityTagsBlocked(GetAssetTags())) 323 | { 324 | bBlocked = true; 325 | } 326 | 327 | const UModularAbilitySystemComponent* AbilityComponent = Cast(&AbilitySystemComponent); 328 | static FGameplayTagContainer AllRequiredTags; 329 | static FGameplayTagContainer AllBlockedTags; 330 | 331 | AllRequiredTags = ActivationRequiredTags; 332 | AllBlockedTags = ActivationBlockedTags; 333 | 334 | // Expand our ability tags to add additional required/blocked tags 335 | if (AbilityComponent) 336 | { 337 | AbilityComponent->GetAdditionalActivationTagRequirements(GetAssetTags(), AllRequiredTags, AllBlockedTags); 338 | } 339 | 340 | // Check to see the required/blocked tags for this ability 341 | if (AllBlockedTags.Num() || AllRequiredTags.Num()) 342 | { 343 | static FGameplayTagContainer AbilitySystemComponentTags; 344 | 345 | AbilitySystemComponentTags.Reset(); 346 | AbilitySystemComponent.GetOwnedGameplayTags(AbilitySystemComponentTags); 347 | 348 | if (AbilitySystemComponentTags.HasAny(AllBlockedTags)) 349 | { 350 | if (OptionalRelevantTags && AbilitySystemComponentTags.HasTag(ModularAbilityTags::Status_Death)) 351 | { 352 | // If player is dead and was rejected due to blocking tags, give that feedback 353 | OptionalRelevantTags->AddTag(ModularAbilityTags::Ability_ActivateFail_IsDead); 354 | } 355 | 356 | bBlocked = true; 357 | } 358 | 359 | if (!AbilitySystemComponentTags.HasAll(AllRequiredTags)) 360 | { 361 | bMissing = true; 362 | } 363 | } 364 | 365 | if (SourceTags != nullptr) 366 | { 367 | if (SourceBlockedTags.Num() || SourceRequiredTags.Num()) 368 | { 369 | if (SourceTags->HasAny(SourceBlockedTags)) 370 | { 371 | bBlocked = true; 372 | } 373 | 374 | if (!SourceTags->HasAll(SourceRequiredTags)) 375 | { 376 | bMissing = true; 377 | } 378 | } 379 | } 380 | 381 | if (TargetTags != nullptr) 382 | { 383 | if (TargetBlockedTags.Num() || TargetRequiredTags.Num()) 384 | { 385 | if (TargetTags->HasAny(TargetBlockedTags)) 386 | { 387 | bBlocked = true; 388 | } 389 | 390 | if (!TargetTags->HasAll(TargetRequiredTags)) 391 | { 392 | bMissing = true; 393 | } 394 | } 395 | } 396 | 397 | if (bBlocked) 398 | { 399 | if (OptionalRelevantTags && BlockedTag.IsValid()) 400 | { 401 | OptionalRelevantTags->AddTag(BlockedTag); 402 | } 403 | return false; 404 | } 405 | if (bMissing) 406 | { 407 | if (OptionalRelevantTags && MissingTag.IsValid()) 408 | { 409 | OptionalRelevantTags->AddTag(MissingTag); 410 | } 411 | return false; 412 | } 413 | 414 | return true; 415 | } 416 | 417 | void UModularGameplayAbility::OnPawnAvatarSet() 418 | { 419 | K2_OnPawnAvatarSet(); 420 | } 421 | 422 | void UModularGameplayAbility::GetAbilitySource(FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, float& OutSourceLevel, const IModularAbilitySourceInterface*& OutAbilitySource, AActor*& OutEffectCauser) const 423 | { 424 | OutSourceLevel = 0.0f; 425 | OutAbilitySource = nullptr; 426 | OutEffectCauser = nullptr; 427 | 428 | OutEffectCauser = ActorInfo->AvatarActor.Get(); 429 | 430 | // If we were added by something that's an ability info source, use it 431 | UObject* SourceObject = GetSourceObject(Handle, ActorInfo); 432 | 433 | OutAbilitySource = Cast(SourceObject); 434 | } 435 | 436 | void UModularGameplayAbility::TryActivateAbilityOnSpawn(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) const 437 | { 438 | const bool bIsPredicting = (GetCurrentActivationInfo().ActivationMode == EGameplayAbilityActivationMode::Predicting); 439 | 440 | // Try to activate if activation policy is on spawn. 441 | if (ActorInfo && !Spec.IsActive() && !bIsPredicting && (ActivationPolicy == EModularAbilityActivationPolicy::OnSpawn)) 442 | { 443 | UAbilitySystemComponent* ASC = ActorInfo->AbilitySystemComponent.Get(); 444 | const AActor* AvatarActor = ActorInfo->AvatarActor.Get(); 445 | 446 | // If avatar actor is torn off or about to die, don't try to activate until we get the new one. 447 | if (ASC && AvatarActor && !AvatarActor->GetTearOff() && (AvatarActor->GetLifeSpan() <= 0.0f)) 448 | { 449 | const bool bIsLocalExecution = (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::LocalPredicted) || (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::LocalOnly); 450 | const bool bIsServerExecution = (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::ServerOnly) || (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::ServerInitiated); 451 | 452 | const bool bClientShouldActivate = ActorInfo->IsLocallyControlled() && bIsLocalExecution; 453 | const bool bServerShouldActivate = ActorInfo->IsNetAuthority() && bIsServerExecution; 454 | 455 | if (bClientShouldActivate || bServerShouldActivate) 456 | { 457 | ASC->TryActivateAbility(Spec.Handle); 458 | } 459 | } 460 | } 461 | } 462 | 463 | bool UModularGameplayAbility::CanChangeActivationGroup(EModularAbilityActivationGroup NewGroup) const 464 | { 465 | if (!IsInstantiated() || !IsActive()) 466 | { 467 | return false; 468 | } 469 | 470 | if (ActivationGroup == NewGroup) 471 | { 472 | return true; 473 | } 474 | 475 | UModularAbilitySystemComponent* AbilityComponent = GetModularAbilitySystemComponentFromActorInfo(); 476 | check(AbilityComponent); 477 | 478 | if ((ActivationGroup != EModularAbilityActivationGroup::Exclusive_Blocking) 479 | && AbilityComponent->IsActivationGroupBlocked(NewGroup)) 480 | { 481 | // This ability can't change groups if it's blocked (unless it is the one doing the blocking). 482 | return false; 483 | } 484 | 485 | if ((NewGroup == EModularAbilityActivationGroup::Exclusive_Replaceable) && !CanBeCanceled()) 486 | { 487 | // This ability can't become replaceable if it can't be canceled. 488 | return false; 489 | } 490 | 491 | return true; 492 | } 493 | 494 | bool UModularGameplayAbility::ChangeActivationGroup(EModularAbilityActivationGroup NewGroup) 495 | { 496 | ENSURE_ABILITY_IS_INSTANTIATED_OR_RETURN(ChangeActivationGroup, false); 497 | 498 | if (!CanChangeActivationGroup(NewGroup)) 499 | { 500 | return false; 501 | } 502 | 503 | if (ActivationGroup != NewGroup) 504 | { 505 | UModularAbilitySystemComponent* AbilityComponent = GetModularAbilitySystemComponentFromActorInfo(); 506 | check(AbilityComponent); 507 | 508 | AbilityComponent->RemoveAbilityFromActivationGroup(ActivationGroup, this); 509 | AbilityComponent->AddAbilityToActivationGroup(NewGroup, this); 510 | 511 | ActivationGroup = NewGroup; 512 | } 513 | 514 | return true; 515 | } 516 | 517 | void UModularGameplayAbility::SetCameraMode(TSubclassOf CameraMode) 518 | { 519 | ENSURE_ABILITY_IS_INSTANTIATED_OR_RETURN(SetCameraMode, ); 520 | 521 | const AModularCharacter* ModularCharacter = GetModularCharacterFromActorInfo(); 522 | if (UModalCameraComponent* PawnComponent = ModularCharacter->FindComponentByClass()) 523 | { 524 | PawnComponent->SetAbilityCameraMode(CameraMode, CurrentSpecHandle); 525 | ActiveCameraMode = CameraMode; 526 | } 527 | } 528 | 529 | void UModularGameplayAbility::ClearCameraMode() 530 | { 531 | ENSURE_ABILITY_IS_INSTANTIATED_OR_RETURN(ClearCameraMode, ); 532 | 533 | if (ActiveCameraMode) 534 | { 535 | const AModularCharacter* ModularCharacter = GetModularCharacterFromActorInfo(); 536 | if (UModalCameraComponent* PawnComponent = ModularCharacter->FindComponentByClass()) 537 | { 538 | PawnComponent->ClearAbilityCameraMode(CurrentSpecHandle); 539 | } 540 | 541 | ActiveCameraMode = nullptr; 542 | } 543 | } 544 | -------------------------------------------------------------------------------- /Source/ModularGameplayAbilities/Private/ActorComponent/ModularAbilitySystemComponent.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "ActorComponent/ModularAbilitySystemComponent.h" 4 | 5 | #include "ModularGameplayAbilitiesLogChannels.h" 6 | #include "Animation/GameplayTagsAnimInstance.h" 7 | #include "DataAsset/ModularAbilityData.h" 8 | #include "DataAsset/ModularAssetManager.h" 9 | #include "GameplayAbilities/ModularGameplayAbility.h" 10 | #include "Engine/World.h" 11 | #include "GameFramework/Pawn.h" 12 | #include "GameplayAbilities/ModularGlobalAbilitySystem.h" 13 | 14 | #include UE_INLINE_GENERATED_CPP_BY_NAME(ModularAbilitySystemComponent) 15 | 16 | UE_DEFINE_GAMEPLAY_TAG(TAG_Gameplay_AbilityInputBlocked, "Gameplay.AbilityInputBlocked"); 17 | 18 | UModularAbilitySystemComponent::UModularAbilitySystemComponent(const FObjectInitializer& ObjectInitializer) 19 | : Super(ObjectInitializer) 20 | { 21 | InputPressedSpecHandles.Reset(); 22 | InputReleasedSpecHandles.Reset(); 23 | InputHeldSpecHandles.Reset(); 24 | 25 | FMemory::Memset(ActivationGroupCounts, 0, sizeof(ActivationGroupCounts)); 26 | } 27 | 28 | void UModularAbilitySystemComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) 29 | { 30 | if (UModularGlobalAbilitySystem* GlobalAbilitySystem = UWorld::GetSubsystem(GetWorld())) 31 | { 32 | GlobalAbilitySystem->UnregisterAbilityComponent(this); 33 | } 34 | 35 | Super::EndPlay(EndPlayReason); 36 | } 37 | 38 | void UModularAbilitySystemComponent::InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor) 39 | { 40 | const FGameplayAbilityActorInfo* ActorInfo = AbilityActorInfo.Get(); 41 | check(ActorInfo); 42 | check(InOwnerActor); 43 | 44 | const bool bHasNewPawnAvatar = Cast(InAvatarActor) && (InAvatarActor != ActorInfo->AvatarActor); 45 | 46 | Super::InitAbilityActorInfo(InOwnerActor, InAvatarActor); 47 | 48 | if (bHasNewPawnAvatar) 49 | { 50 | // Notify all abilities that a new pawn avatar has been set 51 | for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items) 52 | { 53 | if (UModularGameplayAbility* ModularAbilityCDO = CastChecked(AbilitySpec.Ability); 54 | ModularAbilityCDO->GetInstancingPolicy() != EGameplayAbilityInstancingPolicy::InstancedPerActor) 55 | { 56 | for (TArray Instances = AbilitySpec.GetAbilityInstances(); 57 | UGameplayAbility* AbilityInstance : Instances) 58 | { 59 | if (UModularGameplayAbility* ModularAbilityInstance = Cast(AbilityInstance)) 60 | { 61 | // Ability instances may be missing for replays. 62 | ModularAbilityInstance->OnPawnAvatarSet(); 63 | } 64 | } 65 | } 66 | else 67 | { 68 | ModularAbilityCDO->OnPawnAvatarSet(); 69 | } 70 | } 71 | 72 | // Register with the global system once we actually have a pawn avatar. 73 | // We wait until this time since some globally-applied effects may require an avatar. 74 | if (UModularGlobalAbilitySystem* GlobalAbilitySystem = UWorld::GetSubsystem(GetWorld())) 75 | { 76 | GlobalAbilitySystem->RegisterAbilityComponent(this); 77 | } 78 | 79 | if (UGameplayTagsAnimInstance* ModularAnimInst = Cast(ActorInfo->GetAnimInstance())) 80 | { 81 | ModularAnimInst->InitializeWithAbilitySystem(this); 82 | } 83 | 84 | TryActivateAbilitiesOnSpawn(); 85 | } 86 | } 87 | 88 | void UModularAbilitySystemComponent::TryActivateAbilitiesOnSpawn() 89 | { 90 | ABILITYLIST_SCOPE_LOCK(); 91 | for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items) 92 | { 93 | for (TArray Instances = AbilitySpec.GetAbilityInstances(); 94 | UGameplayAbility* AbilityInstance : Instances) 95 | { 96 | if (UModularGameplayAbility* ModularAbilityInstance = Cast(AbilityInstance)) 97 | { 98 | // Ability instances may be missing for replays. 99 | ModularAbilityInstance->OnPawnAvatarSet(); 100 | ModularAbilityInstance->TryActivateAbilityOnSpawn(AbilityActorInfo.Get(), AbilitySpec); 101 | } 102 | } 103 | } 104 | } 105 | 106 | void UModularAbilitySystemComponent::CancelAbilitiesByFunc(TShouldCancelAbilityFunc ShouldCancelFunc, bool bReplicateCancelAbility) 107 | { 108 | ABILITYLIST_SCOPE_LOCK(); 109 | for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items) 110 | { 111 | if (!AbilitySpec.IsActive()) 112 | { 113 | continue; 114 | } 115 | 116 | if (UModularGameplayAbility* ModularAbilityCDO = CastChecked(AbilitySpec.Ability); 117 | ModularAbilityCDO->GetInstancingPolicy() != EGameplayAbilityInstancingPolicy::InstancedPerActor) 118 | { 119 | // Cancel all the spawned instances, not the CDO. 120 | for (TArray Instances = AbilitySpec.GetAbilityInstances(); 121 | UGameplayAbility* AbilityInstance : Instances) 122 | { 123 | if (UModularGameplayAbility* ModularAbilityInstance = CastChecked(AbilityInstance); 124 | ShouldCancelFunc(ModularAbilityInstance, AbilitySpec.Handle)) 125 | { 126 | if (ModularAbilityInstance->CanBeCanceled()) 127 | { 128 | ModularAbilityInstance->CancelAbility(AbilitySpec.Handle, AbilityActorInfo.Get(), ModularAbilityInstance->GetCurrentActivationInfo(), bReplicateCancelAbility); 129 | } 130 | else 131 | { 132 | UE_LOG(LogModularGameplayAbilities, Error, TEXT("CancelAbilitiesByFunc: Can't cancel ability [%s] because CanBeCanceled is false."), *ModularAbilityInstance->GetName()); 133 | } 134 | } 135 | } 136 | } 137 | else 138 | { 139 | // Cancel the non-instanced ability CDO. 140 | if (ShouldCancelFunc(ModularAbilityCDO, AbilitySpec.Handle)) 141 | { 142 | // Non-instanced abilities can always be canceled. 143 | check(ModularAbilityCDO->CanBeCanceled()); 144 | ModularAbilityCDO->CancelAbility(AbilitySpec.Handle, AbilityActorInfo.Get(), FGameplayAbilityActivationInfo(), bReplicateCancelAbility); 145 | } 146 | } 147 | } 148 | } 149 | 150 | void UModularAbilitySystemComponent::CancelInputActivatedAbilities(bool bReplicateCancelAbility) 151 | { 152 | auto ShouldCancelFunc = [this](const UModularGameplayAbility* ModularAbility, FGameplayAbilitySpecHandle Handle) 153 | { 154 | const EModularAbilityActivationPolicy ActivationPolicy = ModularAbility->GetActivationPolicy(); 155 | return ((ActivationPolicy == EModularAbilityActivationPolicy::OnInputTriggered) 156 | || (ActivationPolicy == EModularAbilityActivationPolicy::WhileInputActive)); 157 | }; 158 | 159 | CancelAbilitiesByFunc(ShouldCancelFunc, bReplicateCancelAbility); 160 | } 161 | 162 | void UModularAbilitySystemComponent::AbilitySpecInputPressed(FGameplayAbilitySpec& Spec) 163 | { 164 | Super::AbilitySpecInputPressed(Spec); 165 | 166 | // We don't support UGameplayAbility::bReplicateInputDirectly. 167 | // Use replicated events instead so that the WaitInputPress ability task works. 168 | if (Spec.IsActive()) 169 | { 170 | for (UGameplayAbility* AbilityInstance : Spec.GetAbilityInstances()) 171 | { 172 | // Access activation info from the instance 173 | if (UModularGameplayAbility* ModularAbilityInstance = Cast(AbilityInstance)) 174 | { 175 | // Invoke the InputPressed event. This is not replicated here. 176 | // If someone is listening, they may replicate the InputPressed event to the server. 177 | InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, Spec.Handle, ModularAbilityInstance->GetCurrentActivationInfo().GetActivationPredictionKey()); 178 | } 179 | } 180 | } 181 | } 182 | 183 | void UModularAbilitySystemComponent::AbilitySpecInputReleased(FGameplayAbilitySpec& Spec) 184 | { 185 | Super::AbilitySpecInputReleased(Spec); 186 | 187 | // We don't support UGameplayAbility::bReplicateInputDirectly. 188 | // Use replicated events instead so that the WaitInputRelease ability task works. 189 | if (Spec.IsActive()) 190 | { 191 | for (UGameplayAbility* AbilityInstance : Spec.GetAbilityInstances()) 192 | { 193 | // Access activation info from the instance 194 | if (UModularGameplayAbility* ModularAbilityInstance = Cast(AbilityInstance)) 195 | { 196 | // Invoke the InputReleased event. This is not replicated here. 197 | // If someone is listening, they may replicate the InputReleased event to the server. 198 | InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputReleased, Spec.Handle,ModularAbilityInstance->GetCurrentActivationInfo().GetActivationPredictionKey()); 199 | } 200 | } 201 | } 202 | } 203 | 204 | void UModularAbilitySystemComponent::AbilityInputTagPressed(const FGameplayTag& InputTag) 205 | { 206 | if (!InputTag.IsValid()){return;} 207 | SetInputTagActive(InputTag, true); 208 | 209 | for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items) 210 | { 211 | if (AbilitySpec.Ability && (AbilitySpec.GetDynamicSpecSourceTags().HasTagExact(InputTag))) 212 | { 213 | InputPressedSpecHandles.AddUnique(AbilitySpec.Handle); 214 | InputHeldSpecHandles.AddUnique(AbilitySpec.Handle); 215 | } 216 | } 217 | 218 | } 219 | 220 | void UModularAbilitySystemComponent::AbilityInputTagReleased(const FGameplayTag& InputTag) 221 | { 222 | if (InputTag.IsValid()) 223 | { 224 | SetInputTagActive(InputTag, false); 225 | 226 | for (const FGameplayAbilitySpec& AbilitySpec : ActivatableAbilities.Items) 227 | { 228 | if (AbilitySpec.Ability && (AbilitySpec.GetDynamicSpecSourceTags().HasTagExact(InputTag))) 229 | { 230 | InputReleasedSpecHandles.AddUnique(AbilitySpec.Handle); 231 | InputHeldSpecHandles.Remove(AbilitySpec.Handle); 232 | } 233 | } 234 | } 235 | } 236 | 237 | bool UModularAbilitySystemComponent::IsInputTagActive(const FGameplayTag& InputTag) const 238 | { 239 | return InputTag.IsValid() && ActiveInputTags.HasTagExact(InputTag); 240 | } 241 | 242 | 243 | void UModularAbilitySystemComponent::SetInputTagActive(const FGameplayTag& InputTag, bool bActive) 244 | { 245 | if (!InputTag.IsValid()) 246 | return; 247 | 248 | if (bActive) 249 | { 250 | ActiveInputTags.AddTag(InputTag); 251 | } 252 | else 253 | { 254 | ActiveInputTags.RemoveTag(InputTag); 255 | } 256 | } 257 | 258 | void UModularAbilitySystemComponent::ProcessAbilityInput(float DeltaTime, bool bGamePaused) 259 | { 260 | if (HasMatchingGameplayTag(TAG_Gameplay_AbilityInputBlocked)) 261 | { 262 | ClearAbilityInput(); 263 | return; 264 | } 265 | 266 | static TArray AbilitiesToActivate; 267 | AbilitiesToActivate.Reset(); 268 | 269 | //@TODO: See if we can use FScopedServerAbilityRPCBatcher ScopedRPCBatcher in some of these loops 270 | 271 | // 272 | // Process all abilities that activate when the input is held. 273 | // 274 | for (const FGameplayAbilitySpecHandle& SpecHandle : InputHeldSpecHandles) 275 | { 276 | if (const FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(SpecHandle)) 277 | { 278 | if (AbilitySpec->Ability && !AbilitySpec->IsActive()) 279 | { 280 | const UModularGameplayAbility* ModularAbilityCDO = CastChecked(AbilitySpec->Ability); 281 | 282 | if (ModularAbilityCDO->GetActivationPolicy() == EModularAbilityActivationPolicy::WhileInputActive) 283 | { 284 | AbilitiesToActivate.AddUnique(AbilitySpec->Handle); 285 | } 286 | } 287 | } 288 | } 289 | 290 | // 291 | // Process all abilities that had their input pressed this frame. 292 | // 293 | for (const FGameplayAbilitySpecHandle& SpecHandle : InputPressedSpecHandles) 294 | { 295 | if (FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(SpecHandle)) 296 | { 297 | if (AbilitySpec->Ability) 298 | { 299 | AbilitySpec->InputPressed = true; 300 | 301 | if (AbilitySpec->IsActive()) 302 | { 303 | // Ability is active so pass along the input event. 304 | AbilitySpecInputPressed(*AbilitySpec); 305 | } 306 | else 307 | { 308 | const UModularGameplayAbility* ModularAbilityCDO = CastChecked(AbilitySpec->Ability); 309 | 310 | if (ModularAbilityCDO->GetActivationPolicy() == EModularAbilityActivationPolicy::OnInputTriggered) 311 | { 312 | AbilitiesToActivate.AddUnique(AbilitySpec->Handle); 313 | } 314 | } 315 | } 316 | } 317 | } 318 | 319 | // 320 | // Try to activate all the abilities that are from presses and holds. 321 | // We do it all at once so that held inputs don't activate the ability 322 | // and then also send a input event to the ability because of the press. 323 | // 324 | for (const FGameplayAbilitySpecHandle& AbilitySpecHandle : AbilitiesToActivate) 325 | { 326 | TryActivateAbility(AbilitySpecHandle); 327 | } 328 | 329 | // 330 | // Process all abilities that had their input released this frame. 331 | // 332 | for (const FGameplayAbilitySpecHandle& SpecHandle : InputReleasedSpecHandles) 333 | { 334 | if (FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(SpecHandle)) 335 | { 336 | if (AbilitySpec->Ability) 337 | { 338 | AbilitySpec->InputPressed = false; 339 | 340 | if (AbilitySpec->IsActive()) 341 | { 342 | // Ability is active so pass along the input event. 343 | AbilitySpecInputReleased(*AbilitySpec); 344 | } 345 | } 346 | } 347 | } 348 | 349 | // 350 | // Clear the cached ability handles. 351 | // 352 | InputPressedSpecHandles.Reset(); 353 | InputReleasedSpecHandles.Reset(); 354 | } 355 | 356 | void UModularAbilitySystemComponent::ClearAbilityInput() 357 | { 358 | InputPressedSpecHandles.Reset(); 359 | InputReleasedSpecHandles.Reset(); 360 | InputHeldSpecHandles.Reset(); 361 | } 362 | 363 | void UModularAbilitySystemComponent::NotifyAbilityActivated(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability) 364 | { 365 | Super::NotifyAbilityActivated(Handle, Ability); 366 | 367 | UModularGameplayAbility* ModularAbility = Cast(Ability); 368 | if (!ModularAbility) 369 | { 370 | return; 371 | } 372 | 373 | AddAbilityToActivationGroup(ModularAbility->GetActivationGroup(), ModularAbility); 374 | } 375 | 376 | void UModularAbilitySystemComponent::NotifyAbilityFailed(const FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason) 377 | { 378 | Super::NotifyAbilityFailed(Handle, Ability, FailureReason); 379 | 380 | if (APawn* Avatar = Cast(GetAvatarActor())) 381 | { 382 | if (!Avatar->IsLocallyControlled() && Ability->IsSupportedForNetworking()) 383 | { 384 | ClientNotifyAbilityFailed(Ability, FailureReason); 385 | return; 386 | } 387 | } 388 | 389 | HandleAbilityFailed(Ability, FailureReason); 390 | } 391 | 392 | void UModularAbilitySystemComponent::NotifyAbilityEnded(FGameplayAbilitySpecHandle Handle, UGameplayAbility* Ability, bool bWasCancelled) 393 | { 394 | Super::NotifyAbilityEnded(Handle, Ability, bWasCancelled); 395 | 396 | UModularGameplayAbility* ModularAbility = CastChecked(Ability); 397 | 398 | RemoveAbilityFromActivationGroup(ModularAbility->GetActivationGroup(), ModularAbility); 399 | } 400 | 401 | void UModularAbilitySystemComponent::ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bEnableBlockTags, const FGameplayTagContainer& BlockTags, bool bExecuteCancelTags, const FGameplayTagContainer& CancelTags) 402 | { 403 | FGameplayTagContainer ModifiedBlockTags = BlockTags; 404 | FGameplayTagContainer ModifiedCancelTags = CancelTags; 405 | 406 | if (TagRelationshipMapping) 407 | { 408 | // Use the mapping to expand the ability tags into block and cancel tag 409 | TagRelationshipMapping->GetAbilityTagsToBlockAndCancel(AbilityTags, &ModifiedBlockTags, &ModifiedCancelTags); 410 | } 411 | 412 | Super::ApplyAbilityBlockAndCancelTags(AbilityTags, RequestingAbility, bEnableBlockTags, ModifiedBlockTags, bExecuteCancelTags, ModifiedCancelTags); 413 | 414 | //@TODO: Apply any special logic like blocking input or movement 415 | } 416 | 417 | void UModularAbilitySystemComponent::HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& AbilityTags, UGameplayAbility* RequestingAbility, bool bCanBeCanceled) 418 | { 419 | Super::HandleChangeAbilityCanBeCanceled(AbilityTags, RequestingAbility, bCanBeCanceled); 420 | 421 | //@TODO: Apply any special logic like blocking input or movement 422 | } 423 | 424 | void UModularAbilitySystemComponent::GetAdditionalActivationTagRequirements(const FGameplayTagContainer& AbilityTags, FGameplayTagContainer& OutActivationRequired, FGameplayTagContainer& OutActivationBlocked) const 425 | { 426 | if (TagRelationshipMapping) 427 | { 428 | TagRelationshipMapping->GetRequiredAndBlockedActivationTags(AbilityTags, &OutActivationRequired, &OutActivationBlocked); 429 | } 430 | } 431 | 432 | void UModularAbilitySystemComponent::SetTagRelationshipMapping(UModularAbilityTagRelationshipMapping* NewMapping) 433 | { 434 | TagRelationshipMapping = NewMapping; 435 | } 436 | 437 | void UModularAbilitySystemComponent::ClientNotifyAbilityFailed_Implementation(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason) 438 | { 439 | HandleAbilityFailed(Ability, FailureReason); 440 | } 441 | 442 | void UModularAbilitySystemComponent::HandleAbilityFailed(const UGameplayAbility* Ability, const FGameplayTagContainer& FailureReason) 443 | { 444 | //UE_LOG(LogModularGameplayAbilities, Warning, TEXT("Ability %s failed to activate (tags: %s)"), *GetPathNameSafe(Ability), *FailureReason.ToString()); 445 | 446 | if (const UModularGameplayAbility* ModularAbility = Cast(Ability)) 447 | { 448 | ModularAbility->OnAbilityFailedToActivate(FailureReason); 449 | } 450 | } 451 | 452 | bool UModularAbilitySystemComponent::IsActivationGroupBlocked(EModularAbilityActivationGroup Group) const 453 | { 454 | bool bBlocked = false; 455 | 456 | switch (Group) 457 | { 458 | case EModularAbilityActivationGroup::Independent: 459 | // Independent abilities are never blocked. 460 | bBlocked = false; 461 | break; 462 | 463 | case EModularAbilityActivationGroup::Exclusive_Replaceable: 464 | case EModularAbilityActivationGroup::Exclusive_Blocking: 465 | // Exclusive abilities can activate if nothing is blocking. 466 | bBlocked = (ActivationGroupCounts[StaticCast(EModularAbilityActivationGroup::Exclusive_Blocking)] > 0); 467 | break; 468 | 469 | default: 470 | checkf(false, TEXT("IsActivationGroupBlocked: Invalid ActivationGroup [%d]\n"), StaticCast(Group)); 471 | break; 472 | } 473 | 474 | return bBlocked; 475 | } 476 | 477 | void UModularAbilitySystemComponent::AddAbilityToActivationGroup(EModularAbilityActivationGroup Group, UModularGameplayAbility* ModularAbility) 478 | { 479 | check(ModularAbility); 480 | check(ActivationGroupCounts[StaticCast(Group)] < INT32_MAX); 481 | 482 | ActivationGroupCounts[StaticCast(Group)]++; 483 | 484 | constexpr bool bReplicateCancelAbility = false; 485 | 486 | switch (Group) 487 | { 488 | case EModularAbilityActivationGroup::Independent: 489 | // Independent abilities do not cancel any other abilities. 490 | break; 491 | 492 | case EModularAbilityActivationGroup::Exclusive_Replaceable: 493 | case EModularAbilityActivationGroup::Exclusive_Blocking: 494 | CancelActivationGroupAbilities(EModularAbilityActivationGroup::Exclusive_Replaceable, ModularAbility, bReplicateCancelAbility); 495 | break; 496 | 497 | default: 498 | checkf(false, TEXT("AddAbilityToActivationGroup: Invalid ActivationGroup [%d]\n"), StaticCast(Group)); 499 | break; 500 | } 501 | 502 | if (const int32 ExclusiveCount = ActivationGroupCounts[StaticCast(EModularAbilityActivationGroup::Exclusive_Replaceable)] 503 | + ActivationGroupCounts[StaticCast(EModularAbilityActivationGroup::Exclusive_Blocking)]; 504 | !ensure(ExclusiveCount <= 1)) 505 | { 506 | UE_LOG(LogModularGameplayAbilities, Error, TEXT("AddAbilityToActivationGroup: Multiple exclusive abilities are running.")); 507 | } 508 | } 509 | 510 | void UModularAbilitySystemComponent::RemoveAbilityFromActivationGroup(EModularAbilityActivationGroup Group, UModularGameplayAbility* ModularAbility) 511 | { 512 | check(ModularAbility); 513 | check(ActivationGroupCounts[StaticCast(Group)] > 0); 514 | 515 | ActivationGroupCounts[StaticCast(Group)]--; 516 | } 517 | 518 | void UModularAbilitySystemComponent::CancelActivationGroupAbilities(EModularAbilityActivationGroup Group, UModularGameplayAbility* IgnoreModularAbility, bool bReplicateCancelAbility) 519 | { 520 | auto ShouldCancelFunc = [this, Group, IgnoreModularAbility](const UModularGameplayAbility* ModularAbility, FGameplayAbilitySpecHandle Handle) 521 | { 522 | return ((ModularAbility->GetActivationGroup() == Group) && (ModularAbility != IgnoreModularAbility)); 523 | }; 524 | 525 | CancelAbilitiesByFunc(ShouldCancelFunc, bReplicateCancelAbility); 526 | } 527 | 528 | void UModularAbilitySystemComponent::AddDynamicTagGameplayEffect(const FGameplayTag& Tag) 529 | { 530 | const TSubclassOf DynamicTagGE = UModularAssetManager::GetSubclass(UModularAbilityData::Get().DynamicTagGameplayEffect); 531 | if (!DynamicTagGE) 532 | { 533 | UE_LOG(LogModularGameplayAbilities, Warning, TEXT("AddDynamicTagGameplayEffect: Unable to find DynamicTagGameplayEffect [%s]."), *UModularAbilityData::Get().DynamicTagGameplayEffect.GetAssetName()); 534 | return; 535 | } 536 | 537 | const FGameplayEffectSpecHandle SpecHandle = MakeOutgoingSpec(DynamicTagGE, 1.0f, MakeEffectContext()); 538 | FGameplayEffectSpec* Spec = SpecHandle.Data.Get(); 539 | 540 | if (!Spec) 541 | { 542 | UE_LOG(LogModularGameplayAbilities, Warning, TEXT("AddDynamicTagGameplayEffect: Unable to make outgoing spec for [%s]."), *GetNameSafe(DynamicTagGE)); 543 | return; 544 | } 545 | 546 | Spec->DynamicGrantedTags.AddTag(Tag); 547 | 548 | ApplyGameplayEffectSpecToSelf(*Spec); 549 | } 550 | 551 | void UModularAbilitySystemComponent::RemoveDynamicTagGameplayEffect(const FGameplayTag& Tag) 552 | { 553 | const TSubclassOf DynamicTagGE = UModularAssetManager::GetSubclass(UModularAbilityData::Get().DynamicTagGameplayEffect); 554 | if (!DynamicTagGE) 555 | { 556 | UE_LOG(LogModularGameplayAbilities, Warning, TEXT("RemoveDynamicTagGameplayEffect: Unable to find gameplay effect [%s]."), *UModularAbilityData::Get().DynamicTagGameplayEffect.GetAssetName()); 557 | return; 558 | } 559 | 560 | FGameplayEffectQuery Query = FGameplayEffectQuery::MakeQuery_MatchAnyOwningTags(FGameplayTagContainer(Tag)); 561 | Query.EffectDefinition = DynamicTagGE; 562 | 563 | RemoveActiveEffects(Query); 564 | } 565 | 566 | void UModularAbilitySystemComponent::GetAbilityTargetData(const FGameplayAbilitySpecHandle AbilityHandle, FGameplayAbilityActivationInfo ActivationInfo, FGameplayAbilityTargetDataHandle& OutTargetDataHandle) 567 | { 568 | if (const TSharedPtr ReplicatedData = AbilityTargetDataMap.Find(FGameplayAbilitySpecHandleAndPredictionKey(AbilityHandle, ActivationInfo.GetActivationPredictionKey())); 569 | ReplicatedData.IsValid()) 570 | { 571 | OutTargetDataHandle = ReplicatedData->TargetData; 572 | } 573 | } 574 | --------------------------------------------------------------------------------