/* * G_GameTypes_DenyJoinTeam */ static int G_GameTypes_DenyJoinTeam( edict_t *ent, int team ) { if( team < 0 || team >= GS_MAX_TEAMS ) { G_Printf( "WARNING: 'G_GameTypes_CanJoinTeam' parsing a unrecognized team value\n" ); return ER_TEAM_INVALID; } if( team == TEAM_SPECTATOR ) return ER_TEAM_OK; if( GS_MatchState() > MATCH_STATE_PLAYTIME ) return ER_TEAM_MATCHSTATE; // waiting for chanllengers queue to be executed if( GS_HasChallengers() && game.realtime < level.spawnedTimeStamp + (unsigned)( G_CHALLENGERS_MIN_JOINTEAM_MAPTIME + game.snapFrameTime ) ) return ER_TEAM_CHALLENGERS; // force eveyone to go through queue so things work on map change if( GS_HasChallengers() && !ent->r.client->queueTimeStamp ) return ER_TEAM_CHALLENGERS; //see if team is locked if( G_Teams_TeamIsLocked( team ) && !G_Teams_PlayerIsInvited( team, ent ) ) return ER_TEAM_LOCKED; if( GS_TeamBasedGametype() ) { if( team >= TEAM_ALPHA && team < GS_MAX_TEAMS ) { // see if team is full int count = teamlist[team].numplayers; if( ( count + 1 > level.gametype.maxPlayersPerTeam && level.gametype.maxPlayersPerTeam > 0 ) || ( count + 1 > g_teams_maxplayers->integer && g_teams_maxplayers->integer > 0 ) ) return ER_TEAM_FULL; if( !g_teams_allow_uneven->integer && !G_Teams_CanKeepEvenTeam( ent->s.team, team ) ) return ER_TEAM_UNEVEN; return ER_TEAM_OK; } else { return ER_TEAM_INVALID; } } else if( team == TEAM_PLAYERS ) { return ER_TEAM_OK; } return ER_TEAM_INVALID; }
/* * G_Teams_JoinChallengersQueue */ void G_Teams_JoinChallengersQueue( edict_t *ent ) { int pos = 0; edict_t *e; if( !GS_HasChallengers() ) { ent->r.client->queueTimeStamp = 0; return; } if( ent->s.team != TEAM_SPECTATOR ) return; // enter the challengers queue if( !ent->r.client->queueTimeStamp ) { // enter the line ent->r.client->queueTimeStamp = game.realtime; for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !e->r.inuse || !e->r.client || trap_GetClientState( PLAYERNUM(e) ) < CS_SPAWNED ) continue; if( !e->r.client->queueTimeStamp || e->s.team != TEAM_SPECTATOR ) continue; // if there are other players with the same timestamp, increase ours if( e->r.client->queueTimeStamp >= ent->r.client->queueTimeStamp ) ent->r.client->queueTimeStamp = e->r.client->queueTimeStamp+1; if( e->r.client->queueTimeStamp < ent->r.client->queueTimeStamp ) pos++; } G_PrintMsg( ent, "%sYou entered the challengers queue in position %i\n", S_COLOR_CYAN, pos+1 ); G_UpdatePlayerMatchMsg( ent ); } }
/* * G_Teams_AdvanceChallengersQueue */ void G_Teams_AdvanceChallengersQueue( void ) { int i, team, loserscount, winnerscount, playerscount = 0; int maxscore = 999999; edict_t *won, *e; int START_TEAM = TEAM_PLAYERS, END_TEAM = TEAM_PLAYERS+1; if( !GS_HasChallengers() ) return; G_Teams_UpdateMembersList(); if( GS_TeamBasedGametype() ) { START_TEAM = TEAM_ALPHA; END_TEAM = GS_MAX_TEAMS; } // assign new timestamps to all the players inside teams for( team = START_TEAM; team < END_TEAM; team++ ) { playerscount += teamlist[team].numplayers; } if( !playerscount ) return; loserscount = 0; if( playerscount > 1 ) { loserscount = (int)( playerscount / 2 ); } winnerscount = playerscount - loserscount; // put everyone who just played out of the challengers queue for( team = START_TEAM; team < END_TEAM; team++ ) { for( i = 0; i < teamlist[team].numplayers; i++ ) { e = game.edicts + teamlist[team].playerIndices[i]; e->r.client->queueTimeStamp = 0; } } // put (back) the best scoring players in first positions of challengers queue for( i = 0; i < winnerscount; i++ ) { won = G_Teams_BestScoreBelow( maxscore ); if( won ) { maxscore = won->r.client->level.stats.score; won->r.client->queueTimeStamp = 1 + ( winnerscount-i ); // never have 2 players with the same timestamp } } }
/* * G_Teams_ExecuteChallengersQueue */ void G_Teams_ExecuteChallengersQueue( void ) { edict_t *ent; edict_t **challengers; bool restartmatch = false; // Medar fixme: this is only really makes sense, if playerlimit per team is one if( GS_MatchState() == MATCH_STATE_PLAYTIME ) return; if( !GS_HasChallengers() ) return; if( game.realtime < level.spawnedTimeStamp + G_CHALLENGERS_MIN_JOINTEAM_MAPTIME ) { static int time, lasttime; time = (int)( ( G_CHALLENGERS_MIN_JOINTEAM_MAPTIME - ( game.realtime - level.spawnedTimeStamp ) )*0.001 ); if( lasttime && time == lasttime ) return; lasttime = time; if( lasttime ) G_CenterPrintFormatMsg( NULL, "Waiting... %s", va( "%i", lasttime ), NULL ); else G_CenterPrintMsg( NULL, "" ); return; } // pick players in join order and try to put them in the // game until we get the first refused one. challengers = G_Teams_ChallengersQueue(); if( challengers ) { int i; for( i = 0; challengers[i]; i++ ) { ent = challengers[i]; if( !G_Teams_JoinAnyTeam( ent, true ) ) break; // if we successfully execute the challengers queue during the countdown, revert to warmup if( GS_MatchState() == MATCH_STATE_COUNTDOWN ) { restartmatch = true; } } } if( restartmatch == true ) { G_Match_Autorecord_Cancel(); G_Match_LaunchState( MATCH_STATE_WARMUP ); } }
/* * G_Teams_LeaveChallengersQueue */ void G_Teams_LeaveChallengersQueue( edict_t *ent ) { if( !GS_HasChallengers() ) { ent->r.client->queueTimeStamp = 0; return; } if( ent->s.team != TEAM_SPECTATOR ) return; // exit the challengers queue if( ent->r.client->queueTimeStamp ) { ent->r.client->queueTimeStamp = 0; G_PrintMsg( ent, "%sYou left the challengers queue\n", S_COLOR_CYAN ); G_UpdatePlayerMatchMsg( ent ); } }
/* * G_Teams_JoinAnyTeam - find us a team since we are too lazy to do ourselves */ bool G_Teams_JoinAnyTeam( edict_t *ent, bool silent ) { int best_numplayers = gs.maxclients + 1, best_score = 999999; int i, team = -1; bool wasinqueue = ( ent->r.client->queueTimeStamp != 0 ); G_Teams_UpdateMembersList(); // make sure we have up-to-date data //depending on the gametype, of course if( !GS_TeamBasedGametype() ) { if( ent->s.team == TEAM_PLAYERS ) { if( !silent ) { G_PrintMsg( ent, "You are already in %s team\n", GS_TeamName( TEAM_PLAYERS ) ); } return false; } if( G_Teams_JoinTeam( ent, TEAM_PLAYERS ) ) { if( !silent ) { G_PrintMsg( NULL, "%s%s joined the %s team.\n", ent->r.client->netname, S_COLOR_WHITE, GS_TeamName( ent->s.team ) ); } } return true; } else { //team based //find the available team with smaller player count or worse score for( i = TEAM_ALPHA; i < GS_MAX_TEAMS; i++ ) { if( G_GameTypes_DenyJoinTeam( ent, i ) ) { continue; } if( team == -1 || teamlist[i].numplayers < best_numplayers || ( teamlist[i].numplayers == best_numplayers && teamlist[i].stats.score < best_score ) ) { best_numplayers = teamlist[i].numplayers; best_score = teamlist[i].stats.score; team = i; } } if( team == ent->s.team ) { // he is at the right team if( !silent ) { G_PrintMsg( ent, "%sCouldn't find a better team than team %s.\n", S_COLOR_WHITE, GS_TeamName( ent->s.team ) ); } return false; } if( team != -1 ) { if( G_Teams_JoinTeam( ent, team ) ) { if( !silent ) { G_PrintMsg( NULL, "%s%s joined the %s team.\n", ent->r.client->netname, S_COLOR_WHITE, GS_TeamName( ent->s.team ) ); } return true; } } if( GS_MatchState() <= MATCH_STATE_PLAYTIME && !silent ) G_Teams_JoinChallengersQueue( ent ); } // don't print message if we joined the queue if( !silent && ( !GS_HasChallengers() || wasinqueue || !ent->r.client->queueTimeStamp ) ) G_PrintMsg( ent, "You can't join the game now\n" ); return false; }
/* * 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 ); }
/* * G_SetClientStats */ void G_SetClientStats( edict_t *ent ) { gclient_t *client = ent->r.client; int team, i; if( ent->r.client->resp.chase.active ) // in chasecam it copies the other player stats return; // // layouts // client->ps.stats[STAT_LAYOUTS] = 0; // don't force scoreboard when dead during timeout if( ent->r.client->level.showscores || GS_MatchState() >= MATCH_STATE_POSTMATCH ) client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_SCOREBOARD; if( GS_TeamBasedGametype() && !GS_InvidualGameType() ) client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_TEAMTAB; if( GS_HasChallengers() && ent->r.client->queueTimeStamp ) client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_CHALLENGER; if( GS_MatchState() <= MATCH_STATE_WARMUP && level.ready[PLAYERNUM( ent )] ) client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_READY; if( G_SpawnQueue_GetSystem( ent->s.team ) == SPAWNSYSTEM_INSTANT ) client->ps.stats[STAT_LAYOUTS] |= STAT_LAYOUT_INSTANTRESPAWN; // // team // client->ps.stats[STAT_TEAM] = client->ps.stats[STAT_REALTEAM] = ent->s.team; // // health // if( ent->s.team == TEAM_SPECTATOR ) client->ps.stats[STAT_HEALTH] = STAT_NOTSET; // no health for spectator else client->ps.stats[STAT_HEALTH] = HEALTH_TO_INT( ent->health ); client->r.frags = client->ps.stats[STAT_SCORE]; // // armor // if( GS_Instagib() ) { if( g_instashield->integer ) client->ps.stats[STAT_ARMOR] = ARMOR_TO_INT( 100.0f * ( client->resp.instashieldCharge / INSTA_SHIELD_MAX ) ); else client->ps.stats[STAT_ARMOR] = 0; } else client->ps.stats[STAT_ARMOR] = ARMOR_TO_INT( client->resp.armor ); // // pickup message // if( level.time > client->resp.pickup_msg_time ) { client->ps.stats[STAT_PICKUP_ITEM] = 0; } // // frags // if( ent->s.team == TEAM_SPECTATOR ) { client->ps.stats[STAT_SCORE] = STAT_NOTSET; // no frags for spectators } else { client->ps.stats[STAT_SCORE] = ent->r.client->level.stats.score; } // // Team scores // if( GS_TeamBasedGametype() ) { // team based i = 0; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { client->ps.stats[STAT_TEAM_ALPHA_SCORE+i] = teamlist[team].stats.score; i++; } // mark the rest as not set for(; team < GS_MAX_TEAMS; team++ ) { client->ps.stats[STAT_TEAM_ALPHA_SCORE+i] = STAT_NOTSET; i++; } } else { // not team based i = 0; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { client->ps.stats[STAT_TEAM_ALPHA_SCORE+i] = STAT_NOTSET; i++; } } // spawn system client->ps.stats[STAT_NEXT_RESPAWN] = ceil( G_SpawnQueue_NextRespawnTime( client->team ) * 0.001f ); // pointed player client->ps.stats[STAT_POINTED_TEAMPLAYER] = 0; client->ps.stats[STAT_POINTED_PLAYER] = G_FindPointedPlayer( ent ); if( client->ps.stats[STAT_POINTED_PLAYER] && GS_TeamBasedGametype() ) { edict_t *e = &game.edicts[client->ps.stats[STAT_POINTED_PLAYER]]; if( e->s.team == ent->s.team ) { int pointedhealth = HEALTH_TO_INT( e->health ); int pointedarmor = 0; int available_bits = 0; bool mega = false; if( pointedhealth < 0 ) pointedhealth = 0; if( pointedhealth > 100 ) { pointedhealth -= 100; mega = true; if( pointedhealth > 100 ) pointedhealth = 100; } pointedhealth /= 3.2; if( GS_Armor_TagForCount( e->r.client->resp.armor ) ) { pointedarmor = ARMOR_TO_INT( e->r.client->resp.armor ); } if( pointedarmor > 150 ) { pointedarmor = 150; } pointedarmor /= 5; client->ps.stats[STAT_POINTED_TEAMPLAYER] = ( ( pointedhealth &0x1F )|( pointedarmor&0x3F )<<6|( available_bits&0xF )<<12 ); if( mega ) { client->ps.stats[STAT_POINTED_TEAMPLAYER] |= 0x20; } } } // last killer. ignore world and team kills if( client->teamstate.last_killer ) { edict_t *targ = ent, *attacker = client->teamstate.last_killer; client->ps.stats[STAT_LAST_KILLER] = (attacker->r.client && !GS_IsTeamDamage( &targ->s, &attacker->s ) ? ENTNUM( attacker ) : 0); } else { client->ps.stats[STAT_LAST_KILLER] = 0; } }
void G_ScoreboardMessage_AddSpectators( void ) { char entry[MAX_TOKEN_CHARS]; int i, clstate; edict_t *e; edict_t **challengers; size_t len; len = strlen( scoreboardString ); if( !len ) return; if( GS_HasChallengers() && (challengers = G_Teams_ChallengersQueue()) != NULL ) { // add the challengers Q_strncpyz( entry, "&w ", sizeof(entry) ); ADD_SCOREBOARD_ENTRY( scoreboardString, len, entry ); for( i = 0; challengers[i]; i++ ) { e = challengers[i]; //spectator tab entry if( !( e->r.client->connecting == true || trap_GetClientState( PLAYERNUM( e ) ) < CS_SPAWNED ) ) { Q_snprintfz( entry, sizeof( entry ), "%i %i ", PLAYERNUM( e ), e->r.client->r.ping > 999 ? 999 : e->r.client->r.ping ); ADD_SCOREBOARD_ENTRY( scoreboardString, len, entry ); } } } // add spectator team Q_strncpyz( entry, "&s ", sizeof(entry) ); ADD_SCOREBOARD_ENTRY( scoreboardString, len, entry ); for( i = 0; i < teamlist[TEAM_SPECTATOR].numplayers; i++ ) { e = game.edicts + teamlist[TEAM_SPECTATOR].playerIndices[i]; if( e->r.client->connecting == true || trap_GetClientState( PLAYERNUM( e ) ) < CS_SPAWNED ) continue; if( !e->r.client->queueTimeStamp ) { // not in challenger queue Q_snprintfz( entry, sizeof( entry ), "%i %i ", PLAYERNUM( e ), e->r.client->r.ping > 999 ? 999 : e->r.client->r.ping ); ADD_SCOREBOARD_ENTRY( scoreboardString, len, entry ); } } // add connecting spectators for( i = 0; i < teamlist[TEAM_SPECTATOR].numplayers; i++ ) { e = game.edicts + teamlist[TEAM_SPECTATOR].playerIndices[i]; // spectator tab entry clstate = trap_GetClientState( PLAYERNUM( e ) ); if( e->r.client->connecting == true || ( clstate >= CS_CONNECTED && clstate < CS_SPAWNED ) ) { Q_snprintfz( entry, sizeof( entry ), "%i %i ", PLAYERNUM( e ), -1 ); ADD_SCOREBOARD_ENTRY( scoreboardString, len, entry ); } } }