haxis/Source/UnrealProject/GameState/DefaultGameMode.cpp

382 lines
10 KiB
C++

// Project Lab - NHTV Igad
#include "UnrealProject.h"
#include "DefaultGameMode.h"
#include "DefaultGameState.h"
#include "DefaultGameInstance.h"
#include "PlayerSpawn.h"
#include "NetworkPlayer.h"
#include "CreatureSpawn.h"
#include "DefaultPlayer.h"
#include "DefaultPlayerController.h"
#include "DefaultPlayerState.h"
#include "GameStateBase.h"
#include "SessionManager.h"
#if PLATFORM_SPECIFIC_WIN == 0
#include "HeatMapMetrics.h"
#endif
#if UE_INCLUDE_METRICS
#include "MiniMapVolume.h"
#include "IngameHUD.h"
#include "MiniMapWidget.h"
#endif
ADefaultGameMode::ADefaultGameMode()
{
PrimaryActorTick.bCanEverTick = true;
// Lobby system starts the match
bDelayedStart = true;
PlayerControllerClass = ADefaultPlayerController::StaticClass();
DefaultPawnClass = ConstructorHelpers::FClassFinder<APawn>(TEXT("/Game/Assets/Blueprints/BP_DefaultPlayer")).Class;
SpectatorClass = ConstructorHelpers::FClassFinder<ASpectatorPawn>(TEXT("/Game/Assets/Blueprints/BP_SpectatorPawn")).Class;
GameStateClass = ADefaultGameState::StaticClass();
PlayerStateClass = ADefaultPlayerState::StaticClass();
}
void ADefaultGameMode::PreInitializeComponents()
{
Super::PreInitializeComponents();
ADefaultGameState* gameState = GetGameState<ADefaultGameState>();
check(gameState);
m_playerSpawns.AddZeroed(4);
//FString str = UGameplayStatics::ParseOption(OptionsString, FString("teamCount"));
//int32 teamCountSetting = FCString::Atoi(*str);
//if(teamCountSetting > 1)
//{
// gameState->m_mapTeamCount = teamCountSetting;
// GWPRINT(L"Set map team count to " + gameState->m_mapTeamCount);
//}
}
void ADefaultGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
Super::InitGame(MapName, Options, ErrorMessage);
// Start game immediately in editor since we can't use seamless travel to obtain the players
UWorld* world = GetWorld();
if(world->WorldType == EWorldType::PIE)
{
m_StartGameStartTimer();
}
// Find lobby state blueprint
for(TActorIterator<ALobbyState> it(world); it; ++it)
{
m_lobbyState = *it;
}
if(m_lobbyState)
{
GPRINT("Received traveled lobby state " + m_lobbyState->GetName());
m_totalPlayerCount = m_lobbyState->players.Num();
}
else
{
m_totalPlayerCount = 1;
}
}
void ADefaultGameMode::BeginPlay()
{
Super::BeginPlay();
#if PLATFORM_SPECIFIC_WIN == 0
HeatMapMetrics::ResetMap();
#endif
}
void ADefaultGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
#if UE_INCLUDE_METRICS
if (Metrics::EndSession())
JPRINT("Metrics session ended");
else
JERROR("Failed to close metrics session");
#endif
Super::EndPlay(EndPlayReason);
#if PLATFORM_SPECIFIC_WIN == 0
HeatMapMetrics::ExportToFile();
#endif
}
void ADefaultGameMode::PostSeamlessTravel()
{
GPRINT("PostSeamlessTravel");
Super::PostSeamlessTravel();
}
void ADefaultGameMode::HandleSeamlessTravelPlayer(AController*& C)
{
Super::HandleSeamlessTravelPlayer(C);
UDefaultGameInstance* inst = Cast<UDefaultGameInstance>(GetGameInstance());
// Register new player
ADefaultPlayerController* newPC = Cast<ADefaultPlayerController>(C);
if(!newPC)
{
GERROR("Invalid player genereted by HandleSeamlessTravelPlayer");
}
else
{
GPRINT("Transfering logged in player " + C->GetName());
AGameStateBase* gameState = Cast<AGameStateBase>(GameState);
gameState->RegisterPlayer(newPC);
}
TSharedPtr<const FUniqueNetId> netID = newPC->PlayerState->UniqueId.GetUniqueNetId();
if(m_lobbyState)
{
m_lobbyState->players.RemoveAll([netID](FLobbyStatePlayer& item)
{
return item.netID.IsValid() && netID.IsValid() && (*item.netID == *netID);
});
}
m_numTravelledPlayers++;
if(m_numTravelledPlayers >= m_totalPlayerCount)
{
// Set a timer to start the game
m_StartGameStartTimer();
}
else
{
int32 left = m_totalPlayerCount - m_numTravelledPlayers;
GPRINT("Still waiting for " + left + " more players");
for(auto& p : m_lobbyState->players)
{
FString name = inst->sessionManager->GetPlayerName(*p.netID);
GPRINT("--" + name + " [" + p.netID->ToDebugString() + "]");
}
}
}
void ADefaultGameMode::PreLogin(const FString& Options, const FString& Address, const TSharedPtr<const FUniqueNetId>& UniqueId, FString& ErrorMessage)
{
Super::PreLogin(Options, Address, UniqueId, ErrorMessage);
FString username = "Invalid";
if(GameState->GetMatchState() == MatchState::InProgress)
{
// Check if player is reconnecting but was previously connected
FString uniqueIDString = UniqueId->ToString();
if (m_connectedPlayersNetID.Contains(uniqueIDString))
{
GWPRINT(L"Reconnecting player " + username + " / " + uniqueIDString);
}
else
{
ErrorMessage = "Game has already started";
}
}
}
APlayerController* ADefaultGameMode::Login(class UPlayer* NewPlayer, ENetRole InRemoteRole, const FString& Portal, const FString& Options, const TSharedPtr<const FUniqueNetId>& UniqueId, FString& ErrorMessage)
{
// Register players that ever connected to the game so they can reconnect
APlayerController* controller = Super::Login(NewPlayer, InRemoteRole, Portal, Options, UniqueId, ErrorMessage);
if (controller)
{
FString uniqueIDString = UniqueId->ToString();
m_connectedPlayersNetID.Add(uniqueIDString);
GWPRINT(L"Registering connected player ID " + uniqueIDString);
}
// Register logged on player
// Note: this should only happen when running a PIE instance or standalone with a startup level
// normal login procedure happens in the menu and players are then transfered to the game using seamless travel
ADefaultPlayerController* defPC = Cast<ADefaultPlayerController>(controller);
if(!defPC)
{
GERROR("Invalid player genereted by HandleSeamlessTravelPlayer");
}
else
{
GPRINT("Logging in new player " + defPC->GetName());
AGameStateBase* gameState = Cast<AGameStateBase>(GameState);
gameState->RegisterPlayer(defPC);
}
return controller;
}
void ADefaultGameMode::PostLogin(APlayerController* NewPlayer)
{
Super::PostLogin(NewPlayer);
// Auto assign team when in PIE
UWorld* world = GetWorld();
if(world->WorldType == EWorldType::PIE)
{
AGameStateBase* gameState = Cast<AGameStateBase>(GameState);
APlayerStateBase* state = Cast<APlayerStateBase>(NewPlayer->PlayerState);
state->AutoAssignTeam();
}
}
void ADefaultGameMode::Logout(AController* Exiting)
{
GWPRINT(L"Player left the game");
// Unregister players if not playing
if (GameState->GetMatchState() != MatchState::InProgress)
{
if (Exiting->PlayerState)
{
FString uniqueIDString = Exiting->PlayerState->UniqueId->ToString();
GWPRINT(L"Removing connected player ID " + uniqueIDString);
m_connectedPlayersNetID.Remove(uniqueIDString);
}
}
Super::Logout(Exiting);
}
void ADefaultGameMode::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
m_gameTimer += DeltaTime;
}
void ADefaultGameMode::HandleMatchHasStarted()
{
GWPRINT(L"Match Started");
GetGameState<ADefaultGameState>()->gameEnded = false;
// Assign team mates
ADefaultGameState* gameState = GetWorld()->GetGameState<ADefaultGameState>();
TArray<TArray<ADefaultPlayerState*>> playersByTeam = gameState->GetPlayersByTeam<ADefaultPlayerState>();
for (int32 i = 0; i < playersByTeam.Num(); i++)
{
auto& arr = playersByTeam[i];
if (arr.Num() == 2)
{
arr[0]->teamMate = arr[1];
arr[1]->teamMate = arr[0];
}
}
Super::HandleMatchHasStarted();
}
void ADefaultGameMode::HandleMatchHasEnded()
{
GWPRINT(L"Match Ended");
GetGameState<ADefaultGameState>()->gameEnded = true;
Super::HandleMatchHasStarted();
}
void ADefaultGameMode::RegisterPlayerSpawn(APlayerSpawn& spawn)
{
if (spawn.assignedTeam > m_playerSpawns.Num() - 1)
m_playerSpawns.SetNumZeroed(spawn.assignedTeam + 1);
m_playerSpawns[spawn.assignedTeam] = &spawn;
}
void ADefaultGameMode::RegisterPlayer(class ANetworkPlayer& player)
{
if(m_players.Contains(&player))
{
GWARNING("Player already registered with game mode " + player.GetName());
return;
}
m_players.Add(&player);
}
void ADefaultGameMode::UnregisterPlayer(class ANetworkPlayer& player)
{
m_players.Remove(&player);
}
void ADefaultGameMode::StartGame()
{
#if UE_INCLUDE_METRICS
// Start a new metrics session, and output the file in the Metrics folder
FString filePath = FPaths::GameDir() + FString(L"Metrics/");
if (Metrics::StartSession(filePath.GetCharArray().GetData()))
{
// Retrieve the active map size
for (TActorIterator<AMiniMapVolume> iter(GetWorld()); iter; ++iter)
{
AMiniMapVolume* const volume = *iter;
volume->SetActorRotation(FRotator(0, 0, 0));
FVector area = volume->area->GetScaledBoxExtent();
const FVector pos = volume->GetActorLocation();
FVector2D min, max;
min.X = -area.X + pos.X;
min.Y = -area.Y + pos.Y;
max.X = area.X + pos.X;
max.Y = area.Y + pos.Y;
Metrics::SetMapData(min.X, max.X, min.Y, max.Y);
break;
}
JPRINT("New metrics session started");
}
else
JERROR("Failed to open new metrics session");
#endif
GPRINT("Starting Game");
SetMatchState(MatchState::InProgress);
m_gameTimer = 0;
}
APlayerSpawn* ADefaultGameMode::GetOptimalSpawn(int32 team)
{
check(team > 0);
if (m_playerSpawns.Num() == 0)
return nullptr;
if (m_playerSpawns.Num() == 1)
return m_playerSpawns[0];
if (team - 1 < m_playerSpawns.Num())
return m_playerSpawns[team - 1];
// Either no players yet, or something went wrong
// Pick random spawn
return m_playerSpawns[FMath::Rand() % m_playerSpawns.Num()];
}
TArray<class ANetworkPlayer*> ADefaultGameMode::GetPlayers()
{
return m_players;
}
void ADefaultGameMode::BossHasBeenKilled(int32 team)
{
TArray<class ANetworkCharacter*> players(m_players);
// Set winning team in gamestate (replicated)
GetGameState<ADefaultGameState>()->gameWinningTeam = team;
EndMatch();
// Destroy all player pawns so that they respawn as spectators
for (int32 i = 0; i < players.Num(); i++)
{
players[i]->Destroy(true);
}
TPRINT("Boss was killed. (ADefaultGameMode) the team = " + team);
}
float ADefaultGameMode::GetGameTime()
{
return m_gameTimer;
}
void ADefaultGameMode::m_StartGameStartTimer()
{
GPRINT("Starting game in 2 seconds");
GetWorld()->GetTimerManager().SetTimer(m_gameStartTimer, this, &ADefaultGameMode::m_GameStart, 2.0f, false);
}
void ADefaultGameMode::m_GameStart()
{
StartGame();
}