GravityStomp/GravityStomp/Source/GravityStompGame/GameModes/GSGameModeBase.cpp

302 lines
8.0 KiB
C++

// Gravity Stomp Copyright Kevin Poretti
#include "GSGameModeBase.h"
// GS includes
#include "EngineUtils.h"
#include "GSGameState.h"
#include "Player/GSPlayerController.h"
#include "Player/GSPlayerState.h"
AGSGameModeBase::AGSGameModeBase()
{
ScoreLimit = 25;
MaxNumTeams = 2;
TimeBetweenRounds = 20.0f;
RoundTime = 600.0f;
WarmupTime = 15.0f;
bDelayedStart = true;
PlayerControllerClass = AGSPlayerController::StaticClass();
PlayerStateClass = AGSPlayerState::StaticClass();
GameStateClass = AGSGameState::StaticClass();
MinRespawnDelay = 5.0f;
}
void AGSGameModeBase::PreInitializeComponents()
{
Super::PreInitializeComponents();
// Setup timer for match tick
GetWorldTimerManager().SetTimer(TimerHandle_DefaultTimer, this, &AGSGameModeBase::DefaultTimer, GetWorldSettings()->GetEffectiveTimeDilation(), true);
}
void AGSGameModeBase::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
Super::InitGame(MapName, Options, ErrorMessage);
bUseSeamlessTravel = !GetWorld()->IsPlayInEditor();
}
void AGSGameModeBase::InitGameState()
{
Super::InitGameState();
AGSGameState* GS = GetGameState<AGSGameState>();
check(GS)
GS->SetNumTeams(MaxNumTeams);
}
void AGSGameModeBase::PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId,
FString& ErrorMessage)
{
// reject player is match has ended
AGSGameState* GS = GetGameState<AGSGameState>();
bool bMatchEnded = GS && GS->HasMatchEnded();
if(bMatchEnded)
{
ErrorMessage = TEXT("Can't join a match that has ended.");
}
else
{
Super::PreLogin(Options, Address, UniqueId, ErrorMessage);
}
}
void AGSGameModeBase::PostLogin(APlayerController* NewPlayer)
{
Super::PostLogin(NewPlayer);
PickTeam(NewPlayer->GetPlayerState<AGSPlayerState>());
AGSPlayerController* PC = Cast<AGSPlayerController>(NewPlayer);
if(PC && IsMatchInProgress())
{
PC->Client_GameStarted();
}
}
void AGSGameModeBase::RestartPlayer(AController* NewPlayer)
{
Super::RestartPlayer(NewPlayer);
// inform player controller that game has started using client RPC
AGSPlayerController* PC = Cast<AGSPlayerController>(NewPlayer);
if(PC && IsMatchInProgress())
{
PC->Client_GameStarted();
}
}
bool AGSGameModeBase::ShouldSpawnAtStartSpot(AController* Player)
{
return false;
}
bool AGSGameModeBase::CanDamagePlayer(AGSPlayerController* Damager, AGSPlayerController* Victim)
{
AGSPlayerState* DamagerPS = Damager ? Damager->GetPlayerState<AGSPlayerState>() : nullptr;
AGSPlayerState* VictimPS = Victim ? Victim->GetPlayerState<AGSPlayerState>() : nullptr;
// can damage if both instigator and victim are valid and are on different teams
return DamagerPS && VictimPS && (DamagerPS->GetTeamNum() != VictimPS->GetTeamNum());
}
void AGSGameModeBase::PickTeam(AGSPlayerState* PlayerState)
{
PlayerState->SetTeamNum(PlayerState->GetPlayerController()->GetLocalPlayer()->GetControllerId());
}
void AGSGameModeBase::OnPlayerKilled(AGSPlayerController* Killer, AGSPlayerController* Victim)
{
// Only keep track of kills in an active round
if(MatchState != MatchState::InProgress)
{
return;
}
AGSPlayerState* KillerPS = Killer ? Killer->GetPlayerState<AGSPlayerState>() : nullptr;
AGSPlayerState* VictimPS = Victim ? Victim->GetPlayerState<AGSPlayerState>() : nullptr;
if(KillerPS && VictimPS)
{
// keep track of player's K/D
KillerPS->IncrementNumKills();
Killer->Client_OnKill(KillerPS, VictimPS);
VictimPS->IncrementNumDeaths();
// inform all players of the death
for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
{
AGSPlayerController* PC = Cast<AGSPlayerController>(*It);
if(PC)
{
PC->Client_OnDeath(KillerPS, VictimPS);
}
}
// give killer's team a point and check if score limit is reached
AGSGameState* GS = GetGameState<AGSGameState>();
check(GS);
int32 NewScore = GS->GiveTeamPoint(KillerPS->GetTeamNum());
if(NewScore >= ScoreLimit)
{
FinishMatch();
}
}
}
void AGSGameModeBase::DefaultTimer()
{
AGSGameState* GS = GetGameState<AGSGameState>();
if(GS && GS->GetRemainingTime() > 0)
{
GS->SetRemainingTime(GS->GetRemainingTime() - 1);
if(GS->GetRemainingTime() <= 0)
{
if(GetMatchState() == MatchState::WaitingPostMatch)
{
RestartGame();
}
else if (GetMatchState() == MatchState::InProgress)
{
FinishMatch();
}
else if (GetMatchState() == MatchState::WaitingToStart)
{
StartMatch();
}
}
}
}
void AGSGameModeBase::FinishMatch()
{
if(IsMatchInProgress())
{
EndMatch();
// determine match winner
AGSGameState* GS = GetGameState<AGSGameState>();
check(GS);
int32 WinningTeamIdx = GS->GetWinningTeam();
// notify players that game has ended and notify winning players that they won
for(FConstPlayerControllerIterator PCIt = GetWorld()->GetPlayerControllerIterator(); PCIt; ++PCIt)
{
// notify player if they won
APlayerController* PC = PCIt->Get();
PC->GameHasEnded(nullptr, PC->GetPlayerState<AGSPlayerState>()->GetTeamNum() == WinningTeamIdx);
}
// turn off all pawns
for (APawn* Pawn : TActorRange<APawn>(GetWorld()))
{
Pawn->TurnOff();
}
// set game state remaining time to time between matches
GS->SetRemainingTime(TimeBetweenRounds);
GS->ResetTeamScores();
}
}
void AGSGameModeBase::HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer)
{
// If players should start as spectators, leave them in the spectator state
if (!bStartPlayersAsSpectators && !MustSpectate(NewPlayer))
{
// If match is in progress, start the player
if ((GetMatchState() == MatchState::WaitingToStart || IsMatchInProgress()) && PlayerCanRestart(NewPlayer))
{
RestartPlayer(NewPlayer);
}
// Check to see if we should start right away, avoids a one frame lag in single player games
else if (GetMatchState() == MatchState::WaitingToStart)
{
// Check to see if we should start the match
if (ReadyToStartMatch())
{
StartMatch();
}
}
}
}
void AGSGameModeBase::HandleMatchIsWaitingToStart()
{
Super::HandleMatchIsWaitingToStart();
// setup warmup
AGSGameState* GS = GetGameState<AGSGameState>();
if(GS && GS->GetRemainingTime() <= 0)
{
if(bDelayedStart)
{
GS->SetRemainingTime(WarmupTime);
}
}
// notify players warmup period has started
for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
{
AGSPlayerController* PC = Cast<AGSPlayerController>(*It);
if(PC)
{
RestartPlayer(PC);
PC->Client_WarmupStarted();
}
}
}
void AGSGameModeBase::HandleMatchHasStarted()
{
Super::HandleMatchHasStarted();
// setup in progress match
AGSGameState* GS = GetGameState<AGSGameState>();
if(GS && GS->GetRemainingTime() <= 0)
{
GS->SetRemainingTime(RoundTime);
}
// notify players match has started
for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
{
AGSPlayerController* PC = Cast<AGSPlayerController>(*It);
if(PC)
{
PC->Client_GameStarted();
}
}
}
void AGSGameModeBase::RestartGame()
{
// notify players game has restarted
for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
{
AGSPlayerController* PC = Cast<AGSPlayerController>(*It);
if(PC)
{
PC->Client_GameRestarted();
}
}
Super::RestartGame();
}