//========================================== // BOT_DMclass_CheckShot // Checks if shot is blocked (doesn't verify it would hit) //========================================== static bool BOT_DMclass_CheckShot( edict_t *ent, vec3_t point ) { trace_t tr; vec3_t start, forward, right, offset; if( random() > ent->ai->pers.cha.firerate ) return false; AngleVectors( ent->r.client->ps.viewangles, forward, right, NULL ); VectorSet( offset, 0, 0, ent->viewheight ); G_ProjectSource( ent->s.origin, offset, forward, right, start ); // blocked, don't shoot G_Trace( &tr, start, vec3_origin, vec3_origin, point, ent, MASK_AISOLID ); if( tr.fraction < 0.8f ) { if( tr.ent < 1 || !game.edicts[tr.ent].takedamage || game.edicts[tr.ent].movetype == MOVETYPE_PUSH ) return false; // check if the player we found is at our team if( game.edicts[tr.ent].s.team == ent->s.team && GS_TeamBasedGametype() ) return false; } return true; }
/* * G_Match_ScorelimitHit */ bool G_Match_ScorelimitHit( void ) { edict_t *e; if( GS_MatchState() != MATCH_STATE_PLAYTIME ) return false; if( g_scorelimit->integer ) { if( !GS_TeamBasedGametype() ) { for( e = game.edicts+1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !e->r.inuse ) continue; if( e->r.client->level.stats.score >= g_scorelimit->integer ) return true; } } else { int team; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { if( teamlist[team].stats.score >= g_scorelimit->integer ) return true; } } } return false; }
void G_Gametype_GENERIC_SetUpWarmup( void ) { level.gametype.readyAnnouncementEnabled = true; level.gametype.scoreAnnouncementEnabled = false; level.gametype.countdownEnabled = false; level.gametype.pickableItemsMask = ( level.gametype.spawnableItemsMask|level.gametype.dropableItemsMask ); if( GS_Instagib() ) level.gametype.pickableItemsMask &= ~G_INSTAGIB_NEGATE_ITEMMASK; if( GS_TeamBasedGametype() ) { bool any = false; int team; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { if( G_Teams_TeamIsLocked( team ) ) { G_Teams_UnLockTeam( team ); any = true; } } if( any ) G_PrintMsg( NULL, "Teams unlocked.\n" ); } else { if( G_Teams_TeamIsLocked( TEAM_PLAYERS ) ) { G_Teams_UnLockTeam( TEAM_PLAYERS ); G_PrintMsg( NULL, "Teams unlocked.\n" ); } } G_Teams_RemoveInvites(); }
/* * G_RunGametype */ void G_RunGametype( void ) { G_Teams_ExecuteChallengersQueue(); G_Teams_UpdateMembersList(); G_Match_CheckStateAbort(); G_UpdateScoreBoardMessages(); //check gametype specific rules if( game.asEngine != NULL ) GT_asCallThinkRules(); else G_Gametype_GENERIC_ThinkRules(); if( G_EachNewSecond() ) { G_CheckNumBots(); G_TickOutPowerUps(); } if( G_EachNewMinute() ) G_CheckEvenTeam(); G_Match_ScoreAnnouncement(); G_Match_ReadyAnnouncement(); if( GS_TeamBasedGametype() ) G_Teams_UpdateTeamInfoMessages(); G_asGarbageCollect( false ); }
void G_Gametype_GENERIC_SetUpCountdown( void ) { bool any = false; int team; G_Match_RemoveAllProjectiles(); G_Items_RespawnByType( 0, 0, 0 ); // respawn all items level.gametype.readyAnnouncementEnabled = false; level.gametype.scoreAnnouncementEnabled = false; level.gametype.countdownEnabled = true; level.gametype.pickableItemsMask = 0; // disallow item pickup if( GS_TeamBasedGametype() ) { for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) if( G_Teams_LockTeam( team ) ) any = true; } else { if( G_Teams_LockTeam( TEAM_PLAYERS ) ) any = true; } if( any ) G_PrintMsg( NULL, "Teams locked.\n" ); G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_GET_READY_TO_FIGHT_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); }
float BOT_DMclass_PlayerWeight( edict_t *self, edict_t *enemy ) { bool rage_mode = false; if( !enemy || enemy == self ) return 0; if( G_ISGHOSTING( enemy ) || enemy->flags & (FL_NOTARGET|FL_BUSY) ) return 0; if( self->r.client->ps.inventory[POWERUP_QUAD] || self->r.client->ps.inventory[POWERUP_SHELL] ) rage_mode = true; // don't fight against powerups. if( enemy->r.client && ( enemy->r.client->ps.inventory[POWERUP_QUAD] || enemy->r.client->ps.inventory[POWERUP_SHELL] ) ) return 0.2; //if not team based give some weight to every one if( GS_TeamBasedGametype() && ( enemy->s.team == self->s.team ) ) return 0; // if having EF_CARRIER we can assume it's someone important if( enemy->s.effects & EF_CARRIER ) return 2.0f; if( enemy == self->ai->last_attacker ) return rage_mode ? 4.0f : 1.0f; return rage_mode ? 4.0f : 0.3f; }
//============== // CG_ForceTeam //============== static int CG_ForceTeam( int entNum, int team ) { if( !GS_TeamBasedGametype() ) { if( ISVIEWERENTITY( entNum ) ) { if( cg_forceMyTeamAlpha->integer ) return TEAM_ALPHA; } else { if( cg_forceTeamPlayersTeamBeta->integer ) return TEAM_BETA; } return team; } else { int myteam = cg.predictedPlayerState.stats[STAT_TEAM]; if( cg_forceMyTeamAlpha->integer && myteam != TEAM_SPECTATOR ) { if( team == myteam ) return TEAM_ALPHA; if( team == TEAM_ALPHA ) return myteam; } return team; } }
/* * 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 ); }
static qboolean GS_CheckBladeAutoAttack( player_state_t *playerState, int timeDelta ) { vec3_t origin, dir, end; trace_t trace; entity_state_t *targ, *player; gs_weapon_definition_t *weapondef = GS_GetWeaponDef( WEAP_GUNBLADE ); if( playerState->POVnum <= 0 || (int)playerState->POVnum > gs.maxclients ) return qfalse; if( !( playerState->pmove.stats[PM_STAT_FEATURES] & PMFEAT_GUNBLADEAUTOATTACK ) ) return qfalse; VectorCopy( playerState->pmove.origin, origin ); origin[2] += playerState->viewheight; AngleVectors( playerState->viewangles, dir, NULL, NULL ); VectorMA( origin, weapondef->firedef_weak.timeout, dir, end ); // check for a player to touch module_Trace( &trace, origin, vec3_origin, vec3_origin, end, playerState->POVnum, CONTENTS_BODY, timeDelta ); if( trace.ent <= 0 || trace.ent > gs.maxclients ) return qfalse; player = module_GetEntityState( playerState->POVnum, 0 ); targ = module_GetEntityState( trace.ent, 0 ); if( !( targ->effects & EF_TAKEDAMAGE ) || targ->type != ET_PLAYER ) return qfalse; if( GS_TeamBasedGametype() && ( targ->team == player->team ) ) return qfalse; return qtrue; }
/* * 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; }
AiBaseTeamBrain *AiBaseTeamBrain::InstantiateTeamBrain( int team, const char *gametype ) { // Delegate construction to AiSquadBasedTeamBrain if( GS_TeamBasedGametype() && !GS_InvidualGameType() ) { return AiSquadBasedTeamBrain::InstantiateTeamBrain( team, gametype ); } void *mem = G_Malloc( sizeof( AiBaseTeamBrain ) ); return new(mem)AiBaseTeamBrain( team ); }
/* * G_Gametype_CanTeamDamage */ bool G_Gametype_CanTeamDamage( int damageflags ) { if( damageflags & DAMAGE_NO_PROTECTION ) return true; if( !GS_TeamBasedGametype() ) return true; return g_allow_teamdamage->integer ? true : false; }
/* * 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 } } }
/* * Cmd_Timeout_f */ static void Cmd_Timeout_f( edict_t *ent ) { int num; if( ent->s.team == TEAM_SPECTATOR || GS_MatchState() != MATCH_STATE_PLAYTIME ) return; if( GS_TeamBasedGametype() ) num = ent->s.team; else num = ENTNUM( ent )-1; if( GS_MatchPaused() && ( level.timeout.endtime - level.timeout.time ) >= 2*TIMEIN_TIME ) { G_PrintMsg( ent, "Timeout already in progress\n" ); return; } if( g_maxtimeouts->integer != -1 && level.timeout.used[num] >= g_maxtimeouts->integer ) { if( g_maxtimeouts->integer == 0 ) G_PrintMsg( ent, "Timeouts are not allowed on this server\n" ); else if( GS_TeamBasedGametype() ) G_PrintMsg( ent, "Your team doesn't have any timeouts left\n" ); else G_PrintMsg( ent, "You don't have any timeouts left\n" ); return; } G_PrintMsg( NULL, "%s%s called a timeout\n", ent->r.client->netname, S_COLOR_WHITE ); if( !GS_MatchPaused() ) G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_TIMEOUT_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); level.timeout.used[num]++; GS_GamestatSetFlag( GAMESTAT_FLAG_PAUSED, true ); level.timeout.caller = num; level.timeout.endtime = level.timeout.time + TIMEOUT_TIME + FRAMETIME; }
/* * Cmd_Timeout_f */ static void Cmd_Timein_f( edict_t *ent ) { int num; if( ent->s.team == TEAM_SPECTATOR ) return; if( !GS_MatchPaused() ) { G_PrintMsg( ent, "No timeout in progress.\n" ); return; } if( level.timeout.endtime - level.timeout.time <= 2 * TIMEIN_TIME ) { G_PrintMsg( ent, "The timeout is about to end already.\n" ); return; } if( GS_TeamBasedGametype() ) num = ent->s.team; else num = ENTNUM( ent )-1; if( level.timeout.caller != num ) { if( GS_TeamBasedGametype() ) G_PrintMsg( ent, "Your team didn't call this timeout.\n" ); else G_PrintMsg( ent, "You didn't call this timeout.\n" ); return; } level.timeout.endtime = level.timeout.time + TIMEIN_TIME + FRAMETIME; G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_TIMEIN_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); G_PrintMsg( NULL, "%s%s called a timein\n", ent->r.client->netname, S_COLOR_WHITE ); }
void AiBaseTeamBrain::OnGametypeChanged( const char *gametype ) { // First, unregister all current brains (if any) for( int team = TEAM_PLAYERS; team < GS_MAX_TEAMS; ++team ) UnregisterTeamBrain( team ); if( GS_TeamBasedGametype() ) { for( int team = TEAM_ALPHA; team < GS_MAX_TEAMS; ++team ) { RegisterTeamBrain( team, InstantiateTeamBrain( team, gametype ) ); } } else { RegisterTeamBrain( TEAM_PLAYERS, InstantiateTeamBrain( TEAM_PLAYERS, gametype ) ); } }
/* * GS_IsTeamDamage */ qboolean GS_IsTeamDamage( entity_state_t *targ, entity_state_t *attacker ) { if( !GS_TeamBasedGametype() ) return qfalse; assert( targ && attacker ); if( targ->team && attacker->team && targ->team == attacker->team && targ->number != attacker->number ) return qtrue; return qfalse; }
/* * G_WasLeading */ static bool G_WasLeading( edict_t *ent ) { int num, i; if( GS_TeamBasedGametype() ) num = ent->s.team; else num = PLAYERNUM( ent ) + 1; for( i = 0; i < MAX_CLIENTS && last_leaders[i] != 0; i++ ) { if( last_leaders[i] == num ) return true; } return false; }
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" ); }
void AiBaseTeamBrain::AcquireBotFrameAffinity( int entNum ) { // Always specify offset as zero for affinity module that = 1 (any % 1 == 0) if( AffinityModulo() == 1 ) { SetBotFrameAffinity( entNum, AffinityModulo(), 0 ); return; } if( GS_TeamBasedGametype() ) { unsigned modulo = AffinityModulo(); // Precompute static_assert( MAX_AFFINITY_OFFSET == 4, "Only two teams are supported" ); switch( modulo ) { case 4: // If the think cycle consist of 4 frames: // the Alpha team brain thinks on frame 0, bots of team Alpha think on frame 1, // the Beta team brain thinks on frame 2, bots of team Beta think on frame 3 SetBotFrameAffinity( entNum, modulo, 2 * ( (unsigned)team - TEAM_ALPHA ) + 1 ); break; case 3: // If the think cycle consist of 3 frames: // team brains think on frame 0, bots of team Alpha think on frame 1, bots of team Beta think on frame 2 SetBotFrameAffinity( entNum, modulo, 1 + (unsigned)team - TEAM_ALPHA ); break; case 2: // If the think cycle consist of 2 frames: // the Alpha team brain and team Alpha bot brains think on frame 0, // the Beta team brain an team Beta bot brains think on frame 1 SetBotFrameAffinity( entNum, modulo, (unsigned)team - TEAM_ALPHA ); break; } } else { // Select less used offset (thus, less loaded frames) unsigned chosenOffset = 0; for( unsigned i = 1; i < MAX_AFFINITY_OFFSET; ++i ) { if( affinityOffsetsInUse[chosenOffset] > affinityOffsetsInUse[i] ) { chosenOffset = i; } } affinityOffsetsInUse[chosenOffset]++; SetBotFrameAffinity( entNum, AffinityModulo(), chosenOffset ); } }
/* * G_CheckEvenTeam */ static void G_CheckEvenTeam( void ) { int max = 0; int min = gs.maxclients + 1; int uneven_team = TEAM_SPECTATOR; int i; if( GS_MatchState() >= MATCH_STATE_POSTMATCH ) return; if( !GS_TeamBasedGametype() ) return; if( g_teams_allow_uneven->integer ) return; for( i = TEAM_ALPHA; i < GS_MAX_TEAMS; i++ ) { if( max < teamlist[i].numplayers ) { max = teamlist[i].numplayers; uneven_team = i; } if( min > teamlist[i].numplayers ) min = teamlist[i].numplayers; } if( max - min > 1 ) { for( i = 0; i < teamlist[uneven_team].numplayers; i++ ) { edict_t *e = game.edicts + teamlist[uneven_team].playerIndices[i]; if( !e->r.inuse ) continue; G_CenterPrintMsg( e, "Teams are uneven. Please switch into another team." ); // FIXME: need more suitable message :P G_PrintMsg( e, "%sTeams are uneven. Please switch into another team.\n", S_COLOR_CYAN ); // FIXME: need more suitable message :P } // FIXME: switch team forcibly? } }
/* * * G_Teams_BestScoreBelow */ static edict_t *G_Teams_BestScoreBelow( int maxscore ) { int team, i; edict_t *e, *best = NULL; int bestScore = -9999999; if( GS_TeamBasedGametype() ) { for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { for( i = 0; i < teamlist[team].numplayers; i++ ) { e = game.edicts + teamlist[team].playerIndices[i]; if( e->r.client->level.stats.score > bestScore && e->r.client->level.stats.score <= maxscore && !e->r.client->queueTimeStamp ) { bestScore = e->r.client->level.stats.score; best = e; } } } } else { for( i = 0; i < teamlist[TEAM_PLAYERS].numplayers; i++ ) { e = game.edicts + teamlist[TEAM_PLAYERS].playerIndices[i]; if( e->r.client->level.stats.score > bestScore && e->r.client->level.stats.score <= maxscore && !e->r.client->queueTimeStamp ) { bestScore = e->r.client->level.stats.score; best = e; } } } return best; }
/* * CG_FlashGameWindow * * Flashes game window in case of important events (match state changes, etc) for user to notice */ static void CG_FlashGameWindow( void ) { static int oldState = -1; int newState; bool flash = false; static int oldAlphaScore, oldBetaScore; static bool scoresSet = false; // notify player of important match states newState = GS_MatchState(); if( oldState != newState ) { switch( newState ) { case MATCH_STATE_COUNTDOWN: case MATCH_STATE_PLAYTIME: case MATCH_STATE_POSTMATCH: flash = true; break; default: break; } oldState = newState; } // notify player of teams scoring in team-based gametypes if( !scoresSet || ( oldAlphaScore != cg.predictedPlayerState.stats[STAT_TEAM_ALPHA_SCORE] || oldBetaScore != cg.predictedPlayerState.stats[STAT_TEAM_BETA_SCORE] ) ) { oldAlphaScore = cg.predictedPlayerState.stats[STAT_TEAM_ALPHA_SCORE]; oldBetaScore = cg.predictedPlayerState.stats[STAT_TEAM_BETA_SCORE]; flash = scoresSet && GS_TeamBasedGametype() && !GS_InvidualGameType(); scoresSet = true; } if( flash ) trap_VID_FlashWindow( cg_flashWindowCount->integer ); }
/* * G_Match_CheckExtendPlayTime */ bool G_Match_CheckExtendPlayTime( void ) { // check for extended time/sudden death if( GS_MatchState() != MATCH_STATE_PLAYTIME ) return false; if( GS_TeamBasedGametype() && !level.forceExit ) { if( G_Match_Tied() ) { GS_GamestatSetFlag( GAMESTAT_FLAG_MATCHEXTENDED, true ); gs.gameState.stats[GAMESTAT_MATCHSTATE] = MATCH_STATE_PLAYTIME; gs.gameState.longstats[GAMELONG_MATCHSTART] = game.serverTime; if( g_match_extendedtime->value ) { if( !GS_MatchExtended() ) // first one G_AnnouncerSound( NULL, trap_SoundIndex( S_ANNOUNCER_OVERTIME_GOING_TO_OVERTIME ), GS_MAX_TEAMS, true, NULL ); else G_AnnouncerSound( NULL, trap_SoundIndex( S_ANNOUNCER_OVERTIME_OVERTIME ), GS_MAX_TEAMS, true, NULL ); G_PrintMsg( NULL, "Match tied. Timelimit extended by %i minutes!\n", g_match_extendedtime->integer ); G_CenterPrintFormatMsg( NULL, "%s MINUTE OVERTIME\n", va( "%i", g_match_extendedtime->integer ), NULL ); gs.gameState.longstats[GAMELONG_MATCHDURATION] = (unsigned int)( ( fabs( g_match_extendedtime->value ) * 60 ) * 1000 ); } else { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_OVERTIME_SUDDENDEATH_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); G_PrintMsg( NULL, "Match tied. Sudden death!\n" ); G_CenterPrintMsg( NULL, "SUDDEN DEATH" ); gs.gameState.longstats[GAMELONG_MATCHDURATION] = 0; } return true; } } return false; }
/* * Spawn_ItemTimer */ static edict_t *Spawn_ItemTimer( edict_t *ent ) { edict_t *timer; int locationTag; // location tag locationTag = G_MapLocationTAGForOrigin( ent->s.origin ); // item timer is a special entity type, carrying information about its parent item entity // which is only visible to spectators timer = G_Spawn(); timer->s.type = ET_ITEM_TIMER; timer->s.itemNum = ent->s.itemNum; timer->s.team = TEAM_SPECTATOR; timer->r.svflags = SVF_ONLYTEAM | SVF_BROADCAST; timer->r.owner = ent; timer->s.modelindex = 0; timer->s.modelindex2 = locationTag; timer->nextThink = level.time + 250; timer->think = item_timer_think; VectorCopy( ent->s.origin, timer->s.origin ); // for z-sorting if( /*( ( item->type != IT_POWERUP ) && */GS_TeamBasedGametype() ) { edict_t *base; // what follows is basically a hack that allows timers to be assigned // to different teams in CTF. Powerups remain unassigned though base = G_ClosestFlagBase( ent ); if( base ) timer->s.modelindex = base->s.team; } timer->s.modelindex++; // add + 1 so we're guaranteed to have modelindex > 0 return timer; }
/* * G_Match_ScoreAnnouncement */ static void G_Match_ScoreAnnouncement( void ) { int i; edict_t *e, *chased; int num_leaders, team; if( !level.gametype.scoreAnnouncementEnabled ) return; num_leaders = 0; memset( leaders, 0, sizeof( leaders ) ); if( GS_TeamBasedGametype() ) { int score_max = -999999999; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { if( !teamlist[team].numplayers ) continue; if( teamlist[team].stats.score > score_max ) { score_max = teamlist[team].stats.score; leaders[0] = team; num_leaders = 1; } else if( teamlist[team].stats.score == score_max ) { leaders[num_leaders++] = team; } } leaders[num_leaders] = 0; } else { int score_max = -999999999; for( i = 0; i < MAX_CLIENTS && i < teamlist[TEAM_PLAYERS].numplayers; i++ ) { if( game.clients[teamlist[TEAM_PLAYERS].playerIndices[i]-1].level.stats.score > score_max ) { score_max = game.clients[teamlist[TEAM_PLAYERS].playerIndices[i]-1].level.stats.score; leaders[0] = teamlist[TEAM_PLAYERS].playerIndices[i]; num_leaders = 1; } else if( game.clients[teamlist[TEAM_PLAYERS].playerIndices[i]-1].level.stats.score == score_max ) { leaders[num_leaders++] = teamlist[TEAM_PLAYERS].playerIndices[i]; } } leaders[num_leaders] = 0; } if( !score_announcement_init ) { // copy over to last_leaders memcpy( last_leaders, leaders, sizeof( leaders ) ); score_announcement_init = true; return; } for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !e->r.client || trap_GetClientState( PLAYERNUM( e ) ) < CS_SPAWNED ) continue; if( e->r.client->resp.chase.active ) chased = &game.edicts[e->r.client->resp.chase.target]; else chased = e; // floating spectator if( chased->s.team == TEAM_SPECTATOR ) { if( !GS_TeamBasedGametype() ) continue; if( last_leaders[1] == 0 && leaders[1] != 0 ) { G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TIED_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); } else if( leaders[1] == 0 && ( last_leaders[0] != leaders[0] || last_leaders[1] != 0 ) ) { //G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_1_to_4_TAKEN_LEAD_1_to_2, // leaders[0]-1, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); } continue; } // in the game or chasing someone who is if( G_WasLeading( chased ) && !G_IsLeading( chased ) ) { if( GS_TeamBasedGametype() && !GS_InvidualGameType() ) G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_LOST_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); else G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_LOST_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); } else if( ( !G_WasLeading( chased ) || ( last_leaders[1] != 0 ) ) && G_IsLeading( chased ) && ( leaders[1] == 0 ) ) { if( GS_TeamBasedGametype() && !GS_InvidualGameType() ) G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TAKEN_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); else G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TAKEN_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); } else if( ( !G_WasLeading( chased ) || ( last_leaders[1] == 0 ) ) && G_IsLeading( chased ) && ( leaders[1] != 0 ) ) { if( GS_TeamBasedGametype() && !GS_InvidualGameType() ) G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TEAM_TIED_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); else G_AnnouncerSound( e, trap_SoundIndex( va( S_ANNOUNCER_SCORE_TIED_LEAD_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); } } // copy over to last_leaders memcpy( last_leaders, leaders, sizeof( leaders ) ); }
/* * SCR_DrawPlayerTab */ static int SCR_DrawPlayerTab( const char **ptrptr, int team, int x, int y, int panelWidth, struct qfontface_s *font, int pass ) { int dir, align, i, columncount; char type, string[MAX_STRING_CHARS]; const char *token, *layout; int height, width, xoffset, yoffset; vec4_t teamcolor = { 0.0f, 0.0f, 0.0f, 1.0f }, color; int iconnum; struct shader_s *icon; bool highlight = false, trans = false; if( GS_TeamBasedGametype() ) { dir = ( team == TEAM_ALPHA ) ? -1 : 1; align = ( team == TEAM_ALPHA ) ? ALIGN_RIGHT_TOP : ALIGN_LEFT_TOP; } else { dir = 0; align = ALIGN_CENTER_TOP; } xoffset = 0; yoffset = 0; height = trap_SCR_FontHeight( font ); // start from the center again xoffset = CG_HorizontalAlignForWidth( 0, align, panelWidth ); xoffset += ( SCB_CENTERMARGIN * dir ); // draw the background columncount = 0; if( ( team == TEAM_ALPHA ) || ( team == TEAM_BETA ) ) CG_TeamColor( team, teamcolor ); // draw the player tab column titles layout = cgs.configStrings[CS_SCB_PLAYERTAB_LAYOUT]; while( SCR_GetNextColumnLayout( &layout, NULL, &type, &width, font ) != NULL ) { // grab the actual scoreboard data if( !SCR_ParseToken( ptrptr, &token ) ) break; if( SCR_SkipColumn( type ) ) continue; Vector4Copy( colorWhite, color ); // reset to white after each column icon = NULL; string[0] = 0; // interpret the data based on the type defined in the layout switch( type ) { default: CG_Error( "SCR_DrawPlayerTab: Invalid player tab layout\n" ); break; case 's': // is a string { char l10n[MAX_STRING_CHARS]; Q_strncpyz( string, CG_TranslateColoredString( token, l10n, sizeof( l10n ) ), sizeof( string ) ); } break; case 'n': // is a player name indicated by player number i = atoi( token ); if( i < 0 ) // negative numbers toggle transparency on { trans = true; i = -1 - i; } if( i < 0 || i >= gs.maxclients ) Q_strncpyz( string, "invalid", sizeof( string ) ); else Q_strncpyz( string, cgs.clientInfo[i].name, sizeof( string ) ); if( ISVIEWERENTITY( i + 1 ) ) // highlight if it's our own player highlight = true; break; case 'i': // is a integer (negatives are colored in red) i = atoi( token ); Q_snprintfz( string, sizeof( string ), "%i", i ); VectorCopy( i >= 0 ? colorWhite : colorRed, color ); break; case 'f': // is a float Q_snprintfz( string, sizeof( string ), "%.2f", atof( token ) ); break; case 'l': // p is an integer colored in latency style i = atoi( token ); Q_snprintfz( string, sizeof( string ), "%i", i ); CG_PingColor( i, color ); break; case 'b': // is a Y/N boolean i = atoi( token ); Q_snprintfz( string, sizeof( string ), "%s", CG_TranslateString( ( i != 0 ) ? "Yes" : "No" ) ); VectorCopy( i ? colorGreen : colorRed, color ); break; case 'p': // is a picture. It uses height for width to get a square iconnum = atoi( token ); if( ( iconnum > 0 ) && ( iconnum < MAX_IMAGES ) ) icon = cgs.imagePrecache[iconnum]; break; case 't': // is a race time. Convert time into MM:SS:mm { unsigned int milli, min, sec; milli = (unsigned int)( atoi( token ) ); if( !milli ) Q_snprintfz( string, sizeof( string ), CG_TranslateString( "no time" ) ); else { min = milli / 60000; milli -= min * 60000; sec = milli / 1000; milli -= sec * 1000; Q_snprintfz( string, sizeof( string ), va( "%02i:%02i.%03i", min, sec, milli ) ); } } break; case 'r': // is a ready state tick that is hidden when not in warmup if( atoi( token ) ) icon = CG_MediaShader( cgs.media.shaderVSayIcon[VSAY_YES] ); break; } if( !width ) continue; // draw the column background teamcolor[3] = SCB_BACKGROUND_ALPHA; if( columncount & 1 ) teamcolor[3] -= 0.15; if( highlight ) teamcolor[3] += 0.3; if( trans ) color[3] = 0.3; if( !pass ) { trap_R_DrawStretchPic( x + xoffset, y + yoffset, width, height, 0, 0, 1, 1, teamcolor, cgs.shaderWhite ); if( icon ) SCR_AddPlayerIcon( icon, x + xoffset, y + yoffset, color[3], font ); } // draw the column value if( pass && string[0] ) { trap_SCR_DrawClampString( x + xoffset, y + yoffset, string, x + xoffset, y + yoffset, x + xoffset + width, y + yoffset + height, font, color ); } columncount++; xoffset += width; } yoffset += height; return yoffset; }
/* * G_Match_CheckStateAbort */ static void G_Match_CheckStateAbort( void ) { bool any = false; bool enough; if( GS_MatchState() <= MATCH_STATE_NONE || GS_MatchState() >= MATCH_STATE_POSTMATCH || level.gametype.mathAbortDisabled ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, false ); return; } if( GS_TeamBasedGametype() ) { int team, emptyteams = 0; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { if( !teamlist[team].numplayers ) emptyteams++; else any = true; } enough = ( emptyteams == 0 ); } else { enough = ( teamlist[TEAM_PLAYERS].numplayers > 1 ); any = ( teamlist[TEAM_PLAYERS].numplayers > 0 ); } // if waiting, turn on match states when enough players joined if( GS_MatchWaiting() && enough ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, false ); G_UpdatePlayersMatchMsgs(); } // turn off active match states if not enough players left else if( GS_MatchState() == MATCH_STATE_WARMUP && !enough && GS_MatchDuration() ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, true ); G_UpdatePlayersMatchMsgs(); } else if( GS_MatchState() == MATCH_STATE_COUNTDOWN && !enough ) { if( any ) { G_PrintMsg( NULL, "Not enough players left. Countdown aborted.\n" ); G_CenterPrintMsg( NULL, "COUNTDOWN ABORTED" ); } G_Match_Autorecord_Cancel(); G_Match_LaunchState( MATCH_STATE_WARMUP ); GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, true ); G_UpdatePlayersMatchMsgs(); } // match running, but not enough players left else if( GS_MatchState() == MATCH_STATE_PLAYTIME && !enough ) { if( any ) { G_PrintMsg( NULL, "Not enough players left. Match aborted.\n" ); G_CenterPrintMsg( NULL, "MATCH ABORTED" ); } G_EndMatch(); } }
/* * G_Match_ReadyAnnouncement */ static void G_Match_ReadyAnnouncement( void ) { int i; edict_t *e; int team; bool readyupwarnings = false; int START_TEAM, END_TEAM; if( !level.gametype.readyAnnouncementEnabled ) return; // ready up announcements if( GS_TeamBasedGametype() ) { START_TEAM = TEAM_ALPHA; END_TEAM = GS_MAX_TEAMS; } else { START_TEAM = TEAM_PLAYERS; END_TEAM = TEAM_PLAYERS+1; } for( team = START_TEAM; team < END_TEAM; team++ ) { if( !teamlist[team].numplayers ) continue; for( i = 0; i < teamlist[team].numplayers; i++ ) { e = game.edicts + teamlist[team].playerIndices[i]; if( e->r.svflags & SVF_FAKECLIENT ) continue; if( level.ready[teamlist[team].playerIndices[i]-1] ) { readyupwarnings = true; break; } } } if( !readyupwarnings ) return; // now let's repeat and warn for( team = START_TEAM; team < END_TEAM; team++ ) { if( !teamlist[team].numplayers ) continue; for( i = 0; i < teamlist[team].numplayers; i++ ) { if( !level.ready[teamlist[team].playerIndices[i]-1] ) { e = game.edicts + teamlist[team].playerIndices[i]; if( !e->r.client || trap_GetClientState( PLAYERNUM( e ) ) != CS_SPAWNED ) continue; if( e->r.client->teamstate.readyUpWarningNext < game.realtime ) { e->r.client->teamstate.readyUpWarningNext = game.realtime + G_ANNOUNCER_READYUP_DELAY; e->r.client->teamstate.readyUpWarningCount++; if( e->r.client->teamstate.readyUpWarningCount > 3 ) { G_AnnouncerSound( e, trap_SoundIndex( S_ANNOUNCER_READY_UP_PISSEDOFF ), GS_MAX_TEAMS, true, NULL ); e->r.client->teamstate.readyUpWarningCount = 0; } else { G_AnnouncerSound( e, trap_SoundIndex( S_ANNOUNCER_READY_UP_POLITE ), GS_MAX_TEAMS, true, NULL ); } } } } } }
/* * G_Match_CheckReadys */ void G_Match_CheckReadys( void ) { edict_t *e; bool allready; int readys, notreadys, teamsready; int team, i; if( GS_MatchState() != MATCH_STATE_WARMUP && GS_MatchState() != MATCH_STATE_COUNTDOWN ) return; if( GS_MatchState() == MATCH_STATE_COUNTDOWN && level.forceStart ) return; // never stop countdown if we have run out of warmup_timelimit teamsready = 0; for( team = TEAM_PLAYERS; team < GS_MAX_TEAMS; team++ ) { readys = notreadys = 0; for( i = 0; i < teamlist[team].numplayers; i++ ) { e = game.edicts + teamlist[team].playerIndices[i]; if( !e->r.inuse ) continue; if( e->s.team == TEAM_SPECTATOR ) //ignore spectators continue; if( level.ready[PLAYERNUM( e )] ) readys++; else notreadys++; } if( !notreadys && readys ) teamsready++; } // everyone has commited if( GS_TeamBasedGametype() ) { if( teamsready == GS_MAX_TEAMS - TEAM_ALPHA ) allready = true; else allready = false; } else { //ffa if( teamsready && teamlist[TEAM_PLAYERS].numplayers > 1 ) allready = true; else allready = false; } if( allready == true && GS_MatchState() != MATCH_STATE_COUNTDOWN ) { G_PrintMsg( NULL, "All players are ready. Match starting!\n" ); G_Match_LaunchState( MATCH_STATE_COUNTDOWN ); } else if( allready == false && GS_MatchState() == MATCH_STATE_COUNTDOWN ) { G_PrintMsg( NULL, "Countdown aborted.\n" ); G_CenterPrintMsg( NULL, "COUNTDOWN ABORTED" ); G_Match_Autorecord_Cancel(); G_Match_LaunchState( MATCH_STATE_WARMUP ); } }