/* * 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 ) { G_Teams_JoinTeam( ent, TEAM_SPECTATOR ); 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_SpawnQueue_AddClient */ void G_SpawnQueue_AddClient( edict_t *ent ) { g_teamspawnqueue_t *queue; int i; if( !ent || !ent->r.client ) return; if( ENTNUM( ent ) <= 0 || ENTNUM( ent ) > gs.maxclients ) return; if( ent->r.client->team < TEAM_SPECTATOR|| ent->r.client->team >= GS_MAX_TEAMS ) return; queue = &g_spawnQueues[ent->r.client->team]; for( i = queue->start; i < queue->head; i++ ) { if( queue->list[i % MAX_CLIENTS] == ENTNUM( ent ) ) return; } G_SpawnQueue_RemoveClient( ent ); queue->list[queue->head % MAX_CLIENTS] = ENTNUM( ent ); queue->head++; if( queue->spectate_team ) G_ChasePlayer( ent, NULL, true, 0 ); }
/* * 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 ); } }
void G_Teams_Coach( edict_t *ent ) { if( GS_TeamBasedGametype() && !GS_InvidualGameType() && ent->s.team != TEAM_SPECTATOR ) { if( !teamlist[ent->s.team].has_coach ) { if( GS_MatchState() > MATCH_STATE_WARMUP && !GS_MatchPaused() ) { G_PrintMsg( ent, "Can't set coach mode with the match in progress\n" ); } else { // move to coach mode ent->r.client->teamstate.is_coach = true; G_GhostClient( ent ); ent->health = ent->max_health; ent->deadflag = DEAD_NO; G_ChasePlayer( ent, NULL, true, 0 ); //clear up his scores G_Match_Ready( ent ); // set ready and check readys memset( &ent->r.client->level.stats, 0, sizeof( ent->r.client->level.stats ) ); teamlist[ent->s.team].has_coach = true; G_PrintMsg( NULL, "%s%s is now team %s coach \n", ent->r.client->netname, S_COLOR_WHITE, GS_TeamName( ent->s.team ) ); } } else if( ent->r.client->teamstate.is_coach ) { // if you are this team coach, resign ent->r.client->teamstate.is_coach = false; G_PrintMsg( NULL, "%s%s is no longer team %s coach \n", ent->r.client->netname, S_COLOR_WHITE, GS_TeamName( ent->s.team ) ); G_Teams_SetTeam( ent, ent->s.team ); } else G_PrintMsg( ent, "Your team already has a coach.\n" ); } else G_PrintMsg( ent, "Coaching only valid while on a team in Team based Gametypes.\n" ); }
/* * ChaseStep */ void G_ChaseStep( edict_t *ent, int step ) { int i, j; int start; edict_t *newtarget = NULL; if( !ent->r.client->resp.chase.active ) return; i = start = ent->r.client->resp.chase.target; if( step == 0 ) { if( G_Chase_IsValidTarget( ent, game.edicts + i, ent->r.client->resp.chase.teamonly ) ) newtarget = game.edicts + i; else step = 1; } if( !newtarget ) { for( j = 0; j < gs.maxclients; j++ ) { i += step; if( i < 1 ) i = gs.maxclients; else if( i > gs.maxclients ) i = 1; if( i == start ) break; if( G_Chase_IsValidTarget( ent, game.edicts + i, ent->r.client->resp.chase.teamonly ) ) { newtarget = game.edicts + i; break; } } } if( newtarget ) { G_ChasePlayer( ent, va( "%i", PLAYERNUM( newtarget ) ), ent->r.client->resp.chase.teamonly, ent->r.client->resp.chase.followmode ); } }
/* * ChaseStep */ void G_ChaseStep( edict_t *ent, int step ) { int i, j, team; bool player_found; int actual; int start; edict_t *newtarget = NULL; assert( step == -1 || step == 0 || step == 1 ); if( !ent->r.client->resp.chase.active ) return; start = ent->r.client->resp.chase.target; i = -1; player_found = false; // needed to prevent an infinite loop if there are no players // find the team of the previously chased player and his index in the sorted teamlist for( team = TEAM_PLAYERS; team < GS_MAX_TEAMS; team++ ) { for( j = 0; j < teamlist[team].numplayers; j++ ) { player_found = true; if( teamlist[team].playerIndices[j] == start ) { i = j; break; } } if( j != teamlist[team].numplayers ) break; } if( step == 0 ) { // keep chasing the current player if possible if( i >= 0 && G_Chase_IsValidTarget( ent, game.edicts + start, ent->r.client->resp.chase.teamonly ) ) newtarget = game.edicts + start; else step = 1; } if( !newtarget && player_found ) { // reset the team if the previously chased player was not found if( team == GS_MAX_TEAMS ) team = TEAM_PLAYERS; for( j = 0; j < gs.maxclients; j++ ) { // at this point step is -1 or 1 i += step; // change to the previous team if we skipped before the start of this one // the loop assures empty teams before this team are skipped as well while( i < 0 ) { team--; if( team < TEAM_PLAYERS ) team = GS_MAX_TEAMS - 1; i = teamlist[team].numplayers - 1; } // similarly, change to the next team if we skipped past the end of this one while( i >= teamlist[team].numplayers ) { team++; if( team == GS_MAX_TEAMS ) team = TEAM_PLAYERS; i = 0; } actual = teamlist[team].playerIndices[i]; if( actual == start ) break; // back at the original player, no need to waste time if( G_Chase_IsValidTarget( ent, game.edicts + actual, ent->r.client->resp.chase.teamonly ) ) { newtarget = game.edicts + actual; break; } // make another step if this player is not valid } } if( newtarget ) G_ChasePlayer( ent, va( "%i", PLAYERNUM( newtarget ) ), ent->r.client->resp.chase.teamonly, ent->r.client->resp.chase.followmode ); }
/* * G_EndFrame_UpdateChaseCam */ static void G_EndFrame_UpdateChaseCam( edict_t *ent ) { edict_t *targ; int followpov; // not in chasecam if( !ent->r.client->resp.chase.active ) return; if( ( followpov = G_Chase_FindFollowPOV( ent ) ) != -1 ) ent->r.client->resp.chase.target = followpov; // is our chase target gone? targ = &game.edicts[ent->r.client->resp.chase.target]; if( !G_Chase_IsValidTarget( ent, targ, ent->r.client->resp.chase.teamonly ) ) { if( game.realtime < ent->r.client->resp.chase.timeout ) // wait for timeout return; ent->r.client->resp.chase.timeout = game.realtime + 1500; // update timeout G_ChasePlayer( ent, NULL, ent->r.client->resp.chase.teamonly, ent->r.client->resp.chase.followmode ); targ = &game.edicts[ent->r.client->resp.chase.target]; if( !G_Chase_IsValidTarget( ent, targ, ent->r.client->resp.chase.teamonly ) ) return; } ent->r.client->resp.chase.timeout = game.realtime + 1500; // update timeout if( targ == ent ) return; // free our psev buffer when in chasecam G_ClearPlayerStateEvents( ent->r.client ); // copy target playerState to me ent->r.client->ps = targ->r.client->ps; // fix some stats we don't want copied from the target ent->r.client->ps.stats[STAT_REALTEAM] = ent->s.team; ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_SCOREBOARD; ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_CHALLENGER; ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_READY; ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_SPECTEAMONLY; ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_INSTANTRESPAWN; if( ent->r.client->resp.chase.teamonly ) { ent->r.client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_SPECTEAMONLY; if( !ent->r.client->teamstate.is_coach ) ent->r.client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_SPECDEAD; // show deadcam effect } if( ent->r.client->level.showscores || GS_MatchState() >= MATCH_STATE_POSTMATCH ) ent->r.client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_SCOREBOARD; // show the scoreboard if( GS_HasChallengers() && ent->r.client->queueTimeStamp ) ent->r.client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_CHALLENGER; if( GS_MatchState() <= MATCH_STATE_WARMUP && level.ready[PLAYERNUM( ent )] ) ent->r.client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_READY; // chasecam uses PM_CHASECAM ent->r.client->ps.pmove.pm_type = PM_CHASECAM; ent->r.client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; VectorCopy( targ->s.origin, ent->s.origin ); VectorCopy( targ->s.angles, ent->s.angles ); GClip_LinkEntity( ent ); }
/* * Cmd_ChaseCam_f */ void Cmd_ChaseCam_f( edict_t *ent ) { qboolean team_only; if( ent->s.team != TEAM_SPECTATOR && !ent->r.client->teamstate.is_coach ) { G_Teams_JoinTeam( ent, TEAM_SPECTATOR ); 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 = flags if( ent->r.client->teamstate.is_coach && GS_TeamBasedGametype() ) team_only = qtrue; else team_only = qfalse; if( trap_Cmd_Argc() < 2 ) { G_ChasePlayer( ent, NULL, team_only, 0 ); } else if( !Q_stricmp( trap_Cmd_Argv( 1 ), "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( trap_Cmd_Argv( 1 ), "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( trap_Cmd_Argv( 1 ), "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( trap_Cmd_Argv( 1 ), "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( trap_Cmd_Argv( 1 ), "score" ) ) { G_PrintMsg( ent, "Chasecam mode is 'score'. It will always follow the highest fragger.\n" ); G_ChasePlayer( ent, NULL, team_only, 1 ); } else if( !Q_stricmp( trap_Cmd_Argv( 1 ), "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, trap_Cmd_Argv( 1 ), team_only, 0 ); } G_Teams_LeaveChallengersQueue( ent ); }
/* * 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 ); } } }