221 lines
8.0 KiB
C++
221 lines
8.0 KiB
C++
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||
|
|
||
|
#include "LyraGamePhaseSubsystem.h"
|
||
|
#include "LyraGamePhaseAbility.h"
|
||
|
#include "GameplayTagsManager.h"
|
||
|
#include "GameFramework/GameState.h"
|
||
|
#include "AbilitySystemComponent.h"
|
||
|
#include "AbilitySystem/Abilities/LyraGameplayAbility.h"
|
||
|
#include "AbilitySystem/LyraAbilitySystemComponent.h"
|
||
|
#include "LyraGamePhaseLog.h"
|
||
|
|
||
|
DEFINE_LOG_CATEGORY(LogLyraGamePhase);
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// ULyraGamePhaseSubsystem
|
||
|
|
||
|
ULyraGamePhaseSubsystem::ULyraGamePhaseSubsystem()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void ULyraGamePhaseSubsystem::PostInitialize()
|
||
|
{
|
||
|
Super::PostInitialize();
|
||
|
}
|
||
|
|
||
|
bool ULyraGamePhaseSubsystem::ShouldCreateSubsystem(UObject* Outer) const
|
||
|
{
|
||
|
if (Super::ShouldCreateSubsystem(Outer))
|
||
|
{
|
||
|
//UWorld* World = Cast<UWorld>(Outer);
|
||
|
//check(World);
|
||
|
|
||
|
//return World->GetAuthGameMode() != nullptr;
|
||
|
//return nullptr;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool ULyraGamePhaseSubsystem::DoesSupportWorldType(const EWorldType::Type WorldType) const
|
||
|
{
|
||
|
return WorldType == EWorldType::Game || WorldType == EWorldType::PIE;
|
||
|
}
|
||
|
|
||
|
void ULyraGamePhaseSubsystem::StartPhase(TSubclassOf<ULyraGamePhaseAbility> PhaseAbility, FLyraGamePhaseDelegate PhaseEndedCallback)
|
||
|
{
|
||
|
UWorld* World = GetWorld();
|
||
|
ULyraAbilitySystemComponent* GameState_ASC = World->GetGameState()->FindComponentByClass<ULyraAbilitySystemComponent>();
|
||
|
if (ensure(GameState_ASC))
|
||
|
{
|
||
|
FGameplayAbilitySpec PhaseSpec(PhaseAbility, 1, 0, this);
|
||
|
FGameplayAbilitySpecHandle SpecHandle = GameState_ASC->GiveAbilityAndActivateOnce(PhaseSpec);
|
||
|
FGameplayAbilitySpec* FoundSpec = GameState_ASC->FindAbilitySpecFromHandle(SpecHandle);
|
||
|
|
||
|
if (FoundSpec && FoundSpec->IsActive())
|
||
|
{
|
||
|
FLyraGamePhaseEntry& Entry = ActivePhaseMap.FindOrAdd(SpecHandle);
|
||
|
Entry.PhaseEndedCallback = PhaseEndedCallback;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PhaseEndedCallback.ExecuteIfBound(nullptr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ULyraGamePhaseSubsystem::K2_StartPhase(TSubclassOf<ULyraGamePhaseAbility> PhaseAbility, const FLyraGamePhaseDynamicDelegate& PhaseEndedDelegate)
|
||
|
{
|
||
|
const FLyraGamePhaseDelegate EndedDelegate = FLyraGamePhaseDelegate::CreateWeakLambda(const_cast<UObject*>(PhaseEndedDelegate.GetUObject()), [PhaseEndedDelegate](const ULyraGamePhaseAbility* PhaseAbility) {
|
||
|
PhaseEndedDelegate.ExecuteIfBound(PhaseAbility);
|
||
|
});
|
||
|
|
||
|
StartPhase(PhaseAbility, EndedDelegate);
|
||
|
}
|
||
|
|
||
|
void ULyraGamePhaseSubsystem::K2_WhenPhaseStartsOrIsActive(FGameplayTag PhaseTag, EPhaseTagMatchType MatchType, FLyraGamePhaseTagDynamicDelegate WhenPhaseActive)
|
||
|
{
|
||
|
const FLyraGamePhaseTagDelegate ActiveDelegate = FLyraGamePhaseTagDelegate::CreateWeakLambda(WhenPhaseActive.GetUObject(), [WhenPhaseActive](const FGameplayTag& PhaseTag) {
|
||
|
WhenPhaseActive.ExecuteIfBound(PhaseTag);
|
||
|
});
|
||
|
|
||
|
WhenPhaseStartsOrIsActive(PhaseTag, MatchType, ActiveDelegate);
|
||
|
}
|
||
|
|
||
|
void ULyraGamePhaseSubsystem::K2_WhenPhaseEnds(FGameplayTag PhaseTag, EPhaseTagMatchType MatchType, FLyraGamePhaseTagDynamicDelegate WhenPhaseEnd)
|
||
|
{
|
||
|
const FLyraGamePhaseTagDelegate EndedDelegate = FLyraGamePhaseTagDelegate::CreateWeakLambda(WhenPhaseEnd.GetUObject(), [WhenPhaseEnd](const FGameplayTag& PhaseTag) {
|
||
|
WhenPhaseEnd.ExecuteIfBound(PhaseTag);
|
||
|
});
|
||
|
|
||
|
WhenPhaseEnds(PhaseTag, MatchType, EndedDelegate);
|
||
|
}
|
||
|
|
||
|
void ULyraGamePhaseSubsystem::WhenPhaseStartsOrIsActive(FGameplayTag PhaseTag, EPhaseTagMatchType MatchType, const FLyraGamePhaseTagDelegate& WhenPhaseActive)
|
||
|
{
|
||
|
FPhaseObserver Observer;
|
||
|
Observer.PhaseTag = PhaseTag;
|
||
|
Observer.MatchType = MatchType;
|
||
|
Observer.PhaseCallback = WhenPhaseActive;
|
||
|
PhaseStartObservers.Add(Observer);
|
||
|
|
||
|
if (IsPhaseActive(PhaseTag))
|
||
|
{
|
||
|
WhenPhaseActive.ExecuteIfBound(PhaseTag);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ULyraGamePhaseSubsystem::WhenPhaseEnds(FGameplayTag PhaseTag, EPhaseTagMatchType MatchType, const FLyraGamePhaseTagDelegate& WhenPhaseEnd)
|
||
|
{
|
||
|
FPhaseObserver Observer;
|
||
|
Observer.PhaseTag = PhaseTag;
|
||
|
Observer.MatchType = MatchType;
|
||
|
Observer.PhaseCallback = WhenPhaseEnd;
|
||
|
PhaseEndObservers.Add(Observer);
|
||
|
}
|
||
|
|
||
|
bool ULyraGamePhaseSubsystem::IsPhaseActive(const FGameplayTag& PhaseTag) const
|
||
|
{
|
||
|
for (const auto& KVP : ActivePhaseMap)
|
||
|
{
|
||
|
const FLyraGamePhaseEntry& PhaseEntry = KVP.Value;
|
||
|
if (PhaseEntry.PhaseTag.MatchesTag(PhaseTag))
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void ULyraGamePhaseSubsystem::OnBeginPhase(const ULyraGamePhaseAbility* PhaseAbility, const FGameplayAbilitySpecHandle PhaseAbilityHandle)
|
||
|
{
|
||
|
const FGameplayTag IncomingPhaseTag = PhaseAbility->GetGamePhaseTag();
|
||
|
const FGameplayTag IncomingPhaseParentTag = UGameplayTagsManager::Get().RequestGameplayTagDirectParent(IncomingPhaseTag);
|
||
|
|
||
|
UE_LOG(LogLyraGamePhase, Log, TEXT("Beginning Phase '%s' (%s)"), *IncomingPhaseTag.ToString(), *GetNameSafe(PhaseAbility));
|
||
|
|
||
|
const UWorld* World = GetWorld();
|
||
|
ULyraAbilitySystemComponent* GameState_ASC = World->GetGameState()->FindComponentByClass<ULyraAbilitySystemComponent>();
|
||
|
if (ensure(GameState_ASC))
|
||
|
{
|
||
|
TArray<FGameplayAbilitySpec*> ActivePhases;
|
||
|
for (const auto& KVP : ActivePhaseMap)
|
||
|
{
|
||
|
const FGameplayAbilitySpecHandle ActiveAbilityHandle = KVP.Key;
|
||
|
if (FGameplayAbilitySpec* Spec = GameState_ASC->FindAbilitySpecFromHandle(ActiveAbilityHandle))
|
||
|
{
|
||
|
ActivePhases.Add(Spec);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (const FGameplayAbilitySpec* ActivePhase : ActivePhases)
|
||
|
{
|
||
|
const ULyraGamePhaseAbility* ActivePhaseAbility = CastChecked<ULyraGamePhaseAbility>(ActivePhase->Ability);
|
||
|
const FGameplayTag ActivePhaseTag = ActivePhaseAbility->GetGamePhaseTag();
|
||
|
|
||
|
// So if the active phase currently matches the incoming phase tag, we allow it.
|
||
|
// i.e. multiple gameplay abilities can all be associated with the same phase tag.
|
||
|
// For example,
|
||
|
// You can be in the, Game.Playing, phase, and then start a sub-phase, like Game.Playing.SuddenDeath
|
||
|
// Game.Playing phase will still be active, and if someone were to push another one, like,
|
||
|
// Game.Playing.ActualSuddenDeath, it would end Game.Playing.SuddenDeath phase, but Game.Playing would
|
||
|
// continue. Similarly if we activated Game.GameOver, all the Game.Playing* phases would end.
|
||
|
if (!ActivePhaseTag.MatchesTag(IncomingPhaseTag) && ActivePhaseTag.MatchesTag(IncomingPhaseParentTag))
|
||
|
{
|
||
|
UE_LOG(LogLyraGamePhase, Log, TEXT("\tEnding Phase '%s' (%s)"), *ActivePhaseTag.ToString(), *GetNameSafe(ActivePhaseAbility));
|
||
|
|
||
|
FGameplayAbilitySpecHandle HandleToEnd = ActivePhase->Handle;
|
||
|
GameState_ASC->CancelAbilitiesByFunc([HandleToEnd](const ULyraGameplayAbility* LyraAbility, FGameplayAbilitySpecHandle Handle) {
|
||
|
return Handle == HandleToEnd;
|
||
|
}, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FLyraGamePhaseEntry& Entry = ActivePhaseMap.FindOrAdd(PhaseAbilityHandle);
|
||
|
Entry.PhaseTag = IncomingPhaseTag;
|
||
|
|
||
|
// Notify all observers of this phase that it has started.
|
||
|
for (const FPhaseObserver& Observer : PhaseStartObservers)
|
||
|
{
|
||
|
if (Observer.IsMatch(IncomingPhaseTag))
|
||
|
{
|
||
|
Observer.PhaseCallback.ExecuteIfBound(IncomingPhaseTag);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ULyraGamePhaseSubsystem::OnEndPhase(const ULyraGamePhaseAbility* PhaseAbility, const FGameplayAbilitySpecHandle PhaseAbilityHandle)
|
||
|
{
|
||
|
const FGameplayTag EndedPhaseTag = PhaseAbility->GetGamePhaseTag();
|
||
|
UE_LOG(LogLyraGamePhase, Log, TEXT("Ended Phase '%s' (%s)"), *EndedPhaseTag.ToString(), *GetNameSafe(PhaseAbility));
|
||
|
|
||
|
const FLyraGamePhaseEntry& Entry = ActivePhaseMap.FindChecked(PhaseAbilityHandle);
|
||
|
Entry.PhaseEndedCallback.ExecuteIfBound(PhaseAbility);
|
||
|
|
||
|
ActivePhaseMap.Remove(PhaseAbilityHandle);
|
||
|
|
||
|
// Notify all observers of this phase that it has ended.
|
||
|
for (const FPhaseObserver& Observer : PhaseEndObservers)
|
||
|
{
|
||
|
if (Observer.IsMatch(EndedPhaseTag))
|
||
|
{
|
||
|
Observer.PhaseCallback.ExecuteIfBound(EndedPhaseTag);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ULyraGamePhaseSubsystem::FPhaseObserver::IsMatch(const FGameplayTag& ComparePhaseTag) const
|
||
|
{
|
||
|
switch(MatchType)
|
||
|
{
|
||
|
case EPhaseTagMatchType::ExactMatch:
|
||
|
return ComparePhaseTag == PhaseTag;
|
||
|
case EPhaseTagMatchType::PartialMatch:
|
||
|
return ComparePhaseTag.MatchesTag(PhaseTag);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|