RealtimeStyleTransferRuntime/Source/LyraGame/Teams/LyraTeamSubsystem.cpp

371 lines
11 KiB
C++
Raw Normal View History

2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraTeamSubsystem.h"
#include "Net/UnrealNetwork.h"
#include "LyraTeamPublicInfo.h"
#include "LyraTeamPrivateInfo.h"
#include "GameFramework/Controller.h"
#include "Player/LyraPlayerState.h"
#include "GameFramework/Pawn.h"
#include "AbilitySystemGlobals.h"
#include "LyraTeamCheats.h"
#include "LyraTeamAgentInterface.h"
#include "LyraLogChannels.h"
//////////////////////////////////////////////////////////////////////
// FLyraTeamTrackingInfo
void FLyraTeamTrackingInfo::SetTeamInfo(ALyraTeamInfoBase* Info)
{
if (ALyraTeamPublicInfo* NewPublicInfo = Cast<ALyraTeamPublicInfo>(Info))
{
ensure((PublicInfo == nullptr) || (PublicInfo == NewPublicInfo));
PublicInfo = NewPublicInfo;
ULyraTeamDisplayAsset* OldDisplayAsset = DisplayAsset;
DisplayAsset = NewPublicInfo->GetTeamDisplayAsset();
if (OldDisplayAsset != DisplayAsset)
{
OnTeamDisplayAssetChanged.Broadcast(DisplayAsset);
}
}
else if (ALyraTeamPrivateInfo* NewPrivateInfo = Cast<ALyraTeamPrivateInfo>(Info))
{
ensure((PrivateInfo == nullptr) || (PrivateInfo == NewPrivateInfo));
PrivateInfo = NewPrivateInfo;
}
else
{
checkf(false, TEXT("Expected a public or private team info but got %s"), *GetPathNameSafe(Info))
}
}
void FLyraTeamTrackingInfo::RemoveTeamInfo(ALyraTeamInfoBase* Info)
{
if (PublicInfo == Info)
{
PublicInfo = nullptr;
}
else if (PrivateInfo == Info)
{
PrivateInfo = nullptr;
}
else
{
ensureMsgf(false, TEXT("Expected a previously registered team info but got %s"), *GetPathNameSafe(Info));
}
}
//////////////////////////////////////////////////////////////////////
// ULyraTeamSubsystem
ULyraTeamSubsystem::ULyraTeamSubsystem()
{
}
void ULyraTeamSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
auto AddTeamCheats = [](UCheatManager* CheatManager)
{
CheatManager->AddCheatManagerExtension(NewObject<ULyraTeamCheats>(CheatManager));
};
CheatManagerRegistrationHandle = UCheatManager::RegisterForOnCheatManagerCreated(FOnCheatManagerCreated::FDelegate::CreateLambda(AddTeamCheats));
}
void ULyraTeamSubsystem::Deinitialize()
{
UCheatManager::UnregisterFromOnCheatManagerCreated(CheatManagerRegistrationHandle);
Super::Deinitialize();
}
void ULyraTeamSubsystem::RegisterTeamInfo(ALyraTeamInfoBase* TeamInfo)
{
check(TeamInfo);
const int32 TeamId = TeamInfo->GetTeamId();
check(TeamId != INDEX_NONE);
FLyraTeamTrackingInfo& Entry = TeamMap.FindOrAdd(TeamId);
Entry.SetTeamInfo(TeamInfo);
}
void ULyraTeamSubsystem::UnregisterTeamInfo(ALyraTeamInfoBase* TeamInfo)
{
check(TeamInfo);
const int32 TeamId = TeamInfo->GetTeamId();
check(TeamId != INDEX_NONE);
FLyraTeamTrackingInfo& Entry = TeamMap.FindChecked(TeamId);
Entry.RemoveTeamInfo(TeamInfo);
}
bool ULyraTeamSubsystem::ChangeTeamForActor(AActor* ActorToChange, int32 NewTeamIndex)
{
const FGenericTeamId NewTeamID = IntegerToGenericTeamId(NewTeamIndex);
if (ALyraPlayerState* LyraPS = const_cast<ALyraPlayerState*>(FindPlayerStateFromActor(ActorToChange)))
{
LyraPS->SetGenericTeamId(NewTeamID);
return true;
}
else if (ILyraTeamAgentInterface* TeamActor = Cast<ILyraTeamAgentInterface>(ActorToChange))
{
TeamActor->SetGenericTeamId(NewTeamID);
return true;
}
else
{
return false;
}
}
int32 ULyraTeamSubsystem::FindTeamFromObject(const UObject* TestObject) const
{
// See if it's directly a team agent
if (const ILyraTeamAgentInterface* ObjectWithTeamInterface = Cast<ILyraTeamAgentInterface>(TestObject))
{
return GenericTeamIdToInteger(ObjectWithTeamInterface->GetGenericTeamId());
}
if (const AActor* TestActor = Cast<const AActor>(TestObject))
{
// See if the instigator is a team actor
if (const ILyraTeamAgentInterface* InstigatorWithTeamInterface = Cast<ILyraTeamAgentInterface>(TestActor->GetInstigator()))
{
return GenericTeamIdToInteger(InstigatorWithTeamInterface->GetGenericTeamId());
}
// Fall back to finding the associated player state
if (const ALyraPlayerState* LyraPS = FindPlayerStateFromActor(TestActor))
{
return LyraPS->GetTeamId();
}
}
return INDEX_NONE;
}
const ALyraPlayerState* ULyraTeamSubsystem::FindPlayerStateFromActor(const AActor* PossibleTeamActor) const
{
if (PossibleTeamActor != nullptr)
{
if (const APawn* Pawn = Cast<const APawn>(PossibleTeamActor))
{
//@TODO: Consider an interface instead or have team actors register with the subsystem and have it maintain a map? (or LWC style)
if (ALyraPlayerState* LyraPS = Pawn->GetPlayerState<ALyraPlayerState>())
{
return LyraPS;
}
}
else if (const AController* PC = Cast<const AController>(PossibleTeamActor))
{
if (ALyraPlayerState* LyraPS = Cast<ALyraPlayerState>(PC->PlayerState))
{
return LyraPS;
}
}
else if (const ALyraPlayerState* LyraPS = Cast<const ALyraPlayerState>(PossibleTeamActor))
{
return LyraPS;
}
// Try the instigator
// if (AActor* Instigator = PossibleTeamActor->GetInstigator())
// {
// if (ensure(Instigator != PossibleTeamActor))
// {
// return FindPlayerStateFromActor(Instigator);
// }
// }
}
return nullptr;
}
ELyraTeamComparison ULyraTeamSubsystem::CompareTeams(const UObject* A, const UObject* B, int32& TeamIdA, int32& TeamIdB) const
{
TeamIdA = FindTeamFromObject(Cast<const AActor>(A));
TeamIdB = FindTeamFromObject(Cast<const AActor>(B));
if ((TeamIdA == INDEX_NONE) || (TeamIdB == INDEX_NONE))
{
return ELyraTeamComparison::InvalidArgument;
}
else
{
return (TeamIdA == TeamIdB) ? ELyraTeamComparison::OnSameTeam : ELyraTeamComparison::DifferentTeams;
}
}
ELyraTeamComparison ULyraTeamSubsystem::CompareTeams(const UObject* A, const UObject* B) const
{
int32 TeamIdA;
int32 TeamIdB;
return CompareTeams(A, B, /*out*/ TeamIdA, /*out*/ TeamIdB);
}
void ULyraTeamSubsystem::FindTeamFromActor(const UObject* TestObject, bool& bIsPartOfTeam, int32& TeamId) const
{
TeamId = FindTeamFromObject(TestObject);
bIsPartOfTeam = TeamId != INDEX_NONE;
}
void ULyraTeamSubsystem::AddTeamTagStack(int32 TeamId, FGameplayTag Tag, int32 StackCount)
{
auto FailureHandler = [&](const FString& ErrorMessage)
{
UE_LOG(LogLyraTeams, Error, TEXT("AddTeamTagStack(TeamId: %d, Tag: %s, StackCount: %d) %s"), TeamId, *Tag.ToString(), StackCount, *ErrorMessage);
};
if (FLyraTeamTrackingInfo* Entry = TeamMap.Find(TeamId))
{
if (Entry->PublicInfo)
{
if (Entry->PublicInfo->HasAuthority())
{
Entry->PublicInfo->TeamTags.AddStack(Tag, StackCount);
}
else
{
FailureHandler(TEXT("failed because it was called on a client"));
}
}
else
{
FailureHandler(TEXT("failed because there is no team info spawned yet (called too early, before the experience was ready)"));
}
}
else
{
FailureHandler(TEXT("failed because it was passed an unknown team id"));
}
}
void ULyraTeamSubsystem::RemoveTeamTagStack(int32 TeamId, FGameplayTag Tag, int32 StackCount)
{
auto FailureHandler = [&](const FString& ErrorMessage)
{
UE_LOG(LogLyraTeams, Error, TEXT("RemoveTeamTagStack(TeamId: %d, Tag: %s, StackCount: %d) %s"), TeamId, *Tag.ToString(), StackCount, *ErrorMessage);
};
if (FLyraTeamTrackingInfo* Entry = TeamMap.Find(TeamId))
{
if (Entry->PublicInfo)
{
if (Entry->PublicInfo->HasAuthority())
{
Entry->PublicInfo->TeamTags.RemoveStack(Tag, StackCount);
}
else
{
FailureHandler(TEXT("failed because it was called on a client"));
}
}
else
{
FailureHandler(TEXT("failed because there is no team info spawned yet (called too early, before the experience was ready)"));
}
}
else
{
FailureHandler(TEXT("failed because it was passed an unknown team id"));
}
}
int32 ULyraTeamSubsystem::GetTeamTagStackCount(int32 TeamId, FGameplayTag Tag) const
{
if (const FLyraTeamTrackingInfo* Entry = TeamMap.Find(TeamId))
{
const int32 PublicStackCount = (Entry->PublicInfo != nullptr) ? Entry->PublicInfo->TeamTags.GetStackCount(Tag) : 0;
const int32 PrivateStackCount = (Entry->PrivateInfo != nullptr) ? Entry->PrivateInfo->TeamTags.GetStackCount(Tag) : 0;
return PublicStackCount + PrivateStackCount;
}
else
{
UE_LOG(LogLyraTeams, Verbose, TEXT("GetTeamTagStackCount(TeamId: %d, Tag: %s) failed because it was passed an unknown team id"), TeamId, *Tag.ToString());
return 0;
}
}
bool ULyraTeamSubsystem::TeamHasTag(int32 TeamId, FGameplayTag Tag) const
{
return GetTeamTagStackCount(TeamId, Tag) > 0;
}
bool ULyraTeamSubsystem::DoesTeamExist(int32 TeamId) const
{
return TeamMap.Contains(TeamId);
}
TArray<int32> ULyraTeamSubsystem::GetTeamIDs() const
{
TArray<int32> Result;
TeamMap.GenerateKeyArray(Result);
Result.Sort();
return Result;
}
bool ULyraTeamSubsystem::CanCauseDamage(const UObject* Instigator, const UObject* Target, bool bAllowDamageToSelf) const
{
if (bAllowDamageToSelf)
{
if ((Instigator == Target) || (FindPlayerStateFromActor(Cast<AActor>(Instigator)) == FindPlayerStateFromActor(Cast<AActor>(Target))))
{
return true;
}
}
int32 InstigatorTeamId;
int32 TargetTeamId;
const ELyraTeamComparison Relationship = CompareTeams(Instigator, Target, /*out*/ InstigatorTeamId, /*out*/ TargetTeamId);
if (Relationship == ELyraTeamComparison::DifferentTeams)
{
return true;
}
else if ((Relationship == ELyraTeamComparison::InvalidArgument) && (InstigatorTeamId != INDEX_NONE))
{
// Allow damaging non-team actors for now, as long as they have an ability system component
//@TODO: This is temporary until the target practice dummy has a team assignment
return UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(Cast<const AActor>(Target)) != nullptr;
}
return false;
}
ULyraTeamDisplayAsset* ULyraTeamSubsystem::GetTeamDisplayAsset(int32 TeamId, int32 ViewerTeamId)
{
// Currently ignoring ViewerTeamId
if (FLyraTeamTrackingInfo* Entry = TeamMap.Find(TeamId))
{
return Entry->DisplayAsset;
}
return nullptr;
}
ULyraTeamDisplayAsset* ULyraTeamSubsystem::GetEffectiveTeamDisplayAsset(int32 TeamId, UObject* ViewerTeamAgent)
{
return GetTeamDisplayAsset(TeamId, FindTeamFromObject(ViewerTeamAgent));
}
void ULyraTeamSubsystem::NotifyTeamDisplayAssetModified(ULyraTeamDisplayAsset* /*ModifiedAsset*/)
{
// Broadcasting to all observers when a display asset is edited right now, instead of only the edited one
for (const auto& KVP : TeamMap)
{
const int32 TeamId = KVP.Key;
const FLyraTeamTrackingInfo& TrackingInfo = KVP.Value;
TrackingInfo.OnTeamDisplayAssetChanged.Broadcast(TrackingInfo.DisplayAsset);
}
}
FOnLyraTeamDisplayAssetChangedDelegate& ULyraTeamSubsystem::GetTeamDisplayAssetChangedDelegate(int32 TeamId)
{
return TeamMap.FindOrAdd(TeamId).OnTeamDisplayAssetChanged;
}