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