/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "g_local.h" #include "g_gametypes.h" //================= //G_EndFrame_UpdateChaseCam // // update chasecam follower stats & 1st person effects //================= void G_EndFrame_UpdateChaseCam( edict_t *ent ) { edict_t *targ; // not in chasecam if( !ent->r.client->chase.active ) { return; } // is our chase target gone? targ = &game.edicts[ent->r.client->chase.target]; if( trap_GetClientState(PLAYERNUM(targ)) < CS_SPAWNED || targ->s.team < TEAM_PLAYERS ) { // wait for timeout if( game.realtime < ent->r.client->chase.timeout ) return; ent->r.client->chase.active = 0; G_ChaseCamMode(ent); // Chase someone, or go to spectator return; } ent->r.client->chase.timeout = game.realtime + 2500; // update timeout // cam controls if( ent->r.client->chase.keyNext == qtrue ) { // change player ent->r.client->chase.keyNext = qfalse; ChaseNext(ent); } // free our psev buffer when in chasecam memset( ent->r.client->events, PSEV_NONE, sizeof(ent->r.client->events) ); // copy target playerState to me ent->r.client->ps = targ->r.client->ps; // set up stat chasing for the chasing message ent->r.client->ps.stats[STAT_CHASING] = ENTNUM(targ); // player num can be 0, we use entnum ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_GENERIC; ent->r.client->ps.stats[STAT_LAYOUTS] &= ~STAT_LAYOUT_INVENTORY; 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; if( targ->deadflag || ent->r.client->showscores || match.state >= MATCH_STATE_POSTMATCH ) ent->r.client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_SCOREBOARD; // show the scoreboard if( G_Gametype_hasChallengersQueue( game.gametype ) && ent->r.client->pers.queueTimeStamp ) ent->r.client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_CHALLENGER; if( match.state <= MATCH_STATE_WARMUP && match.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; ent->r.client->ps.POVnum = ENTNUM(targ); ent->r.client->ps.fov = ent->r.client->pers.fov; VectorCopy( targ->s.origin, ent->s.origin ); VectorCopy( targ->s.angles, ent->s.angles ); VectorCopy( targ->r.client->v_angle, ent->r.client->v_angle ); trap_LinkEntity(ent); } //==================== //G_EndServerFrames_UpdateChaseCam //==================== void G_EndServerFrames_UpdateChaseCam( void ) { int i; edict_t *ent; for( i = 0; i < game.maxclients; i++ ) { ent = game.edicts + 1 + i; if( !ent->r.inuse || !ent->r.client ) continue; if( trap_GetClientState(PLAYERNUM(ent)) < CS_SPAWNED || ent->s.team != TEAM_SPECTATOR ) { ent->r.client->chase.active = qfalse; } G_EndFrame_UpdateChaseCam(ent); } } //==================== //ChaseNext //==================== void ChaseNext( edict_t *ent ) { int i; edict_t *e; if( ent->s.team != TEAM_SPECTATOR ) { if( ent->r.client->chase.active ) ent->r.client->chase.active = qfalse; } if( !ent->r.client->chase.active ) return; i = ent->r.client->chase.target; do { i++; if( i > game.maxclients ) i = 1; e = game.edicts + i; if( trap_GetClientState(PLAYERNUM(e)) < CS_SPAWNED || e->s.team < TEAM_PLAYERS ) continue; if( e->r.solid != SOLID_NOT ) break; } while( i != ent->r.client->chase.target ); ent->r.client->chase.target = i; } //==================== //ChasePrev //==================== void ChasePrev( edict_t *ent ) { int i; edict_t *e; if( ent->s.team != TEAM_SPECTATOR ) { if( ent->r.client->chase.active ) ent->r.client->chase.active = qfalse; } if( !ent->r.client->chase.active ) return; i = ent->r.client->chase.target; do { i--; if( i < 1 ) i = game.maxclients; e = game.edicts + i; if( trap_GetClientState(PLAYERNUM(e)) < CS_SPAWNED || e->s.team < TEAM_PLAYERS ) continue; if( e->r.solid != SOLID_NOT ) break; } while( i != ent->r.client->chase.target ); ent->r.client->chase.target = i; } //==================== //G_ChaseCamMode //==================== void G_ChaseCamMode( edict_t *ent ) { int i; edict_t *e; gclient_t *client; client = ent->r.client; // Is already in chasecam if( client->chase.active ) return; // join spectator team if( ent->s.team != TEAM_SPECTATOR ) { G_Teams_JoinTeam( ent, TEAM_SPECTATOR ); G_PrintMsg( NULL, "%s%s joined the %s%s team.\n", ent->r.client->pers.netname, S_COLOR_WHITE, GS_TeamName(ent->s.team), S_COLOR_WHITE ); } // validate any old chase target if( client->chase.target ) { if( client->chase.target < 1 || client->chase.target >= game.maxclients ) client->chase.target = 0; else { e = &game.edicts[client->chase.target]; if( trap_GetClientState(PLAYERNUM(e)) < CS_SPAWNED || e->s.team < TEAM_PLAYERS ) { client->chase.target = 0; } } } // locate a chase target if( !client->chase.target ) { for( i = 1; i <= game.maxclients; i++ ) { e = game.edicts + i; if( trap_GetClientState(PLAYERNUM(e)) == CS_SPAWNED && e->r.solid != SOLID_NOT ) { client->chase.target = ENTNUM(e); break; } } } if( client->chase.target ) { client->chase.active = qtrue; G_UpdatePlayerMatchMsg( ent ); // shut up the match message return; } //failed: stay as observer client->chase.active = qfalse; client->ps.pmove.pm_type = PM_SPECTATOR; client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; G_CenterPrintMsg( ent, "No one to chase" ); } //==================== //Cmd_ChaseCam_f //==================== void Cmd_ChaseCam_f( edict_t *ent ) { G_ChaseCamMode( ent ); G_Teams_LeaveChallengersQueue( ent ); } //==================== //G_GoSpectator //==================== void G_SpectatorMode( edict_t *ent ) { // join spectator team if( ent->s.team != TEAM_SPECTATOR ) { G_Teams_JoinTeam( ent, TEAM_SPECTATOR ); G_PrintMsg( NULL, "%s%s joined the %s%s team.\n", ent->r.client->pers.netname, S_COLOR_WHITE, GS_TeamName(ent->s.team), S_COLOR_WHITE ); } // was in chasecam if( ent->r.client->chase.active ) { ent->r.client->chase.active = qfalse; } ent->r.client->ps.pmove.pm_type = PM_SPECTATOR; ent->r.client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; } //==================== //Cmd_Spec_f //==================== void Cmd_Spec_f( edict_t *ent ) { if( ent->s.team == TEAM_SPECTATOR && !ent->r.client->pers.queueTimeStamp ) { G_PrintMsg( ent, "You are allready a spectator.\n" ); return; } G_SpectatorMode( ent ); G_Teams_LeaveChallengersQueue( ent ); } //==================== //Cmd_SwitchChaseCamMode_f - Used by cgame for switching mode when clicking the mouse button //==================== void Cmd_SwitchChaseCamMode_f( edict_t *ent ) { if( ent->r.client->chase.active ) G_SpectatorMode( ent ); else G_ChaseCamMode( ent ); }