382 lines
10 KiB
C++
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();
|
|
}
|