// Copyright (c) 2026 Reignance Studios. MIT License, see LICENSE. #pragma once #include "CoreMinimal.h" #include "Engine/CancellableAsyncAction.h" #include "GameplayTagContainer.h" #include "RSAsyncWaitTagCount.generated.h" class UAbilitySystemComponent; DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FRSTagCountChanged, FGameplayTag, Tag, int32, NewCount); /** * Fires OnCountChanged whenever the number of a gameplay tag on an ASC changes * (stacks of State.Burning, charges of Ability.Charge, State.Stunned on or off). * Fires once right away with the current count, so a widget never polls in Tick. * Works on clients: tag counts that replicate (effect tags in Mixed or Full mode, * replicated loose tags) trigger it when they arrive. * * Call Cancel (or let the widget's Destruct call it) to unbind. */ UCLASS(meta = (ExposedAsyncProxy = "AsyncTask")) class RSGASHELPERS_API URSAsyncWaitTagCount : public UCancellableAsyncAction { GENERATED_BODY() public: UPROPERTY(BlueprintAssignable) FRSTagCountChanged OnCountChanged; /** Watch a tag on the actor's ASC (PlayerState setups included). Child tags count towards their parent. */ UFUNCTION(BlueprintCallable, Category = "RS|GAS", meta = (BlueprintInternalUseOnly = "true", DefaultToSelf = "Actor")) static URSAsyncWaitTagCount* WaitGameplayTagCount(AActor* Actor, FGameplayTag Tag, bool bFireInitialCount = true); virtual void Activate() override; virtual void Cancel() override; private: void HandleTagChanged(const FGameplayTag Tag, int32 NewCount); TWeakObjectPtr WeakASC; FGameplayTag WatchedTag; FDelegateHandle Binding; bool bFireInitial = true; };