Index: src/game/g_local.h =================================================================== --- src/game/g_local.h (revision 939) +++ src/game/g_local.h (working copy) @@ -294,6 +294,7 @@ typedef struct { team_t sessionTeam; + pTeam_t warmupTeam; // team selected during warmup int spectatorTime; // for determining next-in-line to play spectatorState_t spectatorState; int spectatorClient; // for chasecam and follow mode @@ -378,6 +379,7 @@ clientSession_t sess; qboolean readyToExit; // wishes to leave the intermission + qboolean warmupReady; // ready to start the match qboolean noclip; Index: src/game/g_session.c =================================================================== --- src/game/g_session.c (revision 939) +++ src/game/g_session.c (working copy) @@ -46,8 +46,9 @@ const char *s; const char *var; - s = va( "%i %i %i %i %i %i %i %s", + s = va( "%i %i %i %i %i %i %i %i %s", client->sess.sessionTeam, + client->sess.warmupTeam, client->sess.spectatorTime, client->sess.spectatorState, client->sess.spectatorClient, @@ -78,14 +79,16 @@ int teamLeader; int spectatorState; int sessionTeam; + int warmupTeam; var = va( "session%i", client - level.clients ); trap_Cvar_VariableStringBuffer( var, s, sizeof(s) ); // FIXME: should be using BG_ClientListParse() for ignoreList, but // bg_lib.c's sscanf() currently lacks %s - sscanf( s, "%i %i %i %i %i %i %i %x%x", + sscanf( s, "%i %i %i %i %i %i %i %i %x%x", &sessionTeam, + &warmupTeam, &client->sess.spectatorTime, &spectatorState, &client->sess.spectatorClient, @@ -97,6 +100,7 @@ ); // bk001205 - format issues client->sess.sessionTeam = (team_t)sessionTeam; + client->sess.warmupTeam = (pTeam_t)warmupTeam; client->sess.spectatorState = (spectatorState_t)spectatorState; client->sess.teamLeader = (qboolean)teamLeader; } @@ -132,6 +136,7 @@ sess->sessionTeam = TEAM_FREE; } + sess->warmupTeam = PTE_NONE; sess->spectatorState = SPECTATOR_FREE; sess->spectatorTime = level.time; sess->spectatorClient = -1; Index: src/game/g_main.c =================================================================== --- src/game/g_main.c (revision 939) +++ src/game/g_main.c (working copy) @@ -68,6 +68,7 @@ vmCvar_t g_synchronousClients; vmCvar_t g_warmup; vmCvar_t g_doWarmup; +vmCvar_t g_doWarmupReady; vmCvar_t g_restarted; vmCvar_t g_logFile; vmCvar_t g_logFileSync; @@ -167,6 +168,7 @@ { &g_warmup, "g_warmup", "20", CVAR_ARCHIVE, 0, qtrue }, { &g_doWarmup, "g_doWarmup", "0", 0, 0, qtrue }, + { &g_doWarmupReady, "g_doWarmupReady", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_logFile, "g_logFile", "games.log", CVAR_ARCHIVE, 0, qfalse }, { &g_logFileSync, "g_logFileSync", "0", CVAR_ARCHIVE, 0, qfalse }, @@ -1936,6 +1938,82 @@ /* +============= +CheckTournament + +Once a frame, check for changes in tournement player state +============= +*/ +void CheckTournament( void ) { + if( level.numPlayingClients == 0 ) + return; + + if( level.warmupTime != 0 ) { + if( level.numAlienClients < 1 || level.numHumanClients < 1 ) { + if( level.warmupTime != -1 ) { + level.warmupTime = -1; + trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); + G_LogPrintf( "Warmup:\n" ); + } + return; // still waiting for team members + } + + if( level.warmupTime == 0 ) + return; + + // if the warmup is changed at the console, restart it + if( g_warmup.modificationCount != level.warmupModificationCount ) { + level.warmupModificationCount = g_warmup.modificationCount; + level.warmupTime = -1; + } + + // require all players to ready up + if( g_doWarmupReady.integer && level.warmupTime < 0 ) { + int i, ready = 0, readyMask = 0; + gclient_t *cl; + + for( i = 0; i < g_maxclients.integer; i++ ) + { + cl = level.clients + i; + + if( cl->pers.connected != CON_CONNECTED || + cl->ps.stats[ STAT_PTEAM ] == PTE_NONE ) + continue; + + if( cl->warmupReady ) + { + ready++; + if( i < 16 ) + readyMask |= 1 << i; + } + } + trap_SetConfigstring( CS_CLIENTS_READY, va( "%d", readyMask ) ); + trap_SetConfigstring( CS_WARMUP, va( "%d", -(level.numPlayingClients - + ready) - 1 ) ); + if( ready < level.numPlayingClients ) + return; + } + + // if all players have arrived, start the countdown + if( level.warmupTime < 0 ) { + level.warmupTime = level.time + g_warmup.integer * 1000; + trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); + return; + } + + // if the warmup time has counted down, restart + if( level.time > level.warmupTime ) { + level.warmupTime += 10000; + trap_Cvar_Set( "g_restarted", "1" ); + trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" ); + level.restarted = qtrue; + return; + } + } +} + + +/* ================== CheckVote ================== @@ -2272,6 +2350,9 @@ G_CalculateAvgPlayers( ); G_UpdateZaps( msec ); + // see if it is time to do a tournament restart + CheckTournament(); + // see if it is time to end the level CheckExitRules( ); Index: src/game/g_client.c =================================================================== --- src/game/g_client.c (revision 939) +++ src/game/g_client.c (working copy) @@ -1262,6 +1262,13 @@ // count current clients and rank for scoreboard CalculateRanks( ); G_admin_namelog_update( client, qfalse ); + + // if the match is starting, join the selected team + if ( !level.warmupTime && client->sess.warmupTeam != PTE_NONE ) { + G_ChangeTeam( ent, client->sess.warmupTeam ); + client->sess.warmupTeam = PTE_NONE; + } + return NULL; } Index: src/game/g_cmds.c =================================================================== --- src/game/g_cmds.c (revision 939) +++ src/game/g_cmds.c (working copy) @@ -649,6 +649,11 @@ G_LeaveTeam( ent ); ent->client->pers.teamSelection = newTeam; + // if a team is selected during warmup, we need to preserve the team + // across the next restart + if ( level.warmupTime ) + ent->client->sess.warmupTeam = newTeam; + // under certain circumstances, clients can keep their kills and credits // when switching teams if( G_admin_permission( ent, ADMF_TEAMCHANGEFREE ) || @@ -1549,6 +1554,38 @@ /* +================== +Cmd_Ready_f +================== +*/ +void Cmd_Ready_f( gentity_t *ent, qboolean ready ) +{ + if( ent->client->pers.teamSelection == PTE_NONE ) { + trap_SendServerCommand( ent-g_entities, "print \"Join a team first\n\"" ); + return; + } + + if( level.warmupTime >= 0 ) + { + trap_SendServerCommand( ent-g_entities, + "print \"Match already started\n\"" ); + return; + } + + if( ready == ent->client->warmupReady ) + return; + + if( ready ) + trap_SendServerCommand( -1, va( "print \"%s^7 is ready\n\"", + ent->client->pers.netname ) ); + else + trap_SendServerCommand( -1, va( "print \"%s^7 is NOT ready\n\"", + ent->client->pers.netname ) ); + ent->client->warmupReady = ready; +} + + +/* ================= Cmd_SetViewpos_f ================= @@ -3033,6 +3070,10 @@ Cmd_CallVote_f( ent ); else if( Q_stricmp( cmd, "vote" ) == 0 ) Cmd_Vote_f( ent ); + else if( Q_stricmp( cmd, "ready" ) == 0 ) + Cmd_Ready_f( ent, qtrue ); + else if( Q_stricmp( cmd, "notready" ) == 0 ) + Cmd_Ready_f( ent, qfalse ); else if( Q_stricmp( cmd, "callteamvote" ) == 0 ) Cmd_CallTeamVote_f( ent ); else if( Q_stricmp( cmd, "follow" ) == 0 ) Index: src/cgame/cg_draw.c =================================================================== --- src/cgame/cg_draw.c (revision 939) +++ src/cgame/cg_draw.c (working copy) @@ -3039,6 +3039,48 @@ //============================================================================== +/* +================= +CG_DrawWarmup +================= +*/ +static void CG_DrawWarmup( void ) +{ + char readyKey[ 32 ], *s; + int x, y, w, h; + vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f }; + + if ( !cg.warmup ) + return; + + if ( cg.warmup == -1 ) + s = "Waiting for players to join."; + else if ( cg.warmup < -1 ) { + int readyClients = atoi( CG_ConfigString( CS_CLIENTS_READY ) ); + + if( !( readyClients & ( 1 << cg.snap->ps.clientNum ) ) ) { + Q_strncpyz( readyKey, CG_KeyBinding( "ready" ), sizeof( readyKey ) ); + s = va("Press '%s' to ready up.", readyKey); + } else { + int notReady = -cg.warmup - 1; + + s = va("Waiting for %d player%s to ready up.", notReady, + notReady > 1 ? "s" : "" ); + } + } else { + int timeLeft = ( cg.warmup - cg.time ) / 1000 + 1; + + s = va( "Match starts in %d second%s.", timeLeft, + timeLeft != 1 ? "s" : "" ); + } + + w = CG_Text_Width( s, 0.5, 0 ); + h = CG_Text_Height( s, 0.5, 0 ); + x = ( SCREEN_WIDTH - w ) / 2; + y = ( SCREEN_HEIGHT ) * 3/ 4 - h / 2; + CG_Text_Paint( x, y + h, 0.5, white, s, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE ); +} + //FIXME: both vote notes are hardcoded, change to ownerdrawn? /* @@ -3322,9 +3364,11 @@ // don't draw center string if scoreboard is up cg.scoreBoardShowing = CG_DrawScoreboard( ); - if( !cg.scoreBoardShowing ) + if( !cg.scoreBoardShowing ) { + CG_DrawWarmup( ); CG_DrawCenterString( ); } +} /* =============== @@ -3520,5 +3564,3 @@ CG_Draw2D( ); } - - Index: src/cgame/cg_consolecmds.c =================================================================== --- src/cgame/cg_consolecmds.c (revision 939) +++ src/cgame/cg_consolecmds.c (working copy) @@ -322,4 +322,6 @@ trap_AddCommand( "advanceMapRotation" ); trap_AddCommand( "alienWin" ); trap_AddCommand( "humanWin" ); + trap_AddCommand( "ready" ); + trap_AddCommand( "notready" ); } Index: src/cgame/cg_main.c =================================================================== --- src/cgame/cg_main.c (revision 939) +++ src/cgame/cg_main.c (working copy) @@ -1472,7 +1472,7 @@ sp = &cg.scores[ scoreIndex ]; if( ( atoi( CG_ConfigString( CS_CLIENTS_READY ) ) & ( 1 << sp->client ) ) && - cg.intermissionStarted ) + ( cg.intermissionStarted || cg.warmup < -1 ) ) showIcons = qfalse; else if( cg.snap->ps.pm_type == PM_SPECTATOR || cg.snap->ps.pm_flags & PMF_FOLLOW || team == cg.snap->ps.stats[ STAT_PTEAM ] || cg.intermissionStarted ) @@ -1515,7 +1515,7 @@ case 2: if( ( atoi( CG_ConfigString( CS_CLIENTS_READY ) ) & ( 1 << sp->client ) ) && - cg.intermissionStarted ) + ( cg.intermissionStarted || cg.warmup < -1 ) ) return "Ready"; break;