etqw-sdk/source/game/rules/GameRules_StopWatch.cpp

841 lines
22 KiB
C++
Raw Normal View History

2008-05-29 00:00:00 +00:00
// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "GameRules_StopWatch.h"
#include "../Player.h"
#include "../script/Script_Helper.h"
#include "../script/Script_ScriptObject.h"
#include "../guis/UserInterfaceLocal.h"
#include "../rules/VoteManager.h"
idCVar g_stopWatchMode( "g_stopWatchMode", "0", CVAR_GAME | CVAR_INTEGER, "stopwatch mode, 0 = ABBA, 1 = ABAB" );
sdGameRulesStopWatchNetworkState::sdGameRulesStopWatchNetworkState( void ) {
}
void sdGameRulesStopWatchNetworkState::MakeDefault( void ) {
sdGameRulesNetworkState::MakeDefault();
progression = 0;
timeToBeat = 0;
winReason = 0;
winningTeam = NULL;
}
void sdGameRulesStopWatchNetworkState::Write( idFile* file ) const {
sdGameRulesNetworkState::Write( file );
file->WriteInt( winningTeam ? winningTeam->GetIndex() : -1 );
file->WriteInt( progression );
file->WriteInt( timeToBeat );
file->WriteInt( winReason );
}
void sdGameRulesStopWatchNetworkState::Read( idFile* file ) {
sdGameRulesNetworkState::Read( file );
int winningTeamIndex;
file->ReadInt( winningTeamIndex );
winningTeam = winningTeamIndex == -1 ? NULL : &sdTeamManager::GetInstance().GetTeamByIndex( winningTeamIndex );
file->ReadInt( progression );
file->ReadInt( timeToBeat );
file->ReadInt( winReason );
}
/*
===============================================================================
sdGameRulesStopWatch
===============================================================================
*/
CLASS_DECLARATION( sdGameRules, sdGameRulesStopWatch )
END_CLASS
/*
================
sdGameRulesStopWatch::sdGameRulesStopWatch
================
*/
sdGameRulesStopWatch::sdGameRulesStopWatch( void ) : timeToBeat( 0 ), winningTeam( NULL ), firstMatchWinningTeam( NULL ), progression( GP_FIRST_MATCH ) {
for ( int i = 0; i < GP_MAX; i++ ) {
// pre-allocate plenty of objectives
objectiveCompletionTimes[ 0 ].PreAllocate( 8 );
objectiveCompletionTimes[ 1 ].PreAllocate( 8 );
attackingTeams[ i ] = NULL;
defendingTeams[ i ] = NULL;
attackingTeamXPs[ i ] = -1;
}
winReason = WR_NORMAL;
}
/*
================
sdGameRulesStopWatch::~sdGameRulesStopWatch
================
*/
sdGameRulesStopWatch::~sdGameRulesStopWatch( void ) {
}
/*
================
sdGameRulesStopWatch::GameState_Review
================
*/
void sdGameRulesStopWatch::GameState_Review( void ) {
if ( nextState != GS_INACTIVE ) {
return;
}
if ( si_gameReviewReadyWait.GetBool() ) {
if ( ArePlayersReady() != RS_READY ) {
return;
}
}
NextStateDelayed( GS_NEXTMAP, MINS2MS( g_gameReviewPause.GetFloat() ) );
}
/*
================
sdGameRulesStopWatch::GameState_NextGame
================
*/
void sdGameRulesStopWatch::GameState_NextGame( void ) {
if ( nextState != GS_INACTIVE ) {
return;
}
NewState( GS_WARMUP );
// put everyone back in from endgame spectate
CheckRespawns( true );
}
/*
================
sdGameRulesStopWatch::GameState_Warmup
================
*/
void sdGameRulesStopWatch::GameState_Warmup( void ) {
if ( !CanStartMatch() ) {
needsRestart = true;
return;
}
StartMatch();
}
/*
================
sdGameRulesStopWatch::GameState_Countdown
================
*/
void sdGameRulesStopWatch::GameState_Countdown( void ) {
}
/*
================
sdGameRulesStopWatch::GameState_GameOn
================
*/
void sdGameRulesStopWatch::GameState_GameOn( void ) {
if ( gameLocal.GameState() == GAMESTATE_STARTUP || nextState != GS_INACTIVE ) {
return;
}
int timeLimit = GetTimeLimit();
if ( timeLimit != 0 ) {
if ( gameLocal.time - matchStartedTime >= timeLimit ) {
OnTimeLimitHit();
EndGame();
}
}
}
/*
================
sdGameRulesStopWatch::GameState_NextMap
================
*/
void sdGameRulesStopWatch::GameState_NextMap( void ) {
BackupPlayerTeams();
switch ( progression ) {
case GP_FIRST_MATCH:
progression = GP_RETURN_MATCH;
firstMatchWinningTeam = winningTeam;
break;
case GP_RETURN_MATCH:
if ( gameLocal.NextMap() ) {
return;
}
firstMatchWinningTeam = NULL;
progression = GP_FIRST_MATCH;
timeToBeat = 0;
ResetTiebreakInfo();
winReason = WR_NORMAL;
break;
}
if( gameLocal.DoClientSideStuff() ) {
UpdateClientFromServerInfo( gameLocal.serverInfo, false );
}
winningTeam = NULL;
gameLocal.LocalMapRestart();
sdTeamManagerLocal& manager = sdTeamManager::GetInstance();
bool stopWatchAltMode = g_stopWatchMode.GetInteger() != 0;
sdProficiencyManager::GetInstance().ClearProficiency();
// cycle player teams and clear xp
for ( int i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer* player = gameLocal.GetClient( i );
if ( player == NULL ) {
continue;
}
sdTeamInfo* currentTeam = player->GetGameTeam();
if ( currentTeam == NULL ) {
continue;
}
player->Killed( NULL, NULL, 0, vec3_zero, -1, NULL );
if ( !stopWatchAltMode ) {
int newTeamIndex = ( currentTeam->GetIndex() + 1 ) % manager.GetNumTeams();
sdTeamInfo* newTeam = &manager.GetTeamByIndex( newTeamIndex );
const sdDeclPlayerClass* cls = player->GetInventory().GetClass();
if ( cls ) {
cls = currentTeam->GetEquivalentClass( *cls, *newTeam );
}
player->SetGameTeam( newTeam );
if ( cls ) {
player->GetInventory().GiveClass( cls, true );
}
}
player->ServerForceRespawn( true );
RestoreFireTeam( player );
}
NewState( GS_NEXTGAME );
}
/*
================
sdGameRulesStopWatch::OnGameState_Review
================
*/
void sdGameRulesStopWatch::OnGameState_Review( void ) {
if ( !gameLocal.isClient ) {
nextState = GS_INACTIVE; // used to abort a game. cancel out any upcoming state change
ClearPlayerReadyFlags();
}
}
/*
================
sdGameRulesStopWatch::OnGameState_Countdown
================
*/
void sdGameRulesStopWatch::OnGameState_Countdown( void ) {
}
/*
================
sdGameRulesStopWatch::OnGameState_GameOn
================
*/
void sdGameRulesStopWatch::OnGameState_GameOn( void ) {
if ( !gameLocal.isClient ) {
if ( needsRestart ) {
needsRestart = false;
gameLocal.LocalMapRestart();
}
matchStartedTime = gameLocal.time;
}
}
/*
================
sdGameRulesStopWatch::OnGameState_NextMap
================
*/
void sdGameRulesStopWatch::OnGameState_NextMap( void ) {
ClearPlayerReadyFlags();
}
/*
================
sdGameRulesStopWatch::EndGame
================
*/
void sdGameRulesStopWatch::EndGame( void ) {
if ( IsWarmup() ) {
return;
}
NextStateDelayed( GS_GAMEREVIEW, 1000 );
if ( !gameLocal.isClient ) {
if ( progression == GP_FIRST_MATCH ) {
timeToBeat = gameLocal.time - matchStartedTime;
if ( attackingTeams[ progression ] != NULL ) {
attackingTeamXPs[ progression ] = attackingTeams[ progression ]->GetTotalXP();
}
} else {
// figure out who really won the match by comparing the objective completions etc...
RecordWinningTeam( winningTeam == firstMatchWinningTeam ? winningTeam : NULL, "stopwatch", false );
timeToBeat = 0;
}
CallScriptEndGame();
}
if( gameLocal.DoClientSideStuff() ) {
UpdateClientFromServerInfo( gameLocal.serverInfo, false );
}
}
/*
================
sdGameRulesStopWatch::ApplyNetworkState
================
*/
void sdGameRulesStopWatch::ApplyNetworkState( const sdEntityStateNetworkData& newState ) {
sdGameRules::ApplyNetworkState( newState );
NET_GET_NEW( sdGameRulesStopWatchNetworkState );
SetWinner( newData.winningTeam );
bool changed = ( progression != ( gameProgression_t )newData.progression );
progression = ( gameProgression_t )newData.progression;
changed |= ( timeToBeat != newData.timeToBeat );
timeToBeat = newData.timeToBeat;
changed |= ( winReason != ( winReason_t )newData.winReason );
winReason = ( winReason_t )newData.winReason;
if( changed ) {
UpdateClientFromServerInfo( gameLocal.serverInfo, false );
}
}
/*
================
sdGameRulesStopWatch::ReadNetworkState
================
*/
void sdGameRulesStopWatch::ReadNetworkState( const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, const idBitMsg& msg ) const {
sdGameRules::ReadNetworkState( baseState, newState, msg );
NET_GET_STATES( sdGameRulesStopWatchNetworkState );
sdTeamManagerLocal& teamManager = sdTeamManager::GetInstance();
newData.winningTeam = teamManager.ReadTeamFromStream( baseData.winningTeam, msg );
newData.progression = msg.ReadDeltaLong( baseData.progression );
newData.timeToBeat = msg.ReadDeltaLong( baseData.timeToBeat );
newData.winReason = msg.ReadDeltaLong( baseData.winReason );
}
/*
================
sdGameRulesStopWatch::WriteNetworkState
================
*/
void sdGameRulesStopWatch::WriteNetworkState( const sdEntityStateNetworkData& baseState, sdEntityStateNetworkData& newState, idBitMsg& msg ) const {
sdGameRules::WriteNetworkState( baseState, newState, msg );
NET_GET_STATES( sdGameRulesStopWatchNetworkState );
newData.winningTeam = GetWinningTeam();
newData.progression = progression;
newData.timeToBeat = timeToBeat;
newData.winReason = winReason;
sdTeamManagerLocal& teamManager = sdTeamManager::GetInstance();
teamManager.WriteTeamToStream( baseData.winningTeam, newData.winningTeam, msg );
msg.WriteDeltaLong( baseData.progression, newData.progression );
msg.WriteDeltaLong( baseData.timeToBeat, newData.timeToBeat );
msg.WriteDeltaLong( baseData.winReason, newData.winReason );
}
/*
================
sdGameRulesStopWatch::CheckNetworkStateChanges
================
*/
bool sdGameRulesStopWatch::CheckNetworkStateChanges( const sdEntityStateNetworkData& baseState ) const {
if ( sdGameRules::CheckNetworkStateChanges( baseState ) ) {
return true;
}
NET_GET_BASE( sdGameRulesStopWatchNetworkState );
NET_CHECK_FIELD( winningTeam, GetWinningTeam() );
NET_CHECK_FIELD( progression, progression );
NET_CHECK_FIELD( timeToBeat, timeToBeat );
NET_CHECK_FIELD( winReason, winReason );
return false;
}
/*
================
sdGameRulesStopWatch::CreateNetworkStructure
================
*/
sdEntityStateNetworkData* sdGameRulesStopWatch::CreateNetworkStructure( void ) const {
return new sdGameRulesStopWatchNetworkState();
}
/*
================
sdGameRulesStopWatch::SetWinner
================
*/
void sdGameRulesStopWatch::SetWinner( sdTeamInfo* team ) {
if ( !gameLocal.isClient && ( gameState == GS_GAMEREVIEW ) ) {
gameLocal.Warning( "Attempted to set winning team after game has ended" );
return;
}
if ( winningTeam == team ) {
return;
}
winningTeam = team;
if( gameLocal.DoClientSideStuff() ) {
UpdateClientFromServerInfo( gameLocal.serverInfo, false );
}
}
/*
================
sdGameRulesStopWatch::ChangeMap
================
*/
bool sdGameRulesStopWatch::ChangeMap( const char* mapName ) {
idStr cleanMapName = mapName;
sdGameRules_SingleMapHelper::SanitizeMapName( cleanMapName, true );
idStr sessionCommand = va( "usermap %s", cleanMapName.c_str() );
cmdSystem->PushFrameCommand( sessionCommand.c_str() );
return true;
}
/*
================
sdGameRulesStopWatch::OnUserStartMap
================
*/
userMapChangeResult_e sdGameRulesStopWatch::OnUserStartMap( const char* text, idStr& reason, idStr& mapName ) {
return sdGameRules_SingleMapHelper::OnUserStartMap( text, reason, mapName );
}
/*
================
sdGameRulesStopWatch::GetGameTime
================
*/
int sdGameRulesStopWatch::GetGameTime( void ) const {
int ms;
if ( gameState == GS_WARMUP ) {
ms = 0;
} else if ( gameState == GS_COUNTDOWN ) {
ms = nextStateSwitch - gameLocal.time;
} else {
ms = GetTimeLimit() - ( gameLocal.time - matchStartedTime );
if ( ms < 0 ) {
ms = 0;
}
}
return ms;
}
/*
================
sdGameRulesStopWatch::Reset
================
*/
void sdGameRulesStopWatch::Reset( void ) {
sdGameRules::Reset();
progression = GP_FIRST_MATCH;
firstMatchWinningTeam = NULL;
timeToBeat = 0;
winReason = WR_NORMAL;
if( gameLocal.DoClientSideStuff() ) {
statusText = common->LocalizeText( "guis/hud/stopwatch_1" );
UpdateClientFromServerInfo( gameLocal.serverInfo, false );
}
ResetTiebreakInfo();
}
/*
================
sdGameRulesStopWatch::GetTimeLimit
================
*/
int sdGameRulesStopWatch::GetTimeLimit( void ) const {
switch ( progression ) {
default:
case GP_FIRST_MATCH:
return sdGameRules::GetTimeLimit();
case GP_RETURN_MATCH:
return timeToBeat;
}
}
/*
================
sdGameRulesStopWatch::GetTypeText
================
*/
const sdDeclLocStr* sdGameRulesStopWatch::GetTypeText( void ) const {
return declHolder.declLocStrType[ "game/gametype/stopwatch" ];
}
/*
================
sdGameRulesStopWatch::ArgCompletion_StartGame
================
*/
void sdGameRulesStopWatch::ArgCompletion_StartGame( const idCmdArgs& args, argCompletionCallback_t callback ) {
sdGameRules_SingleMapHelper::ArgCompletion_StartGame( args, callback );
}
/*
============
sdGameRulesStopWatch::UpdateClientFromServerInfo
============
*/
void sdGameRulesStopWatch::UpdateClientFromServerInfo( const idDict& serverInfo, bool allowMedia ) {
sdGameRules::UpdateClientFromServerInfo( serverInfo, allowMedia );
idStr mapName = serverInfo.GetString( "si_map" );
if( mapName.IsEmpty() ) {
return;
}
mapName.StripFileExtension();
using namespace sdProperties;
// update status
if ( sdUserInterfaceScope* scope = gameLocal.globalProperties.GetSubScope( "campaignInfo" ) ) {
const idDict* metaData = gameLocal.mapMetaDataList->FindMetaData( mapName, &gameLocal.defaultMetaData );
if( allowMedia ) {
const sdDeclMapInfo* mapInfo = gameLocal.declMapInfoType.LocalFind( metaData->GetString( "mapinfo", "_default" ) );
// setup the backdrop
if ( sdProperty* property = scope->GetProperty( "backdrop", PT_STRING ) ) {
*property->value.stringValue = mapInfo->GetData().GetString( "mtr_backdrop", "guis/assets/black" );
}
const char* status = "current";
if( winningTeam != NULL ) {
status = winningTeam->GetLookupName();
}
SetupLoadScreenUI( *scope, status, true, 1, *metaData, mapInfo );
}
// setup the name
if ( sdProperty* property = scope->GetProperty( "name", PT_WSTRING ) ) {
*property->value.wstringValue = va( L"%hs", metaData->GetString( "pretty_name" ) );
}
if ( sdProperty* property = scope->GetProperty( "numMaps", PT_FLOAT ) ) {
*property->value.floatValue = 1.0f;
}
if ( sdProperty* property = scope->GetProperty( "currentMap", PT_FLOAT ) ) {
*property->value.floatValue = 1.0f;
}
idWStr text;
if( timeToBeat > 0 ) {
idWStr::hmsFormat_t format;
format.showZeroMinutes = true;
idWStrList args( 1 );
args.Append( idWStr::MS2HMS( timeToBeat, format ) );
text = common->LocalizeText( "guis/mainmenu/timetobeat", args );
}
// setup the status
if ( sdProperty* property = scope->GetProperty( "ruleStatus", PT_WSTRING ) ) {
*property->value.wstringValue = text;
}
}
}
/*
============
sdGameRulesStopWatch::Clear
============
*/
void sdGameRulesStopWatch::Clear( void ) {
sdGameRules::Clear();
winningTeam = NULL;
winReason = WR_NORMAL;
}
/*
============
sdGameRulesStopWatch::GetProbeState
============
*/
byte sdGameRulesStopWatch::GetProbeState( void ) const {
byte value = sdGameRules::GetProbeState();
if( progression == GP_RETURN_MATCH ) {
value |= PGS_RETURN;
}
return value;
}
/*
============
sdGameRulesStopWatch::GetServerBrowserScore
============
*/
int sdGameRulesStopWatch::GetServerBrowserScore( const sdNetSession& session ) const {
int score = 0;
if ( session.GetGameState() & PGS_RETURN ) {
score += sdHotServerList::BROWSER_OK_BONUS;
} else {
score += sdHotServerList::BROWSER_GOOD_BONUS;
}
score += sdGameRules::GetServerBrowserScore( session );
return score;
}
/*
============
sdGameRulesStopWatch::GetBrowserStatusString
============
*/
void sdGameRulesStopWatch::GetBrowserStatusString( idWStr& str, const sdNetSession& netSession ) const {
str.Clear();
sdGameRules::GetBrowserStatusString( str, netSession );
str += L" - ";
if( netSession.GetGameState() & PGS_RETURN ) {
str += declHolder.declLocStrType.LocalFind( "guis/hud/stopwatch_2" )->GetText();
} else {
str += declHolder.declLocStrType.LocalFind( "guis/hud/stopwatch_1" )->GetText();
}
}
/*
============
sdGameRulesStopWatch::InhibitEntitySpawn
============
*/
bool sdGameRulesStopWatch::InhibitEntitySpawn( idDict &spawnArgs ) const {
if ( spawnArgs.GetBool( "noStopwatch" ) ) {
return true;
}
return false;
}
/*
============
sdGameRulesStopWatch::ResetTiebreakInfo
============
*/
void sdGameRulesStopWatch::ResetTiebreakInfo( void ) {
for ( int i = 0; i < GP_MAX; i++ ) {
attackingTeams[ i ] = NULL;
defendingTeams[ i ] = NULL;
attackingTeamXPs[ i ] = -1;
objectiveCompletionTimes[ i ].SetNum( 0, false );
}
winReason = WR_NORMAL;
}
/*
============
sdGameRulesStopWatch::OnObjectiveCompletion
============
*/
void sdGameRulesStopWatch::OnObjectiveCompletion( int objectiveIndex ) {
// record the amount of time each team took to do the objectives
objectiveCompletionTimes[ progression ].AssureSize( objectiveIndex + 1 );
objectiveCompletionTimes[ progression ][ objectiveIndex ] = gameLocal.time - matchStartedTime;
}
/*
============
sdGameRulesStopWatch::SetAttacker
============
*/
void sdGameRulesStopWatch::SetAttacker( sdTeamInfo* team ) {
attackingTeams[ progression ] = team;
}
/*
============
sdGameRulesStopWatch::SetDefender
============
*/
void sdGameRulesStopWatch::SetDefender( sdTeamInfo* team ) {
defendingTeams[ progression ] = team;
}
/*
============
sdGameRulesStopWatch::OnTimeLimitHit
============
*/
void sdGameRulesStopWatch::OnTimeLimitHit( void ) {
sdGameRules::OnTimeLimitHit();
if ( !gameLocal.isClient ) {
if ( progression == GP_RETURN_MATCH ) {
if ( attackingTeams[ GP_FIRST_MATCH ] == NULL || attackingTeams[ GP_RETURN_MATCH ] == NULL ) {
assert( false );
gameLocal.Warning( "sdGameRulesStopWatch::OnTimeLimitHit - hit timelimit in return match with an attacking team being NULL!" );
return;
}
if ( defendingTeams[ GP_FIRST_MATCH ] == NULL || defendingTeams[ GP_RETURN_MATCH ] == NULL ) {
assert( false );
gameLocal.Warning( "sdGameRulesStopWatch::OnTimeLimitHit - hit timelimit in return match with a defending team being NULL!" );
return;
}
// record the XP
attackingTeamXPs[ progression ] = attackingTeams[ progression ]->GetTotalXP();
// time ran out without this team having completed all the objectives!
sdTeamInfo* firstRoundAttackTeam = defendingTeams[ GP_RETURN_MATCH ];
sdTeamInfo* returnRoundAttackTeam = attackingTeams[ GP_RETURN_MATCH ];
// if either team has completed more objectives then they are the winner
// (this also takes into account finishing the map)
int firstMatchNum = objectiveCompletionTimes[ GP_FIRST_MATCH ].Num();
int returnMatchNum = objectiveCompletionTimes[ GP_RETURN_MATCH ].Num();
if ( firstMatchNum > returnMatchNum ) {
SetWinner( firstRoundAttackTeam );
winReason = WR_NORMAL;
return;
}
if ( returnMatchNum > firstMatchNum ) {
SetWinner( returnRoundAttackTeam );
winReason = WR_NORMAL;
return;
}
// it'll fall through to here if both teams did the same number of objectives
for ( int i = objectiveCompletionTimes[ GP_FIRST_MATCH ].Num() - 1; i >= 0; i-- ) {
// find out who finished the objective first
int firstTeamTime = objectiveCompletionTimes[ GP_FIRST_MATCH ][ i ];
int returnTeamTime = objectiveCompletionTimes[ GP_RETURN_MATCH ][ i ];
if ( firstTeamTime < returnTeamTime ) {
// first team did it faster
SetWinner( firstRoundAttackTeam );
winReason = WR_SPEED;
return;
}
if ( returnTeamTime < firstTeamTime ) {
// second team did it faster
SetWinner( returnRoundAttackTeam );
winReason = WR_SPEED;
return;
}
}
// it'll only get here if no-one did any objectives or if both teams completed the objectives
// at the exact same times! (crazy unlikely, but hey...)
// the team with the most attacking XP wins
//
// NOTE: after all this if by some crazy fluke the XP is identical (or if no-one did anything)
// it is counted as the first team winning
if ( attackingTeamXPs[ GP_FIRST_MATCH ] >= attackingTeamXPs[ GP_RETURN_MATCH ] ) {
SetWinner( firstRoundAttackTeam );
winReason = WR_XP;
return;
} else {
SetWinner( returnRoundAttackTeam );
winReason = WR_XP;
return;
}
}
}
}
/*
============
sdGameRulesStopWatch::GetWinningReason
============
*/
const sdDeclLocStr* sdGameRulesStopWatch::GetWinningReason( void ) const {
if ( winReason == WR_NORMAL ) {
return NULL;
}
sdTeamInfo* winner = GetWinningTeam();
if ( winner == NULL ) {
return NULL;
}
if ( winReason == WR_SPEED ) {
return winner->GetWinStringSW_Speed();
}
if ( winReason == WR_XP ) {
return winner->GetWinStringSW_XP();
}
return NULL;
}
/*
============
sdGameRulesStopWatch::GetDemoNameInfo
============
*/
const char* sdGameRulesStopWatch::GetDemoNameInfo( void ) {
static idStr demoNameBuffer;
if ( progression == GP_FIRST_MATCH ) {
demoNameBuffer = va( "stopwatch_first" );
} else {
demoNameBuffer = va( "stopwatch_second" );
}
return demoNameBuffer.c_str();
}