/* * 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_ChasePlayer */ void G_ChasePlayer( edict_t *ent, const char *name, bool teamonly, int followmode ) { int i; edict_t *e; gclient_t *client; int targetNum = -1; int oldTarget; bool can_follow = true; char colorlessname[MAX_NAME_BYTES]; client = ent->r.client; oldTarget = client->resp.chase.target; if( teamonly && !client->teamstate.is_coach ) can_follow = false; if( !can_follow && followmode ) { G_PrintMsg( ent, "Chasecam follow mode unavailable\n" ); followmode = false; } if( ent->r.client->resp.chase.followmode && !followmode ) G_PrintMsg( ent, "Disabling chasecam follow mode\n" ); // always disable chasing as a start memset( &client->resp.chase, 0, sizeof( chasecam_t ) ); // locate the requested target if( name && name[0] ) { // find it by player names for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !G_Chase_IsValidTarget( ent, e, teamonly ) ) continue; Q_strncpyz( colorlessname, COM_RemoveColorTokens( e->r.client->netname ), sizeof(colorlessname) ); if( !Q_stricmp( COM_RemoveColorTokens( name ), colorlessname ) ) { targetNum = PLAYERNUM( e ); break; } } // didn't find it by name, try by numbers if( targetNum == -1 ) { i = atoi( name ); if( i >= 0 && i < gs.maxclients ) { e = game.edicts + 1 + i; if( G_Chase_IsValidTarget( ent, e, teamonly ) ) targetNum = PLAYERNUM( e ); } } if( targetNum == -1 ) G_PrintMsg( ent, "Requested chasecam target is not available\n" ); } // try to reuse old target if we didn't find a valid one if( targetNum == -1 && oldTarget > 0 && oldTarget < gs.maxclients ) { e = game.edicts + 1 + oldTarget; if( G_Chase_IsValidTarget( ent, e, teamonly ) ) targetNum = PLAYERNUM( e ); } // if we still don't have a target, just pick the first valid one if( targetNum == -1 ) { for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !G_Chase_IsValidTarget( ent, e, teamonly ) ) continue; targetNum = PLAYERNUM( e ); break; } } // make the client a ghost G_GhostClient( ent ); if( targetNum != -1 ) { // we found a target, set up the chasecam client->resp.chase.target = targetNum + 1; client->resp.chase.teamonly = teamonly; client->resp.chase.followmode = followmode; G_Chase_SetChaseActive( ent, true ); } else { // stay as observer if( !teamonly ) ent->movetype = MOVETYPE_NOCLIP; client->level.showscores = false; G_Chase_SetChaseActive( ent, false ); G_CenterPrintMsg( ent, "No one to chase" ); } }
/* * 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 ); }