2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
# pragma once
2022-09-13 07:18:28 +00:00
# include "Containers/Array.h"
# include "Containers/Map.h"
# include "Delegates/Delegate.h"
# include "Engine/EngineTypes.h"
2022-05-23 18:41:30 +00:00
# include "GameplayAbilitySpec.h"
2022-09-13 07:18:28 +00:00
# include "GameplayTagContainer.h"
# include "HAL/Platform.h"
# include "Subsystems/WorldSubsystem.h"
# include "Templates/SubclassOf.h"
# include "UObject/UObjectGlobals.h"
# include "UObject/WeakObjectPtr.h"
2022-05-23 18:41:30 +00:00
# include "LyraGamePhaseSubsystem.generated.h"
class ULyraGamePhaseAbility ;
2022-09-13 07:18:28 +00:00
class UObject ;
struct FFrame ;
struct FGameplayAbilitySpecHandle ;
2022-05-23 18:41:30 +00:00
DECLARE_DYNAMIC_DELEGATE_OneParam ( FLyraGamePhaseDynamicDelegate , const ULyraGamePhaseAbility * , Phase ) ;
DECLARE_DELEGATE_OneParam ( FLyraGamePhaseDelegate , const ULyraGamePhaseAbility * Phase ) ;
DECLARE_DYNAMIC_DELEGATE_OneParam ( FLyraGamePhaseTagDynamicDelegate , const FGameplayTag & , PhaseTag ) ;
DECLARE_DELEGATE_OneParam ( FLyraGamePhaseTagDelegate , const FGameplayTag & PhaseTag ) ;
// Match rule for message receivers
UENUM ( BlueprintType )
enum class EPhaseTagMatchType : uint8
{
// An exact match will only receive messages with exactly the same channel
// (e.g., registering for "A.B" will match a broadcast of A.B but not A.B.C)
ExactMatch ,
// A partial match will receive any messages rooted in the same channel
// (e.g., registering for "A.B" will match a broadcast of A.B as well as A.B.C)
PartialMatch
} ;
2022-09-13 07:18:28 +00:00
/** Subsystem for managing Lyra's game phases using gameplay tags in a nested manner, which allows parent and child
* phases to be active at the same time , but not sibling phases .
* Example : Game . Playing and Game . Playing . WarmUp can coexist , but Game . Playing and Game . ShowingScore cannot .
* When a new phase is started , any active phases that are not ancestors will be ended .
* Example : if Game . Playing and Game . Playing . CaptureTheFlag are active when Game . Playing . PostGame is started ,
* Game . Playing will remain active , while Game . Playing . CaptureTheFlag will end .
*/
2022-05-23 18:41:30 +00:00
UCLASS ( )
class ULyraGamePhaseSubsystem : public UWorldSubsystem
{
GENERATED_BODY ( )
public :
ULyraGamePhaseSubsystem ( ) ;
virtual void PostInitialize ( ) override ;
virtual bool ShouldCreateSubsystem ( UObject * Outer ) const override ;
void StartPhase ( TSubclassOf < ULyraGamePhaseAbility > PhaseAbility , FLyraGamePhaseDelegate PhaseEndedCallback = FLyraGamePhaseDelegate ( ) ) ;
//TODO Return a handle so folks can delete these. They will just grow until the world resets.
//TODO Should we just occasionally clean these observers up? It's not as if everyone will properly unhook them even if there is a handle.
void WhenPhaseStartsOrIsActive ( FGameplayTag PhaseTag , EPhaseTagMatchType MatchType , const FLyraGamePhaseTagDelegate & WhenPhaseActive ) ;
void WhenPhaseEnds ( FGameplayTag PhaseTag , EPhaseTagMatchType MatchType , const FLyraGamePhaseTagDelegate & WhenPhaseEnd ) ;
UFUNCTION ( BlueprintCallable , BlueprintAuthorityOnly , BlueprintPure = false , meta = ( AutoCreateRefTerm = " PhaseTag " ) )
bool IsPhaseActive ( const FGameplayTag & PhaseTag ) const ;
protected :
virtual bool DoesSupportWorldType ( const EWorldType : : Type WorldType ) const override ;
UFUNCTION ( BlueprintCallable , BlueprintAuthorityOnly , Category = " Game Phase " , meta = ( DisplayName = " Start Phase " , AutoCreateRefTerm = " PhaseEnded " ) )
void K2_StartPhase ( TSubclassOf < ULyraGamePhaseAbility > Phase , const FLyraGamePhaseDynamicDelegate & PhaseEnded ) ;
UFUNCTION ( BlueprintCallable , BlueprintAuthorityOnly , Category = " Game Phase " , meta = ( DisplayName = " When Phase Starts or Is Active " , AutoCreateRefTerm = " WhenPhaseActive " ) )
void K2_WhenPhaseStartsOrIsActive ( FGameplayTag PhaseTag , EPhaseTagMatchType MatchType , FLyraGamePhaseTagDynamicDelegate WhenPhaseActive ) ;
UFUNCTION ( BlueprintCallable , BlueprintAuthorityOnly , Category = " Game Phase " , meta = ( DisplayName = " When Phase Ends " , AutoCreateRefTerm = " WhenPhaseEnd " ) )
void K2_WhenPhaseEnds ( FGameplayTag PhaseTag , EPhaseTagMatchType MatchType , FLyraGamePhaseTagDynamicDelegate WhenPhaseEnd ) ;
void OnBeginPhase ( const ULyraGamePhaseAbility * PhaseAbility , const FGameplayAbilitySpecHandle PhaseAbilityHandle ) ;
void OnEndPhase ( const ULyraGamePhaseAbility * PhaseAbility , const FGameplayAbilitySpecHandle PhaseAbilityHandle ) ;
private :
struct FLyraGamePhaseEntry
{
public :
FGameplayTag PhaseTag ;
FLyraGamePhaseDelegate PhaseEndedCallback ;
} ;
TMap < FGameplayAbilitySpecHandle , FLyraGamePhaseEntry > ActivePhaseMap ;
struct FPhaseObserver
{
public :
bool IsMatch ( const FGameplayTag & ComparePhaseTag ) const ;
FGameplayTag PhaseTag ;
EPhaseTagMatchType MatchType = EPhaseTagMatchType : : ExactMatch ;
FLyraGamePhaseTagDelegate PhaseCallback ;
} ;
TArray < FPhaseObserver > PhaseStartObservers ;
TArray < FPhaseObserver > PhaseEndObservers ;
friend class ULyraGamePhaseAbility ;
} ;