213 lines
5.7 KiB
C++
213 lines
5.7 KiB
C++
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||
|
|
||
|
#include "LyraPlayerSpawningManagerComponent.h"
|
||
|
#include "Engine/World.h"
|
||
|
#include "GameFramework/Controller.h"
|
||
|
#include "GameFramework/GameState.h"
|
||
|
#include "GameFramework/PlayerController.h"
|
||
|
#include "GameFramework/PlayerState.h"
|
||
|
#include "GameFramework/PlayerStart.h"
|
||
|
#include "EngineUtils.h"
|
||
|
#include "Engine/PlayerStartPIE.h"
|
||
|
#include "LyraPlayerStart.h"
|
||
|
|
||
|
DEFINE_LOG_CATEGORY_STATIC(LogPlayerSpawning, Log, All);
|
||
|
|
||
|
ULyraPlayerSpawningManagerComponent::ULyraPlayerSpawningManagerComponent(const FObjectInitializer& ObjectInitializer)
|
||
|
: Super(ObjectInitializer)
|
||
|
{
|
||
|
SetIsReplicatedByDefault(false);
|
||
|
bAutoRegister = true;
|
||
|
bAutoActivate = true;
|
||
|
bWantsInitializeComponent = true;
|
||
|
PrimaryComponentTick.TickGroup = TG_PrePhysics;
|
||
|
PrimaryComponentTick.bCanEverTick = true;
|
||
|
PrimaryComponentTick.bAllowTickOnDedicatedServer = true;
|
||
|
PrimaryComponentTick.bStartWithTickEnabled = false;
|
||
|
}
|
||
|
|
||
|
void ULyraPlayerSpawningManagerComponent::InitializeComponent()
|
||
|
{
|
||
|
Super::InitializeComponent();
|
||
|
|
||
|
FWorldDelegates::LevelAddedToWorld.AddUObject(this, &ThisClass::OnLevelAdded);
|
||
|
|
||
|
UWorld* World = GetWorld();
|
||
|
World->AddOnActorSpawnedHandler(FOnActorSpawned::FDelegate::CreateUObject(this, &ThisClass::HandleOnActorSpawned));
|
||
|
|
||
|
for (TActorIterator<ALyraPlayerStart> It(World); It; ++It)
|
||
|
{
|
||
|
if (ALyraPlayerStart* PlayerStart = *It)
|
||
|
{
|
||
|
CachedPlayerStarts.Add(PlayerStart);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ULyraPlayerSpawningManagerComponent::OnLevelAdded(ULevel* InLevel, UWorld* InWorld)
|
||
|
{
|
||
|
if (InWorld == GetWorld())
|
||
|
{
|
||
|
for (AActor* Actor : InLevel->Actors)
|
||
|
{
|
||
|
if (ALyraPlayerStart* PlayerStart = Cast<ALyraPlayerStart>(Actor))
|
||
|
{
|
||
|
ensure(!CachedPlayerStarts.Contains(PlayerStart));
|
||
|
CachedPlayerStarts.Add(PlayerStart);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ULyraPlayerSpawningManagerComponent::HandleOnActorSpawned(AActor* SpawnedActor)
|
||
|
{
|
||
|
if (ALyraPlayerStart* PlayerStart = Cast<ALyraPlayerStart>(SpawnedActor))
|
||
|
{
|
||
|
CachedPlayerStarts.Add(PlayerStart);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ALyraGameMode Proxied Calls - Need to handle when someone chooses
|
||
|
// to restart a player the normal way in the engine.
|
||
|
//======================================================================
|
||
|
|
||
|
AActor* ULyraPlayerSpawningManagerComponent::ChoosePlayerStart(AController* Player)
|
||
|
{
|
||
|
if (Player)
|
||
|
{
|
||
|
#if WITH_EDITOR
|
||
|
if (APlayerStart* PlayerStart = FindPlayFromHereStart(Player))
|
||
|
{
|
||
|
return PlayerStart;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
TArray<ALyraPlayerStart*> StarterPoints;
|
||
|
for (auto StartIt = CachedPlayerStarts.CreateIterator(); StartIt; ++StartIt)
|
||
|
{
|
||
|
if (ALyraPlayerStart* Start = (*StartIt).Get())
|
||
|
{
|
||
|
StarterPoints.Add(Start);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
StartIt.RemoveCurrent();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (APlayerState* PlayerState = Player->GetPlayerState<APlayerState>())
|
||
|
{
|
||
|
// start dedicated spectators at any random starting location, but they do not claim it
|
||
|
if (PlayerState->IsOnlyASpectator())
|
||
|
{
|
||
|
if (!StarterPoints.IsEmpty())
|
||
|
{
|
||
|
return StarterPoints[FMath::RandRange(0, StarterPoints.Num() - 1)];
|
||
|
}
|
||
|
|
||
|
return nullptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
AActor* PlayerStart = OnChoosePlayerStart(Player, StarterPoints);
|
||
|
|
||
|
if (!PlayerStart)
|
||
|
{
|
||
|
PlayerStart = GetFirstRandomUnoccupiedPlayerStart(Player, StarterPoints);
|
||
|
}
|
||
|
|
||
|
if (ALyraPlayerStart* LyraStart = Cast<ALyraPlayerStart>(PlayerStart))
|
||
|
{
|
||
|
LyraStart->TryClaim(Player);
|
||
|
}
|
||
|
|
||
|
return PlayerStart;
|
||
|
}
|
||
|
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
#if WITH_EDITOR
|
||
|
APlayerStart* ULyraPlayerSpawningManagerComponent::FindPlayFromHereStart(AController* Player)
|
||
|
{
|
||
|
// Only 'Play From Here' for a player controller, bots etc. should all spawn from normal spawn points.
|
||
|
if (Player->IsA<APlayerController>())
|
||
|
{
|
||
|
if (UWorld* World = GetWorld())
|
||
|
{
|
||
|
for (TActorIterator<APlayerStart> It(World); It; ++It)
|
||
|
{
|
||
|
if (APlayerStart* PlayerStart = *It)
|
||
|
{
|
||
|
if (PlayerStart->IsA<APlayerStartPIE>())
|
||
|
{
|
||
|
// Always prefer the first "Play from Here" PlayerStart, if we find one while in PIE mode
|
||
|
return PlayerStart;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nullptr;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
bool ULyraPlayerSpawningManagerComponent::ControllerCanRestart(AController* Player)
|
||
|
{
|
||
|
bool bCanRestart = true;
|
||
|
|
||
|
// TODO Can they restart?
|
||
|
|
||
|
return bCanRestart;
|
||
|
}
|
||
|
|
||
|
void ULyraPlayerSpawningManagerComponent::FinishRestartPlayer(AController* NewPlayer, const FRotator& StartRotation)
|
||
|
{
|
||
|
OnFinishRestartPlayer(NewPlayer, StartRotation);
|
||
|
K2_OnFinishRestartPlayer(NewPlayer, StartRotation);
|
||
|
}
|
||
|
|
||
|
//================================================================
|
||
|
|
||
|
void ULyraPlayerSpawningManagerComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||
|
{
|
||
|
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||
|
}
|
||
|
|
||
|
APlayerStart* ULyraPlayerSpawningManagerComponent::GetFirstRandomUnoccupiedPlayerStart(AController* Controller, const TArray<ALyraPlayerStart*>& StartPoints) const
|
||
|
{
|
||
|
if (Controller)
|
||
|
{
|
||
|
TArray<ALyraPlayerStart*> UnOccupiedStartPoints;
|
||
|
TArray<ALyraPlayerStart*> OccupiedStartPoints;
|
||
|
|
||
|
for (ALyraPlayerStart* StartPoint : StartPoints)
|
||
|
{
|
||
|
ELyraPlayerStartLocationOccupancy State = StartPoint->GetLocationOccupancy(Controller);
|
||
|
|
||
|
switch (State)
|
||
|
{
|
||
|
case ELyraPlayerStartLocationOccupancy::Empty:
|
||
|
UnOccupiedStartPoints.Add(StartPoint);
|
||
|
break;
|
||
|
case ELyraPlayerStartLocationOccupancy::Partial:
|
||
|
OccupiedStartPoints.Add(StartPoint);
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (UnOccupiedStartPoints.Num() > 0)
|
||
|
{
|
||
|
return UnOccupiedStartPoints[FMath::RandRange(0, UnOccupiedStartPoints.Num() - 1)];
|
||
|
}
|
||
|
else if (OccupiedStartPoints.Num() > 0)
|
||
|
{
|
||
|
return OccupiedStartPoints[FMath::RandRange(0, OccupiedStartPoints.Num() - 1)];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nullptr;
|
||
|
}
|