/* * G_ClientBeginFrame * * This will be called once for each server frame, before running * any other entities in the world. */ void G_ClientBeginFrame(g_edict_t *ent) { g_client_t *client; if (g_level.intermission_time) return; client = ent->client; if (ent->ground_entity) // let this be reset each frame as needed client->ps.pmove.pm_flags &= ~PMF_PUSHED; // run weapon think if it hasn't been done by a command if (client->weapon_think_time < g_level.time && !client->persistent.spectator) G_WeaponThink(ent); if (ent->dead) { // check for respawn conditions // rounds mode implies last-man-standing, force to spectator immediately if round underway if (g_level.rounds && g_level.round_time && g_level.time >= g_level.round_time) { client->persistent.spectator = true; G_ClientRespawn(ent, false); } else if (g_level.time > client->respawn_time && (client->latched_buttons & BUTTON_ATTACK)) { G_ClientRespawn(ent, false); // all other respawns require a click from the player } } client->latched_buttons = 0; }
/* * G_Teams_SetTeam - sets clients team without any checking */ void G_Teams_SetTeam( edict_t *ent, int team ) { assert( ent && ent->r.inuse && ent->r.client ); assert( team >= TEAM_SPECTATOR && team < GS_MAX_TEAMS ); // if player was on a team, send partial report to matchmaker if( ent->r.client->team != TEAM_SPECTATOR && ent->r.client->team != team && GS_MatchState() == MATCH_STATE_PLAYTIME ) { G_Printf("Sending teamchange to MM, team %d to team %d\n", ent->r.client->team, team ); G_AddPlayerReport( ent, false ); // trap_MR_SendPartialReport(); } // clear scores at changing team memset( &ent->r.client->level.stats, 0, sizeof( ent->r.client->level.stats ) ); memset( &ent->r.client->teamstate, 0, sizeof( ent->r.client->teamstate ) ); ent->r.client->team = team; ent->r.client->teamstate.timeStamp = level.time; G_Teams_UnInvitePlayer( team, ent ); G_ClientRespawn( ent, true ); // make ghost using G_ClientRespawn so team is updated at ghosting G_SpawnQueue_AddClient( ent ); level.ready[PLAYERNUM( ent )] = false; G_Match_CheckReadys(); G_UpdatePlayerMatchMsg( ent ); }
/* * G_CheckRoundLimit */ static void G_CheckRoundLimit() { int i; g_edict_t *ent; g_client_t *cl; if (g_level.round_num >= (unsigned int) g_level.round_limit) { // enforce round_limit gi.BroadcastPrint(PRINT_HIGH, "Roundlimit hit\n"); G_EndLevel(); return; } // or attempt to re-join previously active players for (i = 0; i < sv_max_clients->integer; i++) { if (!g_game.edicts[i + 1].in_use) continue; ent = &g_game.edicts[i + 1]; cl = ent->client; if (cl->persistent.round_num != g_level.round_num) continue; // they were intentionally spectating, skip them if (g_level.teams || g_level.ctf) { // rejoin a team if (cl->persistent.team) G_AddClientToTeam(ent, cl->persistent.team->name); else G_AddClientToTeam(ent, G_SmallestTeam()->name); } else // just rejoin the game cl->persistent.spectator = false; G_ClientRespawn(ent, false); } }
/* * G_SpawnQueue_ReleaseTeamQueue */ void G_SpawnQueue_ReleaseTeamQueue( int team ) { g_teamspawnqueue_t *queue; edict_t *ent; int count; bool ghost; if( team < TEAM_SPECTATOR || team >= GS_MAX_TEAMS ) return; queue = &g_spawnQueues[team]; if( queue->start >= queue->head ) return; // try to spawn them for( count = 0; ( queue->start < queue->head ) && ( count < gs.maxclients ); queue->start++, count++ ) { if( queue->list[queue->start % MAX_CLIENTS] <= 0 || queue->list[queue->start % MAX_CLIENTS] > gs.maxclients ) continue; ent = &game.edicts[queue->list[queue->start % MAX_CLIENTS]]; ghost = false; if( team == TEAM_SPECTATOR || ent->r.client->teamstate.is_coach ) ghost = true; G_ClientRespawn( ent, ghost ); // when spawning inside spectator team bring up the chase camera if( team == TEAM_SPECTATOR && !ent->r.client->resp.chase.active ) G_ChasePlayer( ent, NULL, false, 0 ); } }
/* * G_ClientBegin * * Called when a client has finished connecting, and is ready * to be placed into the game. This will happen every level load. */ void G_ClientBegin(g_edict_t *ent) { char welcome[256]; int player_num = ent - g_game.edicts - 1; ent->client = g_game.clients + player_num; G_InitEdict(ent); G_InitClientLocals(ent->client); VectorClear(ent->client->cmd_angles); ent->client->persistent.first_frame = g_level.frame_num; // force spectator if match or rounds if (g_level.match || g_level.rounds) ent->client->persistent.spectator = true; else if (g_level.teams || g_level.ctf) { if (g_auto_join->value) G_AddClientToTeam(ent, G_SmallestTeam()->name); else ent->client->persistent.spectator = true; } // spawn them in G_ClientRespawn(ent, true); if (g_level.intermission_time) { G_ClientToIntermission(ent); } else { memset(welcome, 0, sizeof(welcome)); snprintf(welcome, sizeof(welcome), "^2Welcome to ^7http://quake2world.net\n" "^2Gameplay is ^1%s\n", G_GameplayName(g_level.gameplay)); if (g_level.teams) strncat(welcome, "^2Teams are enabled\n", sizeof(welcome)); if (g_level.ctf) strncat(welcome, "^2CTF is enabled\n", sizeof(welcome)); if (g_voting->value) strncat(welcome, "^2Voting is allowed\n", sizeof(welcome)); gi.ClientCenterPrint(ent, "%s", welcome); } // make sure all view stuff is valid G_ClientEndFrame(ent); srand(time(NULL)); // set random seed }
/* * G_BeginIntermission */ static void G_BeginIntermission(const char *map) { int i; g_edict_t *ent, *client; if (g_level.intermission_time) return; // already activated g_level.intermission_time = g_level.time; // respawn any dead clients for (i = 0; i < sv_max_clients->integer; i++) { client = g_game.edicts + 1 + i; if (!client->in_use) continue; if (client->health <= 0) G_ClientRespawn(client, false); } // find an intermission spot ent = G_Find(NULL, FOFS(class_name), "info_player_intermission"); if (!ent) { // map does not have an intermission point ent = G_Find(NULL, FOFS(class_name), "info_player_start"); if (!ent) ent = G_Find(NULL, FOFS(class_name), "info_player_deathmatch"); } VectorCopy(ent->s.origin, g_level.intermission_origin); VectorCopy(ent->s.angles, g_level.intermission_angle); // move all clients to the intermission point for (i = 0; i < sv_max_clients->integer; i++) { client = g_game.edicts + 1 + i; if (!client->in_use) continue; G_ClientToIntermission(client); } // play a dramatic sound effect gi.PositionedSound(g_level.intermission_origin, g_game.edicts, gi.SoundIndex("weapons/bfg/hit"), ATTN_NORM); // stay on same level if not provided g_level.changemap = map && *map ? map : g_level.name; }
/* * ClientDisconnect * Called when a player drops from the server. * Will not be called between levels. */ void ClientDisconnect( edict_t *ent, const char *reason ) { int team; if( !ent->r.client || !ent->r.inuse ) return; // always report in RACE mode if( GS_RaceGametype() || ( ent->r.client->team != TEAM_SPECTATOR && ( GS_MatchState() == MATCH_STATE_PLAYTIME || GS_MatchState() == MATCH_STATE_POSTMATCH ) ) ) G_AddPlayerReport( ent, GS_MatchState() == MATCH_STATE_POSTMATCH ); for( team = TEAM_PLAYERS; team < GS_MAX_TEAMS; team++ ) G_Teams_UnInvitePlayer( team, ent ); if( !level.gametype.disableObituaries || !(ent->r.svflags & SVF_FAKECLIENT ) ) { if( !reason ) G_PrintMsg( NULL, "%s" S_COLOR_WHITE " disconnected\n", ent->r.client->netname ); else G_PrintMsg( NULL, "%s" S_COLOR_WHITE " disconnected (%s" S_COLOR_WHITE ")\n", ent->r.client->netname, reason ); } // send effect if( ent->s.team > TEAM_SPECTATOR ) G_TeleportEffect( ent, false ); ent->r.client->team = TEAM_SPECTATOR; G_ClientRespawn( ent, true ); // respawn as ghost ent->movetype = MOVETYPE_NOCLIP; // allow freefly // let the gametype scripts know this client just disconnected G_Gametype_ScoreEvent( ent->r.client, "disconnect", NULL ); G_FreeAI( ent ); AI_EnemyRemoved( ent ); ent->r.inuse = false; ent->r.svflags = SVF_NOCLIENT; memset( ent->r.client, 0, sizeof( *ent->r.client ) ); ent->r.client->ps.playerNum = PLAYERNUM( ent ); trap_ConfigString( CS_PLAYERINFOS+PLAYERNUM( ent ), "" ); GClip_UnlinkEntity( ent ); G_Match_CheckReadys(); }
void G_Gametype_GENERIC_SetUpEndMatch( void ) { edict_t *ent; level.gametype.readyAnnouncementEnabled = false; level.gametype.scoreAnnouncementEnabled = false; level.gametype.pickableItemsMask = 0; // disallow item pickup level.gametype.countdownEnabled = false; for( ent = game.edicts + 1; PLAYERNUM( ent ) < gs.maxclients; ent++ ) { if( ent->r.inuse && trap_GetClientState( PLAYERNUM( ent ) ) >= CS_SPAWNED ) G_ClientRespawn( ent, true ); } G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_POSTMATCH_GAMEOVER_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); }
void G_Gametype_GENERIC_SetUpMatch( void ) { int i; level.gametype.readyAnnouncementEnabled = false; level.gametype.scoreAnnouncementEnabled = true; level.gametype.countdownEnabled = true; level.gametype.pickableItemsMask = ( level.gametype.spawnableItemsMask|level.gametype.dropableItemsMask ); if( GS_Instagib() ) level.gametype.pickableItemsMask &= ~G_INSTAGIB_NEGATE_ITEMMASK; // clear player stats and scores, team scores and respawn clients in team lists for( i = TEAM_PLAYERS; i < GS_MAX_TEAMS; i++ ) { int j; g_teamlist_t *team = &teamlist[i]; memset( &team->stats, 0, sizeof( team->stats ) ); // respawn all clients inside the playing teams for( j = 0; j < team->numplayers; j++ ) { edict_t *ent = &game.edicts[ team->playerIndices[j] ]; G_ClientClearStats( ent ); G_ClientRespawn( ent, false ); } } // set items to be spawned with a delay G_Items_RespawnByType( IT_ARMOR, ARMOR_RA, 15 ); G_Items_RespawnByType( IT_ARMOR, ARMOR_RA, 15 ); G_Items_RespawnByType( IT_HEALTH, HEALTH_MEGA, 15 ); G_Items_RespawnByType( IT_HEALTH, HEALTH_ULTRA, 15 ); G_Items_RespawnByType( IT_POWERUP, 0, brandom( 20, 40 ) ); G_Match_FreeBodyQueue(); G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_FIGHT_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, false, NULL ); G_CenterPrintMsg( NULL, "FIGHT!" ); }
/* * ClientBegin * called when a client has finished connecting, and is ready * to be placed into the game. This will happen every level load. */ void ClientBegin( edict_t *ent ) { gclient_t *client = ent->r.client; const char *mm_login; memset( &client->ucmd, 0, sizeof( client->ucmd ) ); memset( &client->level, 0, sizeof( client->level ) ); client->level.timeStamp = level.time; G_Client_UpdateActivity( client ); // activity detected client->team = TEAM_SPECTATOR; G_ClientRespawn( ent, true ); // respawn as ghost ent->movetype = MOVETYPE_NOCLIP; // allow freefly G_UpdatePlayerMatchMsg( ent ); mm_login = Info_ValueForKey( client->userinfo, "cl_mm_login" ); if( mm_login && *mm_login && client->mm_session > 0 ) { G_PrintMsg( NULL, "%s" S_COLOR_WHITE " (" S_COLOR_YELLOW "%s" S_COLOR_WHITE ") entered the game\n", client->netname, mm_login ); } else { if( !level.gametype.disableObituaries || !(ent->r.svflags & SVF_FAKECLIENT ) ) G_PrintMsg( NULL, "%s" S_COLOR_WHITE " entered the game\n", client->netname ); } client->level.respawnCount = 0; // clear respawncount client->connecting = false; // schedule the next scoreboard update client->level.scoreboard_time = game.realtime + scoreboardInterval - ( game.realtime%scoreboardInterval ); AI_EnemyAdded( ent ); G_ClientEndSnapFrame( ent ); // make sure all view stuff is valid // let the gametype scripts now this client just entered the level G_Gametype_ScoreEvent( client, "enterGame", NULL ); }
/* * Cmd_ChaseCam_f */ void Cmd_ChaseCam_f( edict_t *ent ) { bool team_only; const char *arg1; if( ent->s.team != TEAM_SPECTATOR && !ent->r.client->teamstate.is_coach ) { // racesow - changed from G_Teams_JoinTeam( ent, TEAM_SPECTATOR ) as this makes // a second G_ChasePlayer call with followmode 0, thereby overriding the chasecam mode set here ent->r.client->team = TEAM_SPECTATOR; G_ClientRespawn( ent, true ); // !racesow if( !CheckFlood( ent, false )) { // prevent 'joined spectators' spam G_PrintMsg( NULL, "%s%s joined the %s%s team.\n", ent->r.client->netname, S_COLOR_WHITE, GS_TeamName( ent->s.team ), S_COLOR_WHITE ); } } // & 1 = scorelead // & 2 = powerups // & 4 = objectives // & 8 = fragger if( ent->r.client->teamstate.is_coach && GS_TeamBasedGametype() ) team_only = true; else team_only = false; arg1 = trap_Cmd_Argv( 1 ); if( trap_Cmd_Argc() < 2 ) { G_ChasePlayer( ent, NULL, team_only, 0 ); } else if( !Q_stricmp( arg1, "auto" ) ) { G_PrintMsg( ent, "Chasecam mode is 'auto'. It will follow the score leader when no powerup nor flag is carried.\n" ); G_ChasePlayer( ent, NULL, team_only, 7 ); } else if( !Q_stricmp( arg1, "carriers" ) ) { G_PrintMsg( ent, "Chasecam mode is 'carriers'. It will switch to flag or powerup carriers when any of these items is picked up.\n" ); G_ChasePlayer( ent, NULL, team_only, 6 ); } else if( !Q_stricmp( arg1, "powerups" ) ) { G_PrintMsg( ent, "Chasecam mode is 'powerups'. It will switch to powerup carriers when any of these items is picked up.\n" ); G_ChasePlayer( ent, NULL, team_only, 2 ); } else if( !Q_stricmp( arg1, "objectives" ) ) { G_PrintMsg( ent, "Chasecam mode is 'objectives'. It will switch to objectives carriers when any of these items is picked up.\n" ); G_ChasePlayer( ent, NULL, team_only, 4 ); } else if( !Q_stricmp( arg1, "score" ) ) { G_PrintMsg( ent, "Chasecam mode is 'score'. It will always follow the player with the best score.\n" ); G_ChasePlayer( ent, NULL, team_only, 1 ); } else if( !Q_stricmp( arg1, "fragger" ) ) { G_PrintMsg( ent, "Chasecam mode is 'fragger'. The last fragging player will be followed.\n" ); G_ChasePlayer( ent, NULL, team_only, 8 ); } else if( !Q_stricmp( arg1, "help" ) ) { G_PrintMsg( ent, "Chasecam modes:\n" ); G_PrintMsg( ent, "- 'auto': Chase the score leader unless there's an objective carrier or a powerup carrier.\n" ); G_PrintMsg( ent, "- 'carriers': User has pov control unless there's an objective carrier or a powerup carrier.\n" ); G_PrintMsg( ent, "- 'objectives': User has pov control unless there's an objective carrier.\n" ); G_PrintMsg( ent, "- 'powerups': User has pov control unless there's a flag carrier.\n" ); G_PrintMsg( ent, "- 'score': Always follow the score leader. User has no pov control.\n" ); G_PrintMsg( ent, "- 'none': Disable chasecam.\n" ); return; } else { G_ChasePlayer( ent, arg1, team_only, 0 ); } G_Teams_LeaveChallengersQueue( ent ); }
/* * G_CheckRules */ static void G_CheckRules(void) { int i, seconds; g_client_t *cl; if (g_level.intermission_time) return; // match mode, no match, or countdown underway g_level.warmup = g_level.match && (!g_level.match_time || g_level.match_time > g_level.time); // arena mode, no round, or countdown underway g_level.warmup |= g_level.rounds && (!g_level.round_time || g_level.round_time > g_level.time); if (g_level.start_match && g_level.time >= g_level.match_time) { // players have readied, begin match g_level.start_match = false; g_level.warmup = false; for (i = 0; i < sv_max_clients->integer; i++) { if (!g_game.edicts[i + 1].in_use) continue; G_ClientRespawn(&g_game.edicts[i + 1], false); } gi.Sound(&g_game.edicts[0], gi.SoundIndex("world/teleport"), ATTN_NONE); gi.BroadcastPrint(PRINT_HIGH, "Match has started\n"); } if (g_level.start_round && g_level.time >= g_level.round_time) { // pre-game expired, begin round g_level.start_round = false; g_level.warmup = false; for (i = 0; i < sv_max_clients->integer; i++) { if (!g_game.edicts[i + 1].in_use) continue; G_ClientRespawn(&g_game.edicts[i + 1], false); } gi.Sound(&g_game.edicts[0], gi.SoundIndex("world/teleport"), ATTN_NONE); gi.BroadcastPrint(PRINT_HIGH, "Round has started\n"); } seconds = g_level.time; if (g_level.rounds) { if (g_level.round_time > g_level.time) // round about to start, show pre-game countdown seconds = g_level.round_time - g_level.time; else if (g_level.round_time) seconds = g_level.time - g_level.round_time; // round started, count up else seconds = -1; } else if (g_level.match) { if (g_level.match_time > g_level.time) // match about to start, show pre-game countdown seconds = g_level.match_time - g_level.time; else if (g_level.match_time) { if (g_level.time_limit) // count down to time_limit seconds = g_level.match_time + g_level.time_limit * 60 - g_level.time; else seconds = g_level.time - g_level.match_time; // count up } else seconds = -1; } if (g_level.time_limit) { // check time_limit float t = g_level.time; if (g_level.match) // for matches t = g_level.time - g_level.match_time; else if (g_level.rounds) // and for rounds t = g_level.time - g_level.round_time; if (t >= g_level.time_limit * 60) { gi.BroadcastPrint(PRINT_HIGH, "Timelimit hit\n"); G_EndLevel(); return; } seconds = g_level.time_limit * 60 - t; // count down } if (g_level.frame_num % gi.frame_rate == 0) // send time updates once per second gi.ConfigString(CS_TIME, (g_level.warmup ? "Warmup" : G_FormatTime(seconds))); if (!g_level.ctf && g_level.frag_limit) { // check frag_limit if (g_level.teams) { // check team scores if (g_team_good.score >= g_level.frag_limit || g_team_evil.score >= g_level.frag_limit) { gi.BroadcastPrint(PRINT_HIGH, "Fraglimit hit\n"); G_EndLevel(); return; } } else { // or individual scores for (i = 0; i < sv_max_clients->integer; i++) { cl = g_game.clients + i; if (!g_game.edicts[i + 1].in_use) continue; if (cl->persistent.score >= g_level.frag_limit) { gi.BroadcastPrint(PRINT_HIGH, "Fraglimit hit\n"); G_EndLevel(); return; } } } } if (g_level.ctf && g_level.capture_limit) { // check capture limit if (g_team_good.captures >= g_level.capture_limit || g_team_evil.captures >= g_level.capture_limit) { gi.BroadcastPrint(PRINT_HIGH, "Capturelimit hit\n"); G_EndLevel(); return; } } if (g_gameplay->modified) { // change gameplay, fix items, respawn clients g_gameplay->modified = false; g_level.gameplay = G_GameplayByName(g_gameplay->string); gi.ConfigString(CS_GAMEPLAY, va("%d", g_level.gameplay)); G_RestartGame(false); // reset all clients gi.BroadcastPrint(PRINT_HIGH, "Gameplay has changed to %s\n", G_GameplayName(g_level.gameplay)); } if (g_gravity->modified) { // send gravity config string g_gravity->modified = false; g_level.gravity = g_gravity->integer; gi.ConfigString(CS_GRAVITY, va("%d", g_level.gravity)); } if (g_teams->modified) { // reset teams, scores g_teams->modified = false; g_level.teams = g_teams->integer; gi.ConfigString(CS_TEAMS, va("%d", g_level.teams)); gi.BroadcastPrint(PRINT_HIGH, "Teams have been %s\n", g_level.teams ? "enabled" : "disabled"); G_RestartGame(true); } if (g_ctf->modified) { // reset teams, scores g_ctf->modified = false; g_level.ctf = g_ctf->integer; gi.ConfigString(CS_CTF, va("%d", g_level.ctf)); gi.BroadcastPrint(PRINT_HIGH, "CTF has been %s\n", g_level.ctf ? "enabled" : "disabled"); G_RestartGame(true); } if (g_match->modified) { // reset scores g_match->modified = false; g_level.match = g_match->integer; gi.ConfigString(CS_MATCH, va("%d", g_level.match)); g_level.warmup = g_level.match; // toggle warmup gi.BroadcastPrint(PRINT_HIGH, "Match has been %s\n", g_level.match ? "enabled" : "disabled"); G_RestartGame(false); } if (g_rounds->modified) { // reset scores g_rounds->modified = false; g_level.rounds = g_rounds->integer; gi.ConfigString(CS_ROUNDS, va("%d", g_level.rounds)); g_level.warmup = g_level.rounds; // toggle warmup gi.BroadcastPrint(PRINT_HIGH, "Rounds have been %s\n", g_level.rounds ? "enabled" : "disabled"); G_RestartGame(false); } if (g_cheats->modified) { // notify when cheats changes g_cheats->modified = false; gi.BroadcastPrint(PRINT_HIGH, "Cheats have been %s\n", g_cheats->integer ? "enabled" : "disabled"); } if (g_frag_limit->modified) { g_frag_limit->modified = false; g_level.frag_limit = g_frag_limit->integer; gi.BroadcastPrint(PRINT_HIGH, "Fraglimit has been changed to %d\n", g_level.frag_limit); } if (g_round_limit->modified) { g_round_limit->modified = false; g_level.round_limit = g_round_limit->integer; gi.BroadcastPrint(PRINT_HIGH, "Roundlimit has been changed to %d\n", g_level.round_limit); } if (g_capture_limit->modified) { g_capture_limit->modified = false; g_level.capture_limit = g_capture_limit->integer; gi.BroadcastPrint(PRINT_HIGH, "Capturelimit has been changed to %d\n", g_level.capture_limit); } if (g_time_limit->modified) { g_time_limit->modified = false; g_level.time_limit = g_time_limit->value; gi.BroadcastPrint(PRINT_HIGH, "Timelimit has been changed to %d\n", (int) g_level.time_limit); } }
/* * G_RestartGame * * For normal games, this just means reset scores and respawn. * For match games, this means cancel the match and force everyone * to ready again. Teams are only reset when teamz is true. */ static void G_RestartGame(boolean_t teamz) { int i; g_edict_t *ent; g_client_t *cl; if (g_level.match_time) g_level.match_num++; if (g_level.round_time) g_level.round_num++; for (i = 0; i < sv_max_clients->integer; i++) { // reset clients if (!g_game.edicts[i + 1].in_use) continue; ent = &g_game.edicts[i + 1]; cl = ent->client; cl->persistent.ready = false; // back to warmup cl->persistent.score = 0; cl->persistent.captures = 0; if (teamz) // reset teams cl->persistent.team = NULL; // determine spectator or team affiliations if (g_level.match) { if (cl->persistent.match_num == g_level.match_num) cl->persistent.spectator = false; else cl->persistent.spectator = true; } else if (g_level.rounds) { if (cl->persistent.round_num == g_level.round_num) cl->persistent.spectator = false; else cl->persistent.spectator = true; } if (g_level.teams || g_level.ctf) { if (!cl->persistent.team) { if (g_auto_join->value) G_AddClientToTeam(ent, G_SmallestTeam()->name); else cl->persistent.spectator = true; } } G_ClientRespawn(ent, false); } G_ResetItems(); g_level.match_time = g_level.round_time = 0; g_team_good.score = g_team_evil.score = 0; g_team_good.captures = g_team_evil.captures = 0; gi.BroadcastPrint(PRINT_HIGH, "Game restarted\n"); gi.Sound(&g_game.edicts[0], gi.SoundIndex("world/teleport"), ATTN_NONE); }
/* * G_SpawnQueue_Think */ void G_SpawnQueue_Think( void ) { int team, maxCount, count, spawnSystem; g_teamspawnqueue_t *queue; edict_t *ent; bool ghost; for( team = TEAM_SPECTATOR; team < GS_MAX_TEAMS; team++ ) { queue = &g_spawnQueues[team]; // if the system is limited, set limits maxCount = MAX_CLIENTS; spawnSystem = queue->system; clamp( spawnSystem, SPAWNSYSTEM_INSTANT, SPAWNSYSTEM_HOLD ); switch( spawnSystem ) { case SPAWNSYSTEM_INSTANT: default: break; case SPAWNSYSTEM_WAVES: if( queue->nextWaveTime > level.time ) { maxCount = 0; } else { maxCount = ( queue->wave_maxcount < 1 ) ? gs.maxclients : queue->wave_maxcount; // max count per reinforcement wave queue->nextWaveTime = level.time + ( queue->wave_time * 1000 ); } break; case SPAWNSYSTEM_HOLD: maxCount = 0; // players wait to be spawned elsewhere break; } if( maxCount <= 0 ) continue; if( queue->start >= queue->head ) continue; // try to spawn them for( count = 0; ( queue->start < queue->head ) && ( count < maxCount ); queue->start++, count++ ) { if( queue->list[queue->start % MAX_CLIENTS] <= 0 || queue->list[queue->start % MAX_CLIENTS] > gs.maxclients ) continue; ent = &game.edicts[queue->list[queue->start % MAX_CLIENTS]]; ghost = false; if( team == TEAM_SPECTATOR || ent->r.client->teamstate.is_coach ) ghost = true; G_ClientRespawn( ent, ghost ); // when spawning inside spectator team bring up the chase camera if( team == TEAM_SPECTATOR && !ent->r.client->resp.chase.active ) G_ChasePlayer( ent, NULL, false, 0 ); } } }