/* * G_Timeout_Reset */ void G_Timeout_Reset( void ) { int i; GS_GamestatSetFlag( GAMESTAT_FLAG_PAUSED, false ); level.timeout.time = 0; level.timeout.endtime = 0; level.timeout.caller = 0; for( i = 0; i < MAX_CLIENTS; i++ ) level.timeout.used[i] = 0; }
/* * G_Match_CheckExtendPlayTime */ bool G_Match_CheckExtendPlayTime( void ) { // check for extended time/sudden death if( GS_MatchState() != MATCH_STATE_PLAYTIME ) return false; if( GS_TeamBasedGametype() && !level.forceExit ) { if( G_Match_Tied() ) { GS_GamestatSetFlag( GAMESTAT_FLAG_MATCHEXTENDED, true ); gs.gameState.stats[GAMESTAT_MATCHSTATE] = MATCH_STATE_PLAYTIME; gs.gameState.longstats[GAMELONG_MATCHSTART] = game.serverTime; if( g_match_extendedtime->value ) { if( !GS_MatchExtended() ) // first one G_AnnouncerSound( NULL, trap_SoundIndex( S_ANNOUNCER_OVERTIME_GOING_TO_OVERTIME ), GS_MAX_TEAMS, true, NULL ); else G_AnnouncerSound( NULL, trap_SoundIndex( S_ANNOUNCER_OVERTIME_OVERTIME ), GS_MAX_TEAMS, true, NULL ); G_PrintMsg( NULL, "Match tied. Timelimit extended by %i minutes!\n", g_match_extendedtime->integer ); G_CenterPrintFormatMsg( NULL, "%s MINUTE OVERTIME\n", va( "%i", g_match_extendedtime->integer ), NULL ); gs.gameState.longstats[GAMELONG_MATCHDURATION] = (unsigned int)( ( fabs( g_match_extendedtime->value ) * 60 ) * 1000 ); } else { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_OVERTIME_SUDDENDEATH_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); G_PrintMsg( NULL, "Match tied. Sudden death!\n" ); G_CenterPrintMsg( NULL, "SUDDEN DEATH" ); gs.gameState.longstats[GAMELONG_MATCHDURATION] = 0; } return true; } } return false; }
/* * Cmd_Timeout_f */ static void Cmd_Timeout_f( edict_t *ent ) { int num; if( ent->s.team == TEAM_SPECTATOR || GS_MatchState() != MATCH_STATE_PLAYTIME ) return; if( GS_TeamBasedGametype() ) num = ent->s.team; else num = ENTNUM( ent )-1; if( GS_MatchPaused() && ( level.timeout.endtime - level.timeout.time ) >= 2*TIMEIN_TIME ) { G_PrintMsg( ent, "Timeout already in progress\n" ); return; } if( g_maxtimeouts->integer != -1 && level.timeout.used[num] >= g_maxtimeouts->integer ) { if( g_maxtimeouts->integer == 0 ) G_PrintMsg( ent, "Timeouts are not allowed on this server\n" ); else if( GS_TeamBasedGametype() ) G_PrintMsg( ent, "Your team doesn't have any timeouts left\n" ); else G_PrintMsg( ent, "You don't have any timeouts left\n" ); return; } G_PrintMsg( NULL, "%s%s called a timeout\n", ent->r.client->netname, S_COLOR_WHITE ); if( !GS_MatchPaused() ) G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_TIMEOUT_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); level.timeout.used[num]++; GS_GamestatSetFlag( GAMESTAT_FLAG_PAUSED, true ); level.timeout.caller = num; level.timeout.endtime = level.timeout.time + TIMEOUT_TIME + FRAMETIME; }
/* * G_Match_LaunchState */ void G_Match_LaunchState( int matchState ) { static bool advance_queue = false; if( game.asEngine != NULL ) { // give the gametype a chance to refuse the state change, or to set up things for it if( !GT_asCallMatchStateFinished( matchState ) ) return; } else { // There isn't any script, run generic fuction if( !G_Gametype_GENERIC_MatchStateFinished( matchState ) ) return; } GS_GamestatSetFlag( GAMESTAT_FLAG_MATCHEXTENDED, false ); GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, false ); if( matchState == MATCH_STATE_POSTMATCH ) { level.finalMatchDuration = game.serverTime - GS_MatchStartTime(); } if( ( matchState == MATCH_STATE_POSTMATCH && GS_RaceGametype() ) || ( matchState != MATCH_STATE_POSTMATCH && gs.gameState.stats[GAMESTAT_MATCHSTATE] == MATCH_STATE_POSTMATCH ) ) { // entering postmatch in race or leaving postmatch in normal gt G_Match_SendReport(); trap_MM_GameState( false ); } switch( matchState ) { default: case MATCH_STATE_WARMUP: { advance_queue = false; level.forceStart = false; gs.gameState.stats[GAMESTAT_MATCHSTATE] = MATCH_STATE_WARMUP; gs.gameState.longstats[GAMELONG_MATCHDURATION] = (unsigned int)( fabs( g_warmup_timelimit->value * 60 ) * 1000 ); gs.gameState.longstats[GAMELONG_MATCHSTART] = game.serverTime; // race has playtime in warmup too, so flag the matchmaker about this if( GS_RaceGametype() ) trap_MM_GameState( true ); break; } case MATCH_STATE_COUNTDOWN: { advance_queue = true; gs.gameState.stats[GAMESTAT_MATCHSTATE] = MATCH_STATE_COUNTDOWN; gs.gameState.longstats[GAMELONG_MATCHDURATION] = (unsigned int)( fabs( g_countdown_time->value ) * 1000 ); gs.gameState.longstats[GAMELONG_MATCHSTART] = game.serverTime; break; } case MATCH_STATE_PLAYTIME: { // ch : should clear some statcollection memory from warmup? advance_queue = true; // shouldn't be needed here level.forceStart = false; gs.gameState.stats[GAMESTAT_MATCHSTATE] = MATCH_STATE_PLAYTIME; gs.gameState.longstats[GAMELONG_MATCHDURATION] = (unsigned int)( fabs( 60 * g_timelimit->value )*1000 ); gs.gameState.longstats[GAMELONG_MATCHSTART] = game.serverTime; // request a new match UUID trap_ConfigString( CS_MATCHUUID, "" ); // tell matchmaker that the game is on, so if // client disconnects before SendReport, it is flagged // as 'purgable' on MM side trap_MM_GameState( true ); } break; case MATCH_STATE_POSTMATCH: { gs.gameState.stats[GAMESTAT_MATCHSTATE] = MATCH_STATE_POSTMATCH; gs.gameState.longstats[GAMELONG_MATCHDURATION] = (unsigned int)fabs( g_postmatch_timelimit->value * 1000 ); // postmatch time in seconds gs.gameState.longstats[GAMELONG_MATCHSTART] = game.serverTime; G_Timeout_Reset(); level.teamlock = false; level.forceExit = false; G_Match_Autorecord_Stats(); } break; case MATCH_STATE_WAITEXIT: { if( advance_queue ) { G_Teams_AdvanceChallengersQueue(); advance_queue = true; } gs.gameState.stats[GAMESTAT_MATCHSTATE] = MATCH_STATE_WAITEXIT; gs.gameState.longstats[GAMELONG_MATCHDURATION] = 25000; gs.gameState.longstats[GAMELONG_MATCHSTART] = game.serverTime; level.exitNow = false; } break; } // give the gametype the chance to setup for the new state if( game.asEngine != NULL ) GT_asCallMatchStateStarted(); else G_Gametype_GENERIC_MatchStateStarted(); G_UpdatePlayersMatchMsgs(); }
/* * G_Match_CheckStateAbort */ static void G_Match_CheckStateAbort( void ) { bool any = false; bool enough; if( GS_MatchState() <= MATCH_STATE_NONE || GS_MatchState() >= MATCH_STATE_POSTMATCH || level.gametype.mathAbortDisabled ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, false ); return; } if( GS_TeamBasedGametype() ) { int team, emptyteams = 0; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { if( !teamlist[team].numplayers ) emptyteams++; else any = true; } enough = ( emptyteams == 0 ); } else { enough = ( teamlist[TEAM_PLAYERS].numplayers > 1 ); any = ( teamlist[TEAM_PLAYERS].numplayers > 0 ); } // if waiting, turn on match states when enough players joined if( GS_MatchWaiting() && enough ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, false ); G_UpdatePlayersMatchMsgs(); } // turn off active match states if not enough players left else if( GS_MatchState() == MATCH_STATE_WARMUP && !enough && GS_MatchDuration() ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, true ); G_UpdatePlayersMatchMsgs(); } else if( GS_MatchState() == MATCH_STATE_COUNTDOWN && !enough ) { if( any ) { G_PrintMsg( NULL, "Not enough players left. Countdown aborted.\n" ); G_CenterPrintMsg( NULL, "COUNTDOWN ABORTED" ); } G_Match_Autorecord_Cancel(); G_Match_LaunchState( MATCH_STATE_WARMUP ); GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, true ); G_UpdatePlayersMatchMsgs(); } // match running, but not enough players left else if( GS_MatchState() == MATCH_STATE_PLAYTIME && !enough ) { if( any ) { G_PrintMsg( NULL, "Not enough players left. Match aborted.\n" ); G_CenterPrintMsg( NULL, "MATCH ABORTED" ); } G_EndMatch(); } }
/* * G_Timeout_Update * * Updates the timeout struct and informs clients about the status of the pause */ static void G_Timeout_Update( unsigned int msec ) { static int timeout_printtime = 0; static int timeout_last_endtime = 0; static int countdown_set = 1; if( !GS_MatchPaused() ) return; game.frametime = 0; if( timeout_last_endtime != level.timeout.endtime ) // force print when endtime is changed { timeout_printtime = 0; timeout_last_endtime = level.timeout.endtime; } level.timeout.time += msec; if( level.timeout.endtime && level.timeout.time >= level.timeout.endtime ) { level.timeout.time = 0; level.timeout.caller = -1; GS_GamestatSetFlag( GAMESTAT_FLAG_PAUSED, false ); timeout_printtime = 0; timeout_last_endtime = -1; G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_MATCH_RESUMED_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); G_CenterPrintMsg( NULL, "Match resumed" ); G_PrintMsg( NULL, "Match resumed\n" ); } else if( timeout_printtime == 0 || level.timeout.time - timeout_printtime >= 1000 ) { if( level.timeout.endtime ) { int seconds_left = (int)( ( level.timeout.endtime - level.timeout.time ) / 1000.0 + 0.5 ); if( seconds_left == ( TIMEIN_TIME * 2 ) / 1000 ) { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_READY_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, false, NULL ); countdown_set = ( rand()&1 )+1; } else if( seconds_left >= 1 && seconds_left <= 3 ) { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_COUNT_1_to_3_SET_1_to_2, seconds_left, countdown_set ) ), GS_MAX_TEAMS, false, NULL ); } if( seconds_left > 1 ) G_CenterPrintFormatMsg( NULL, "Match will resume in %s seconds", va( "%i", seconds_left ), NULL ); else G_CenterPrintMsg( NULL, "Match will resume in 1 second" ); } else { G_CenterPrintMsg( NULL, "Match paused" ); } timeout_printtime = level.timeout.time; } }
/* * G_CheckCvars * Check for cvars that have been modified and need the game to be updated */ void G_CheckCvars( void ) { if( g_antilag_maxtimedelta->modified ) { if( g_antilag_maxtimedelta->integer < 0 ) trap_Cvar_SetValue( "g_antilag_maxtimedelta", abs( g_antilag_maxtimedelta->integer ) ); g_antilag_maxtimedelta->modified = false; g_antilag_timenudge->modified = true; } if( g_antilag_timenudge->modified ) { if( g_antilag_timenudge->integer > g_antilag_maxtimedelta->integer ) trap_Cvar_SetValue( "g_antilag_timenudge", g_antilag_maxtimedelta->integer ); else if( g_antilag_timenudge->integer < -g_antilag_maxtimedelta->integer ) trap_Cvar_SetValue( "g_antilag_timenudge", -g_antilag_maxtimedelta->integer ); g_antilag_timenudge->modified = false; } if( g_warmup_timelimit->modified ) { // if we are inside timelimit period, update the endtime if( GS_MatchState() == MATCH_STATE_WARMUP ) { gs.gameState.longstats[GAMELONG_MATCHDURATION] = (unsigned int)fabs( 60.0f * 1000 * g_warmup_timelimit->integer ); } g_warmup_timelimit->modified = false; } if( g_timelimit->modified ) { // if we are inside timelimit period, update the endtime if( GS_MatchState() == MATCH_STATE_PLAYTIME && !GS_MatchExtended() ) { if( g_timelimit->value ) gs.gameState.longstats[GAMELONG_MATCHDURATION] = (unsigned int)fabs( 60.0f * 1000 * g_timelimit->value ); else gs.gameState.longstats[GAMELONG_MATCHDURATION] = 0; } g_timelimit->modified = false; } if( g_match_extendedtime->modified ) { // if we are inside extended_time period, update the endtime if( GS_MatchExtended() ) { if( g_match_extendedtime->integer ) gs.gameState.longstats[GAMELONG_MATCHDURATION] = (unsigned int)fabs( 60 * 1000 * g_match_extendedtime->value ); } g_match_extendedtime->modified = false; } if( g_allow_falldamage->modified ) { g_allow_falldamage->modified = false; } // update gameshared server settings // FIXME: This should be restructured so gameshared settings are the master settings GS_GamestatSetFlag( GAMESTAT_FLAG_INSTAGIB, ( g_instagib->integer != 0 ) ); GS_GamestatSetFlag( GAMESTAT_FLAG_FALLDAMAGE, ( g_allow_falldamage->integer != 0 ) ); GS_GamestatSetFlag( GAMESTAT_FLAG_SELFDAMAGE, ( g_allow_selfdamage->integer != 0 ) ); GS_GamestatSetFlag( GAMESTAT_FLAG_HASCHALLENGERS, ( level.gametype.hasChallengersQueue != 0 ) ); GS_GamestatSetFlag( GAMESTAT_FLAG_ISTEAMBASED, ( level.gametype.isTeamBased != 0 ) ); GS_GamestatSetFlag( GAMESTAT_FLAG_ISRACE, ( level.gametype.isRace != 0 ) ); GS_GamestatSetFlag( GAMESTAT_FLAG_COUNTDOWN, level.gametype.countdownEnabled ); GS_GamestatSetFlag( GAMESTAT_FLAG_INHIBITSHOOTING, level.gametype.shootingDisabled ); GS_GamestatSetFlag( GAMESTAT_FLAG_INFINITEAMMO, ( level.gametype.infiniteAmmo || GS_Instagib() ) != 0 ); GS_GamestatSetFlag( GAMESTAT_FLAG_CANFORCEMODELS, level.gametype.canForceModels ); GS_GamestatSetFlag( GAMESTAT_FLAG_CANSHOWMINIMAP, level.gametype.canShowMinimap ); GS_GamestatSetFlag( GAMESTAT_FLAG_TEAMONLYMINIMAP, level.gametype.teamOnlyMinimap ); GS_GamestatSetFlag( GAMESTAT_FLAG_MMCOMPATIBLE, level.gametype.mmCompatible ); GS_GamestatSetLongFlag( GAMELONG_FLAG_ISTUTORIAL, ( level.gametype.isTutorial != 0 ) ); gs.gameState.stats[GAMESTAT_MAXPLAYERSINTEAM] = level.gametype.maxPlayersPerTeam; clamp( gs.gameState.stats[GAMESTAT_MAXPLAYERSINTEAM], 0, 255 ); }