/* Copyright (C) 2006 Warsow Team. 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" //=================================================================== int clientVoted[MAX_CLIENTS]; cvar_t *g_callvote_electpercentage; cvar_t *g_callvote_electtime; // in seconds cvar_t *g_callvote_enabled; enum { CALLVOTE_NONE = 0, CALLVOTE_INPROGRESS }; enum { VOTED_NOTHING = 0, VOTED_YES, VOTED_NO }; // Data that can be used by the vote specific functions typedef struct { edict_t *caller; int argc; char *argv[MAX_STRING_TOKENS]; char *string; // can be used to overwrite the displayed vote string void *data; // any data vote wants to carry over multiple calls of validate and to execute } callvotedata_t; // Data that will only be used by the common callvote functions typedef struct { int state; int type; // number of callvote type in callvoteslist unsigned int timeout; // time to finish callvotedata_t data; } gamecallvote_t; gamecallvote_t callvote; typedef struct { char *name; int expectedargs; // -1 = any amount, -2 = any amount except 0 qboolean (*validate)(callvotedata_t *vote, qboolean first); void (*execute)(callvotedata_t *vote); char *(*current)(void); void (*extraHelp)(edict_t *ent); char *argument_format; char *help; } callvotetype_t; //=================================================================== // Props of functions below //=================================================================== static qboolean G_VoteMapValidate( callvotedata_t *vote, qboolean first ); static void G_VoteMapPassed( callvotedata_t *vote ); static char *G_VoteMapCurrent( void ); static void G_VoteMapExtraHelp( edict_t *ent ); static void G_VoteRestartPassed( callvotedata_t *vote ); static void G_VoteNextMapPassed( callvotedata_t *vote ); static qboolean G_VoteScorelimitValidate( callvotedata_t *vote, qboolean first ); static void G_VoteScorelimitPassed( callvotedata_t *vote ); static char *G_VoteScorelimitCurrent( void ); static qboolean G_VoteAllreadyValidate( callvotedata_t *vote, qboolean first ); static void G_VoteAllreadyPassed( callvotedata_t *vote ); static qboolean G_VoteTimelimitValidate( callvotedata_t *vote, qboolean first ); static void G_VoteTimelimitPassed( callvotedata_t *vote ); static char *G_VoteTimelimitCurrent( void ); static qboolean G_VoteGametypeValidate( callvotedata_t *vote, qboolean first ); static void G_VoteGametypePassed( callvotedata_t *vote ); static char *G_VoteGametypeCurrent( void ); static void G_VoteGametypeExtraHelp( edict_t *ent ); static qboolean G_VoteWarmupValidate( callvotedata_t *vote, qboolean first ); static void G_VoteWarmupPassed( callvotedata_t *vote ); static char *G_VoteWarmupCurrent( void ); static qboolean G_VoteWarmupTimelimitValidate( callvotedata_t *vote, qboolean first ); static void G_VoteWarmupTimelimitPassed( callvotedata_t *vote ); static char *G_VoteWarmupTimelimitCurrent( void ); static qboolean G_VoteExtendedTimeValidate( callvotedata_t *vote, qboolean first ); static void G_VoteExtendedTimePassed( callvotedata_t *vote ); static char *G_VoteExtendedTimeCurrent( void ); static qboolean G_VoteMaxTeamsValidate( callvotedata_t *vote, qboolean first ); static void G_VoteMaxTeamsPassed( callvotedata_t *vote ); static char *G_VoteMaxTeamsCurrent( void ); static qboolean G_VoteMaxTeamplayersValidate( callvotedata_t *vote, qboolean first ); static void G_VoteMaxTeamplayersPassed( callvotedata_t *vote ); static char *G_VoteMaxTeamplayersCurrent( void ); static qboolean G_VoteLockTeamsValidate( callvotedata_t *vote, qboolean first ); static void G_VoteLockTeamsPassed( callvotedata_t *vote ); static char *G_VoteLockTeamsCurrent( void ); static qboolean G_VoteRemoveValidate( callvotedata_t *vote, qboolean first ); static void G_VoteRemovePassed( callvotedata_t *vote ); static void G_VoteRemoveExtraHelp( edict_t *ent ); static qboolean G_VoteKickValidate( callvotedata_t *vote, qboolean first ); static void G_VoteKickPassed( callvotedata_t *vote ); static void G_VoteKickExtraHelp( edict_t *ent ); static qboolean G_VoteMuteValidate( callvotedata_t *vote, qboolean first ); static void G_VoteMutePassed( callvotedata_t *vote ); static void G_VoteVMutePassed( callvotedata_t *vote ); static void G_VoteMuteExtraHelp( edict_t *ent ); static qboolean G_VoteNumBotsValidate( callvotedata_t *vote, qboolean first ); static void G_VoteNumBotsPassed( callvotedata_t *vote ); static char *G_VoteNumBotsCurrent( void ); static qboolean G_VoteAllowTeamDamageValidate( callvotedata_t *vote, qboolean first ); static void G_VoteAllowTeamDamagePassed( callvotedata_t *vote ); static char *G_VoteAllowTeamDamageCurrent( void ); static qboolean G_VoteAllowFallDamageValidate( callvotedata_t *vote, qboolean first ); static void G_VoteAllowFallDamagePassed( callvotedata_t *vote ); static char *G_VoteAllowFallDamageCurrent( void ); static qboolean G_VoteMaxTimeoutsValidate( callvotedata_t *vote, qboolean first ); static void G_VoteMaxTimeoutsPassed( callvotedata_t *vote ); static char *G_VoteMaxTimeoutsCurrent( void ); static qboolean G_VoteTimeoutValidate( callvotedata_t *vote, qboolean first ); static void G_VoteTimeoutPassed( callvotedata_t *vote ); static qboolean G_VoteTimeinValidate( callvotedata_t *vote, qboolean first ); static void G_VoteTimeinPassed( callvotedata_t *vote ); static qboolean G_VoteChallengersValidate( callvotedata_t *vote, qboolean first ); static void G_VoteChallengersPassed( callvotedata_t *vote ); static char *G_VoteChallengersCurrent( void ); #ifndef WSW_RELEASE static qboolean G_VoteDeadBodyFilterValidate( callvotedata_t *vote, qboolean first ); static void G_VoteDeadBodyFilterPassed( callvotedata_t *vote ); static char *G_VoteDeadBodyFilterCurrent( void ); #endif //================================================ // Votes definitions //================================================ callvotetype_t callvoteslist[] = { { "map", // callvote name 1, // expects number of argument after vote name G_VoteMapValidate, // validate the vote, with arguments (NULL for don't) G_VoteMapPassed, // execute when vote passed G_VoteMapCurrent, // current value of the setting (NULL for no value) G_VoteMapExtraHelp, // extra help in addition to argument format and help string "", // argument format "- Changes map" // help string }, { "restart", 0, NULL, G_VoteRestartPassed, NULL, NULL, NULL, "- Restarts current map" }, { "nextmap", 0, NULL, G_VoteNextMapPassed, NULL, NULL, NULL, "- Jumps to the next map" }, { "scorelimit", 1, G_VoteScorelimitValidate, G_VoteScorelimitPassed, G_VoteScorelimitCurrent, NULL, "", "- Sets the number of frags or caps needed to win the match\n- Use 0 to disable" }, { "timelimit", 1, G_VoteTimelimitValidate, G_VoteTimelimitPassed, G_VoteTimelimitCurrent, NULL, "", "- Sets number of minutes after which the match ends\n- Use 0 to disable" }, { "gametype", 1, G_VoteGametypeValidate, G_VoteGametypePassed, G_VoteGametypeCurrent, G_VoteGametypeExtraHelp, "", "- Changes the gametype" }, { "warmup", 1, G_VoteWarmupValidate, G_VoteWarmupPassed, G_VoteWarmupCurrent, NULL, "<1 or 0>", "- Enables or disables the warmup period before the match", }, { "warmup_timelimit", 1, G_VoteWarmupTimelimitValidate, G_VoteWarmupTimelimitPassed, G_VoteWarmupTimelimitCurrent, NULL, "", "- Sets the number of minutes after which the warmup ends\n- Use 0 to disable" }, { "extended_time", 1, G_VoteExtendedTimeValidate, G_VoteExtendedTimePassed, G_VoteExtendedTimeCurrent, NULL, "", "- Sets the length of the overtime\n- Use 0 to enable suddendeath mode" }, { "maxteams", 1, G_VoteMaxTeamsValidate, G_VoteMaxTeamsPassed, G_VoteMaxTeamsCurrent, NULL, "", "- Set the maximum number of teams allowed" }, { "maxteamplayers", 1, G_VoteMaxTeamplayersValidate, G_VoteMaxTeamplayersPassed, G_VoteMaxTeamplayersCurrent, NULL, "", "- Sets the maximum number of players in one team" }, { "lock_teams", 1, G_VoteLockTeamsValidate, G_VoteLockTeamsPassed, G_VoteLockTeamsCurrent, NULL, "<1 or 0>", "- Toggles whether people are able to join teams in middle of the game" }, { "allready", 0, G_VoteAllreadyValidate, G_VoteAllreadyPassed, NULL, NULL, NULL, "- Sets all players as ready so the match can start" }, { "remove", 1, G_VoteRemoveValidate, G_VoteRemovePassed, NULL, G_VoteRemoveExtraHelp, "", "- Forces player back to spectator mode" }, { "kick", 1, G_VoteKickValidate, G_VoteKickPassed, NULL, G_VoteKickExtraHelp, "", "- Removes player from the server" }, { "mute", 1, G_VoteMuteValidate, G_VoteMutePassed, NULL, G_VoteMuteExtraHelp, "", "- Disallows chat messages from the muted player" }, { "vmute", 1, G_VoteMuteValidate, G_VoteVMutePassed, NULL, G_VoteMuteExtraHelp, "", "- Disallows voice chat messages from the muted player" }, { "numbots", 1, G_VoteNumBotsValidate, G_VoteNumBotsPassed, G_VoteNumBotsCurrent, NULL, "", "- Sets the number of bots to play on the server" }, { "allow_teamdamage", 1, G_VoteAllowTeamDamageValidate, G_VoteAllowTeamDamagePassed, G_VoteAllowTeamDamageCurrent, NULL, "<1 or 0>", "- Toggles whether shooting teammates will do damage to them" }, { "allow_falldamage", 1, G_VoteAllowFallDamageValidate, G_VoteAllowFallDamagePassed, G_VoteAllowFallDamageCurrent, NULL, "<1 or 0>", "- Toggles whether falling long distances deals damage" }, { "maxtimeouts", 1, G_VoteMaxTimeoutsValidate, G_VoteMaxTimeoutsPassed, G_VoteMaxTimeoutsCurrent, NULL, "", "- Sets the maximum number of timeouts per team or player\n- Use 0 to disable\n- Use unlimited to allow unlimited amount" }, { "timeout", 0, G_VoteTimeoutValidate, G_VoteTimeoutPassed, NULL, NULL, NULL, "- Pauses the game" }, { "timein", 0, G_VoteTimeinValidate, G_VoteTimeinPassed, NULL, NULL, NULL, "- Resumes the game if in timeout" }, { "challengers_queue", 1, G_VoteChallengersValidate, G_VoteChallengersPassed, G_VoteChallengersCurrent, NULL, "<1 or 0>", "- Toggles the challenging spectators queue line" }, #ifndef WSW_RELEASE { "deadbody_filter", 1, G_VoteDeadBodyFilterValidate, G_VoteDeadBodyFilterPassed, G_VoteDeadBodyFilterCurrent, NULL, "<1 or 0>", "- Toggles whether the dead bodies remain in the game or go away fast" }, #endif {NULL} }; //=================================================================== // Common functions //=================================================================== void G_CallVotes_Reset( void ) { int i; callvote.state = CALLVOTE_NONE; memset( clientVoted, VOTED_NOTHING, sizeof(clientVoted) ); callvote.timeout = 0; callvote.data.caller = NULL; if( callvote.data.string ) { G_Free( callvote.data.string ); callvote.data.string = NULL; } if( callvote.data.data ) { G_Free( callvote.data.data ); callvote.data.data = NULL; } for( i = 0; i < callvote.data.argc; i++ ) { G_Free( callvote.data.argv[i] ); callvote.data.argv[i] = NULL; } } void G_CallVotes_Init( void ) { int i = 0; g_callvote_electpercentage = trap_Cvar_Get( "g_vote_percent", "55", CVAR_ARCHIVE ); g_callvote_electtime = trap_Cvar_Get( "g_vote_electtime", "40", CVAR_ARCHIVE ); g_callvote_enabled = trap_Cvar_Get( "g_vote_allowed", "1", CVAR_ARCHIVE ); // wsw : pb : server admin can now disable a specific vote command (g_disable_vote_) // init disable_vote_ cvars while( callvoteslist[i].name != NULL ) { trap_Cvar_Get( va("g_disable_vote_%s", callvoteslist[i].name), "0", CVAR_ARCHIVE ); i++; } G_CallVotes_Reset(); } static void G_CallVotes_PrintUsagesToPlayer( edict_t *ent ) { callvotetype_t *votetype; G_PrintMsg( ent, "Available votes:\n" ); for( votetype = callvoteslist; votetype->name; votetype++ ) { if( trap_Cvar_VariableValue( va("g_disable_vote_%s", votetype->name) ) == 1 ) continue; if( votetype->argument_format ) G_PrintMsg( ent, " %s %s\n", votetype->name, votetype->argument_format ); else G_PrintMsg( ent, " %s\n", votetype->name ); } } static void G_CallVotes_PrintHelpToPlayer( edict_t *ent, int type ) { callvotetype_t *vt = &callvoteslist[type]; G_PrintMsg( ent, "Usage: %s %s\n%s%s\n", vt->name, ( vt->argument_format ? vt->argument_format : "" ), ( vt->current ? va("Current: %s\n", vt->current()) : "" ), ( vt->help ? vt->help : "" ) ); if( callvoteslist[type].extraHelp != NULL ) callvoteslist[type].extraHelp( ent ); } static char *G_CallVotes_ArgsToString( callvotedata_t *vote ) { static char argstring[MAX_STRING_CHARS]; int i; argstring[0] = 0; if( vote->argc > 0 ) Q_strncatz(argstring, vote->argv[0], sizeof(argstring)); for( i = 1; i < vote->argc; i++ ) { Q_strncatz(argstring, " ", sizeof(argstring)); Q_strncatz(argstring, vote->argv[i], sizeof(argstring)); } return argstring; } static char *G_CallVotes_String( callvotedata_t *vote ) { if( vote->string ) return vote->string; else return G_CallVotes_ArgsToString( vote ); } static void G_CallVotes_CheckState( void ) { edict_t *ent; int needvotes, yeses = 0, voters = 0, noes = 0; static unsigned int warntimer; if( callvote.state != CALLVOTE_INPROGRESS ) { warntimer = 0; return; } if( callvoteslist[callvote.type].validate != NULL && !callvoteslist[callvote.type].validate( &callvote.data, qfalse ) ) { // fixme: should be vote cancelled or something G_AnnouncerSound( NULL, trap_SoundIndex(va(S_ANNOUNCER_CALLVOTE_FAILED_1_to_2, (rand()&1)+1)), GS_MAX_TEAMS, qtrue ); G_PrintMsg( NULL, "Vote is no longer valid\nVote %s%s %s%s canceled\n", S_COLOR_YELLOW, callvoteslist[callvote.type].name, G_CallVotes_String(&callvote.data), S_COLOR_WHITE ); G_CallVotes_Reset(); return; } //analize votation state for( ent = game.edicts + 1; PLAYERNUM(ent) < game.maxclients; ent++ ) { //fixme: ignore spectators too? if( ent->r.inuse && !(ent->r.svflags & SVF_FAKECLIENT) ) { //ignore bots voters++; if( clientVoted[PLAYERNUM(ent)] == VOTED_YES ) yeses++; else if( clientVoted[PLAYERNUM(ent)] == VOTED_NO ) noes++; } } //passed? needvotes = (int)((voters * g_callvote_electpercentage->value) / 100); if( yeses > needvotes ) { G_AnnouncerSound( NULL, trap_SoundIndex(va(S_ANNOUNCER_CALLVOTE_PASSED_1_to_2, (rand()&1)+1)), GS_MAX_TEAMS, qtrue ); G_PrintMsg( NULL, "Vote %s%s %s%s passed\n", S_COLOR_YELLOW, callvoteslist[callvote.type].name, G_CallVotes_String(&callvote.data), S_COLOR_WHITE ); if( callvoteslist[callvote.type].execute != NULL ) callvoteslist[callvote.type].execute(&callvote.data); G_CallVotes_Reset(); return; } //failed? if( game.realtime > callvote.timeout || voters-noes <= needvotes ) // no change to pass anymore { G_AnnouncerSound( NULL, trap_SoundIndex(va(S_ANNOUNCER_CALLVOTE_FAILED_1_to_2, (rand()&1)+1)), GS_MAX_TEAMS, qtrue ); G_PrintMsg( NULL, "Vote %s%s %s%s failed\n", S_COLOR_YELLOW, callvoteslist[callvote.type].name, G_CallVotes_String(&callvote.data), S_COLOR_WHITE ); G_CallVotes_Reset(); return; } if( warntimer < game.realtime ) { if( callvote.timeout - game.realtime <= 7.5 && callvote.timeout - game.realtime > 2.5 ) G_AnnouncerSound( NULL, trap_SoundIndex(S_ANNOUNCER_CALLVOTE_VOTE_NOW), GS_MAX_TEAMS, qtrue ); G_PrintMsg( NULL, "Vote in progress: %s%s %s%s, %i voted yes, %i voted no. %i required\n", S_COLOR_YELLOW, callvoteslist[callvote.type].name, G_CallVotes_String(&callvote.data), S_COLOR_WHITE, yeses, noes, needvotes + 1 ); warntimer = game.realtime + 5 * 1000; } } void G_CallVotes_CmdVote( edict_t *ent ) { char *vote; if( callvote.state != CALLVOTE_INPROGRESS ) { G_PrintMsg( ent, "%sThere's no vote in progress\n", S_COLOR_RED ); return; } if( clientVoted[PLAYERNUM(ent)] != VOTED_NOTHING ) { G_PrintMsg( ent, "%sYou have already voted\n", S_COLOR_RED ); return; } //fixme: check the rules //if( ent->r.svflags & SVF_NOCLIENT ) { // G_PrintMsg (ent, "Spectators are not allowed to vote\n"); // return; //} vote = trap_Cmd_Argv(1); if( !Q_stricmp( vote, "yes" ) ) { clientVoted[PLAYERNUM(ent)] = VOTED_YES; G_CallVotes_CheckState(); return; } else if( !Q_stricmp( vote, "no" ) ) { clientVoted[PLAYERNUM(ent)] = VOTED_NO; G_CallVotes_CheckState(); return; } G_PrintMsg( ent, "%sInvalid vote: %s%s%s. Use yes or no\n", S_COLOR_RED S_COLOR_YELLOW, vote, S_COLOR_RED ); } void G_CallVotes_Think( void ) { static unsigned int callvotethinktimer = 0; if( callvote.state != CALLVOTE_INPROGRESS ) { callvotethinktimer = 0; return; } if( callvotethinktimer < game.realtime ) { G_CallVotes_CheckState(); callvotethinktimer = game.realtime + 1000; } } void G_CallVote_Cmd( edict_t *ent ) { int i, type = -1; char *votename; callvotetype_t *votetype; if( !g_callvote_enabled->integer ) { G_PrintMsg( ent, "%sCallvoting is disabled on this server\n", S_COLOR_RED ); return; } if( callvote.state != CALLVOTE_NONE ) { G_PrintMsg( ent, "%sA vote is already in progress\n", S_COLOR_RED ); return; } votename = trap_Cmd_Argv(1); if( !votename || !votename[0] ) { G_CallVotes_PrintUsagesToPlayer( ent ); return; } if( strlen(votename) > MAX_QPATH ) { G_PrintMsg( ent, "%sInvalid vote\n", S_COLOR_RED ); G_CallVotes_PrintUsagesToPlayer( ent ); return; } //find the actual callvote command for( votetype = callvoteslist; votetype->name; votetype++ ) { if( votetype->name && !Q_stricmp( votetype->name, votename ) ) { type = votetype - callvoteslist; break; } } //unrecognized callvote type if( type == -1 ) { G_PrintMsg( ent, "%sUnrecognized vote: %s\n", S_COLOR_RED, votename ); G_CallVotes_PrintUsagesToPlayer( ent ); return; } // wsw : pb : server admin can now disable a specific vote command (g_disable_vote_) // check if vote is disabled if( trap_Cvar_VariableValue( va("g_disable_vote_%s", callvoteslist[type].name) ) ) { G_PrintMsg( ent, "%sCallvote %s is disabled on this server\n", S_COLOR_RED, callvoteslist[type].name ); return; } //we got a valid type. Get the parameters if any if( callvoteslist[type].expectedargs != trap_Cmd_Argc()-2 ) { if( callvoteslist[type].expectedargs != -1 && (callvoteslist[type].expectedargs != -2 || trap_Cmd_Argc()-2 > 0) ) { // wrong number of parametres G_CallVotes_PrintHelpToPlayer( ent, type ); return; } } callvote.data.argc = trap_Cmd_Argc()-2; for( i = 0; i < callvote.data.argc; i++ ) callvote.data.argv[i] = G_CopyString(trap_Cmd_Argv(i+2)); callvote.data.caller = ent; //validate if there's a validation func if( callvoteslist[type].validate != NULL && !callvoteslist[type].validate( &callvote.data, qtrue ) ) { G_CallVotes_PrintHelpToPlayer( ent, type ); G_CallVotes_Reset(); // free the args return; } //we're done. Proceed launching the election memset( clientVoted, VOTED_NOTHING, sizeof(clientVoted) ); callvote.type = type; callvote.timeout = game.realtime + (g_callvote_electtime->integer * 1000); //caller is assumed to vote YES clientVoted[PLAYERNUM(ent)] = VOTED_YES; callvote.state = CALLVOTE_INPROGRESS; G_AnnouncerSound( NULL, trap_SoundIndex(va(S_ANNOUNCER_CALLVOTE_CALLED_1_to_2, (rand()&1)+1)), GS_MAX_TEAMS, qtrue ); G_PrintMsg( NULL, "%s%s requested to vote %s%s %s%s\n", ent->r.client->pers.netname, S_COLOR_WHITE, S_COLOR_YELLOW, callvoteslist[callvote.type].name, G_CallVotes_String(&callvote.data), S_COLOR_WHITE ); G_PrintMsg( NULL, "%sPress %sF1 (\\vote yes)%s or %sF2 (\\vote no)%s\n", S_COLOR_WHITE, S_COLOR_YELLOW, S_COLOR_WHITE, S_COLOR_YELLOW, S_COLOR_WHITE ); G_CallVotes_Think(); //make the first think } //============================================== // Vote specifics //============================================== //==================== // map //==================== static void G_VoteMapExtraHelp( edict_t *ent ) { char *s; char buffer[2048]; char message[2048+200]; // use buffer to send only one print message int nummaps, length, i; message[0] = 0; Q_strncatz( message, "- Available maps:", sizeof(message) ); nummaps = trap_FS_GetFileList( "maps", ".bsp", buffer, sizeof(buffer) ); s = buffer; length = 0; for ( i = 0; i < nummaps; i++, s += length+1 ) { length = strlen( s ); Q_strncatz( message, " ", sizeof(message) ); Q_strncatz( message, s, sizeof(message) ); message[strlen(message)-4] = 0; // get rid of the .bsp } G_PrintMsg( ent, "%s\n", message ); } static qboolean G_VoteMapValidate( callvotedata_t *vote, qboolean first ) { char *mapname = vote->argv[0]; char *s; char buffer[2048]; int nummaps, length, i; if( !first ) // map can't become invalid while voting return qtrue; if( !Q_stricmp( level.mapname, mapname ) ) { G_PrintMsg( vote->caller, "%sYou are already on that map\n", S_COLOR_RED ); return qfalse; } // use filelist for case insensitive matching nummaps = trap_FS_GetFileList( "maps", ".bsp", buffer, sizeof(buffer) ); s = buffer; length = 0; for ( i = 0; i < nummaps; i++, s += length+1 ) { length = strlen( s ); if( length - 4 <= 0 || (int)strlen(mapname) != length - 4 ) continue; if( !Q_strnicmp( s, mapname, length-4 ) ) // ignore the .bsp return qtrue; } G_PrintMsg( vote->caller, "%sNo such map available on this server\n", S_COLOR_RED ); return qfalse; } static void G_VoteMapPassed( callvotedata_t *vote ) { Q_strncpyz (level.forcemap, Q_strlwr(vote->argv[0]), sizeof(level.forcemap)); G_EndMatch(); } static char *G_VoteMapCurrent( void ) { return level.mapname; } //==================== // restart //==================== static void G_VoteRestartPassed( callvotedata_t *vote ) { Q_strncpyz( level.forcemap, level.mapname, sizeof(level.mapname) ); G_EndMatch(); } //==================== // nextmap //==================== static void G_VoteNextMapPassed( callvotedata_t *vote ) { level.forcemap[0] = 0; G_EndMatch(); } //==================== // scorelimit //==================== static qboolean G_VoteScorelimitValidate( callvotedata_t *vote, qboolean first ) { int scorelimit = atoi(vote->argv[0]); if( scorelimit < 0 ) { if( first ) G_PrintMsg( vote->caller, "%sCan't set negative scorelimit\n", S_COLOR_RED ); return qfalse; } if( scorelimit == g_scorelimit->integer ) { if( first ) { G_PrintMsg( vote->caller, "%sScorelimit is already set to %i\n", S_COLOR_RED, scorelimit ); } return qfalse; } return qtrue; } static void G_VoteScorelimitPassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_scorelimit", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteScorelimitCurrent( void ) { return va("%i", g_scorelimit->integer); } //==================== // timelimit //==================== static qboolean G_VoteTimelimitValidate( callvotedata_t *vote, qboolean first ) { int timelimit = atoi(vote->argv[0]); if( timelimit < 0 ) { if( first ) G_PrintMsg( vote->caller, "%sCan't set negative timelimit\n", S_COLOR_RED ); return qfalse; } if( timelimit == g_timelimit->integer ) { if( first ) G_PrintMsg( vote->caller, "%sTimelimit is already set to %i\n", S_COLOR_RED, timelimit ); return qfalse; } return qtrue; } static void G_VoteTimelimitPassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_timelimit", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteTimelimitCurrent( void ) { return va("%i", g_timelimit->integer); } //==================== // gametype //==================== static void G_VoteGametypeExtraHelp( edict_t *ent ) { int type; char message[2048]; // use buffer to send only one print message message[0] = 0; if( (g_gametype->latched_string && strlen(g_gametype->latched_string) > 0) && (GS_Gametype_FindByShortName(g_gametype->latched_string) != -1) ) { Q_strncatz( message, "- Will be changed to: ", sizeof(message) ); Q_strncatz( message, g_gametype->latched_string, sizeof(message) ); Q_strncatz( message, "\n", sizeof(message) ); } Q_strncatz( message, "- Available gametypes:", sizeof(message) ); for( type = GAMETYPE_DM; type < GAMETYPE_TOTAL; type++ ) { if( G_Gametype_IsVotable(type) ) { Q_strncatz( message, " ", sizeof(message) ); Q_strncatz( message, GS_Gametype_ShortName(type), sizeof(message) ); } } G_PrintMsg( ent, "%s\n", message ); } static qboolean G_VoteGametypeValidate( callvotedata_t *vote, qboolean first ) { int type, type_latched; type = GS_Gametype_FindByShortName( vote->argv[0] ); if( type == -1 ) { if( first ) G_PrintMsg( vote->caller, "%sUnknown gametype\n", S_COLOR_RED ); return qfalse; } if( (g_gametype->latched_string && strlen(g_gametype->latched_string) > 0) && (GS_Gametype_FindByShortName(g_gametype->latched_string) != -1) ) type_latched = GS_Gametype_FindByShortName(g_gametype->latched_string); else type_latched = -1; if( match.state > MATCH_STATE_PLAYTIME && type == type_latched ) { if( first ) G_PrintMsg( vote->caller, "%s%s is already the next gametype\n", S_COLOR_RED, vote->argv[0] ); return qfalse; } if( (match.state <= MATCH_STATE_PLAYTIME || type_latched == -1) && type == game.gametype ) { if( first ) G_PrintMsg( vote->caller, "%s%s is the current gametype\n", S_COLOR_RED, vote->argv[0] ); return qfalse; } // if the g_votable_gametypes is empty, allow all gametypes if( !G_Gametype_IsVotable(type) ) { if( first ) { G_PrintMsg( vote->caller, "%sVoting gametype %s is not allowed on this server\n", S_COLOR_RED, vote->argv[0] ); } return qfalse; } return qtrue; } static void G_VoteGametypePassed( callvotedata_t *vote ) { int type; char message[MAX_STRING_CHARS]; type = GS_Gametype_FindByShortName( vote->argv[0] ); trap_Cvar_Set( "g_gametype", vote->argv[0] ); trap_Cvar_Set( "g_timelimit", va("%i", gametypes[type].timelimit)); trap_Cvar_Set( "g_match_extendedtime", va("%i", gametypes[type].extended_time)); trap_Cvar_Set( "g_scoreslimit", va("%i", gametypes[type].scorelimit)); trap_Cvar_Set( "g_teams_lock", (gametypes[type].lock_teams ? "1" : "0")); if( match.state == MATCH_STATE_COUNTDOWN || match.state == MATCH_STATE_PLAYTIME ) { // go thought scoreboard if in game Q_strncpyz( level.forcemap, level.mapname, sizeof(level.forcemap) ); G_EndMatch(); } else { if( !G_Match_RestartLevel() ) { Q_strncpyz( level.forcemap, level.mapname, sizeof(level.forcemap) ); G_EndMatch(); } } message[0] = 0; G_PrintMsg( NULL, "Gametype changed to %s\nTimelimit: %i\nExtended time: %i\nScorelimit: %i\nTeam locking: %s\n", g_gametype->string, g_timelimit->integer, g_match_extendedtime->integer, g_scorelimit->integer, (g_teams_lock->integer ? "Enabled" : "Disabled") ); } static char *G_VoteGametypeCurrent( void ) { return GS_Gametype_ShortName(game.gametype); } //==================== // warmup //==================== static qboolean G_VoteWarmupValidate( callvotedata_t *vote, qboolean first ) { int warmup = atoi(vote->argv[0]); if( warmup != 0 && warmup != 1 ) { return qfalse; } if( warmup && g_warmup_enabled->integer ) { if( first ) G_PrintMsg( vote->caller, "%sWarmup is already enabled\n", S_COLOR_RED ); return qfalse; } if( !warmup && !g_warmup_enabled->integer ) { if( first ) G_PrintMsg( vote->caller, "%sWarmup is already disabled\n", S_COLOR_RED ); return qfalse; } return qtrue; } static void G_VoteWarmupPassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_warmup_enabled", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteWarmupCurrent( void ) { if( g_warmup_enabled->integer ) return "1"; else return "0"; } //==================== // warmup_timelimit //==================== static qboolean G_VoteWarmupTimelimitValidate( callvotedata_t *vote, qboolean first ) { int warmup_timelimit = atoi(vote->argv[0]); if( warmup_timelimit < 0 ) { if( first ) G_PrintMsg( vote->caller, "%sCan't set negative warmup timelimit\n", S_COLOR_RED ); return qfalse; } if( warmup_timelimit == g_warmup_timelimit->integer ) { if( first ) G_PrintMsg( vote->caller, "%sWarmup timelimit is already set to %i\n", S_COLOR_RED, warmup_timelimit ); return qfalse; } return qtrue; } static void G_VoteWarmupTimelimitPassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_warmup_timelimit", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteWarmupTimelimitCurrent( void ) { return va("%i", g_warmup_timelimit->integer); } //==================== // extended_time //==================== static qboolean G_VoteExtendedTimeValidate( callvotedata_t *vote, qboolean first ) { int extended_time = atoi(vote->argv[0]); if( extended_time < 0 ) { if( first ) G_PrintMsg( vote->caller, "%sCan't set negative extended time\n", S_COLOR_RED ); return qfalse; } if( extended_time == g_match_extendedtime->integer ) { if( first ) G_PrintMsg( vote->caller, "%sExtended time is already set to %i\n", S_COLOR_RED, extended_time ); return qfalse; } return qtrue; } static void G_VoteExtendedTimePassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_match_extendedtime", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteExtendedTimeCurrent( void ) { return va("%i", g_match_extendedtime->integer); } //==================== // allready //==================== static qboolean G_VoteAllreadyValidate( callvotedata_t *vote, qboolean first ) { int notreadys = 0; edict_t *ent; if( match.state >= MATCH_STATE_COUNTDOWN ) { if( first ) G_PrintMsg( vote->caller, "%sThe game is not in warmup mode\n", S_COLOR_RED ); return qfalse; } for( ent = game.edicts+1; PLAYERNUM(ent) < game.maxclients; ent++ ) { if( trap_GetClientState(PLAYERNUM(ent)) < CS_SPAWNED ) continue; if( ent->s.team > TEAM_SPECTATOR && !match.ready[PLAYERNUM(ent)] ) notreadys++; } if( !notreadys ) { if( first ) G_PrintMsg( vote->caller, "%sEveryone is already ready\n", S_COLOR_RED ); return qfalse; } return qtrue; } static void G_VoteAllreadyPassed( callvotedata_t *vote ) { edict_t *ent; for( ent = game.edicts+1; PLAYERNUM(ent) < game.maxclients; ent++ ) { if( trap_GetClientState(PLAYERNUM(ent)) < CS_SPAWNED ) continue; if( ent->s.team > TEAM_SPECTATOR && !match.ready[PLAYERNUM(ent)] ) { match.ready[PLAYERNUM(ent)] = qtrue; G_UpdatePlayerMatchMsg(ent); G_Match_CheckReadys(); } } } //==================== // maxteams //==================== static qboolean G_VoteMaxTeamsValidate( callvotedata_t *vote, qboolean first ) { int maxteams = atoi(vote->argv[0]); if( maxteams < 2 || maxteams > GS_MAX_TEAMS ) { if( first ) { G_PrintMsg( vote->caller, "%sThe number of teams must be inbetween 2 and %i\n", S_COLOR_RED, GS_MAX_TEAMS ); } return qfalse; } if( g_maxteams->integer == maxteams ) { if( first ) G_PrintMsg( vote->caller, "%sMaximum number of teams is already %i\n", S_COLOR_RED, maxteams ); return qfalse; } return qtrue; } static void G_VoteMaxTeamsPassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_maxteams", va("%i", atoi(vote->argv[0])) ); //force map reload and restart the match Q_strncpyz( level.forcemap, level.mapname, sizeof(level.forcemap) ); G_EndMatch(); } static char *G_VoteMaxTeamsCurrent( void ) { return va("%i", g_maxteams->integer); } //==================== // maxteamplayers //==================== static qboolean G_VoteMaxTeamplayersValidate( callvotedata_t *vote, qboolean first ) { int maxteamplayers = atoi(vote->argv[0]); if( maxteamplayers < 1 ) { if( first ) { G_PrintMsg( vote->caller, "%sThe maximum number of players in team can't be less than 1\n", S_COLOR_RED ); } return qfalse; } if( g_teams_maxplayers->integer == maxteamplayers ) { if( first ) { G_PrintMsg( vote->caller, "%sMaximum number of players in team is already %i\n", S_COLOR_RED, maxteamplayers ); } return qfalse; } return qtrue; } static void G_VoteMaxTeamplayersPassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_teams_maxplayers", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteMaxTeamplayersCurrent( void ) { return va("%i", g_teams_maxplayers->integer); } //==================== // teamlock //==================== static qboolean G_VoteLockTeamsValidate( callvotedata_t *vote, qboolean first ) { int lockteams = atoi(vote->argv[0]); if( lockteams != 0 && lockteams != 1 ) return qfalse; if( lockteams && g_teams_lock->integer ) { if( first ) G_PrintMsg( vote->caller, "%sTeam locking is already enabled\n", S_COLOR_RED ); return qfalse; } if( !lockteams && !g_teams_lock->integer ) { if( first ) G_PrintMsg( vote->caller, "%sTeam locking is already disabled\n", S_COLOR_RED ); return qfalse; } return qtrue; } static void G_VoteLockTeamsPassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_teams_lock", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteLockTeamsCurrent( void ) { return va("%i", g_teams_lock->integer); } //==================== // remove //==================== static void G_VoteRemoveExtraHelp( edict_t *ent ) { int i; edict_t *e; char msg[1024]; msg[0] = 0; Q_strncatz( msg, "- List of players in game:\n", sizeof(msg) ); if( GS_Gametype_IsTeamBased(game.gametype) ) { int team; for( team = TEAM_RED; team < TEAM_RED + g_maxteams->integer; team++ ) { Q_strncatz( msg, va("%s:\n", GS_TeamName(team)), sizeof(msg) ); for( i = 0, e = game.edicts+1; i < game.maxclients; i++, e++ ) { if( !e->r.inuse || e->s.team != team ) continue; Q_strncatz( msg, va("%3i: %s\n", PLAYERNUM(e), e->r.client->pers.netname), sizeof(msg) ); } } } else { for( i = 0, e = game.edicts+1; i < game.maxclients; i++, e++ ) { if( !e->r.inuse || e->s.team != TEAM_PLAYERS ) continue; Q_strncatz( msg, va("%3i: %s\n", PLAYERNUM(e), e->r.client->pers.netname), sizeof(msg) ); } } G_PrintMsg( ent, "%s", msg ); } static qboolean G_VoteRemoveValidate( callvotedata_t *vote, qboolean first ) { int who = -1; if( first ) { edict_t *tokick = G_PlayerForText( vote->argv[0] ); if( tokick ) who = PLAYERNUM(tokick); else who = -1; if( who == -1 ) { G_PrintMsg( vote->caller, "%sNo such player\n", S_COLOR_RED ); return qfalse; } else if( tokick->s.team == TEAM_SPECTATOR ) { G_PrintMsg( vote->caller, "Player %s%s%s is already spectator.\n", S_COLOR_WHITE, tokick->r.client->pers.netname, S_COLOR_RED ); return qfalse; } else { // we save the player id to be removed, so we don't later get confused by new ids or players changing names vote->data = G_Malloc( sizeof(int) ); memcpy( vote->data, &who, sizeof(int) ); } } else { memcpy( &who, vote->data, sizeof(int) ); } if( !game.edicts[who+1].r.inuse || game.edicts[who+1].s.team == TEAM_SPECTATOR ) { return qfalse; } else { if( !vote->string || Q_stricmp(vote->string, game.edicts[who+1].r.client->pers.netname) ) { if( vote->string ) G_Free(vote->string); vote->string = G_CopyString( game.edicts[who+1].r.client->pers.netname ); } return qtrue; } } static void G_VoteRemovePassed( callvotedata_t *vote ) { int who; edict_t *ent; memcpy( &who, vote->data, sizeof(int) ); ent = &game.edicts[who+1]; // may have disconnect along the callvote time if( !ent->r.inuse || !ent->r.client || ent->s.team == TEAM_SPECTATOR ) return; G_PrintMsg( NULL, "Player %s%s removed from team %s%s.\n", ent->r.client->pers.netname, S_COLOR_WHITE, GS_TeamName(ent->s.team), S_COLOR_WHITE ); G_Teams_SetTeam( ent, TEAM_SPECTATOR ); } //==================== // kick //==================== static void G_VoteKickExtraHelp( edict_t *ent ) { int i; edict_t *e; char msg[1024]; msg[0] = 0; Q_strncatz( msg, "- List of current players:\n", sizeof(msg) ); for( i = 0, e = game.edicts+1; i < game.maxclients; i++, e++ ) { if( !e->r.inuse ) continue; Q_strncatz( msg, va("%3i: %s\n", PLAYERNUM(e), e->r.client->pers.netname), sizeof(msg) ); } G_PrintMsg( ent, "%s", msg ); } static qboolean G_VoteKickValidate( callvotedata_t *vote, qboolean first ) { int who = -1; if( first ) { edict_t *tokick = G_PlayerForText( vote->argv[0] ); if( tokick ) who = PLAYERNUM(tokick); else who = -1; if( who != -1 ) { // we save the player id to be kicked, so we don't later get confused by new ids or players changing names vote->data = G_Malloc( sizeof(int) ); memcpy( vote->data, &who, sizeof(int) ); } else { G_PrintMsg( vote->caller, "%sNo such player\n", S_COLOR_RED ); return qfalse; } } else { memcpy( &who, vote->data, sizeof(int) ); } if( !game.edicts[who+1].r.inuse ) { return qfalse; } else { if( !vote->string || Q_stricmp(vote->string, game.edicts[who+1].r.client->pers.netname) ) { if( vote->string ) G_Free(vote->string); vote->string = G_CopyString( game.edicts[who+1].r.client->pers.netname ); } return qtrue; } } static void G_VoteKickPassed( callvotedata_t *vote ) { int who; edict_t *ent; memcpy( &who, vote->data, sizeof(int) ); ent = &game.edicts[who+1]; if( !ent->r.inuse || !ent->r.client ) // may have disconnect along the callvote time return; trap_DropClient(ent, DROP_TYPE_NORECONNECT, "You were kicked"); } //==================== // mute //==================== static void G_VoteMuteExtraHelp( edict_t *ent ) { int i; edict_t *e; char msg[1024]; msg[0] = 0; Q_strncatz( msg, "- List of current players:\n", sizeof(msg) ); for( i = 0, e = game.edicts+1; i < game.maxclients; i++, e++ ) { if( !e->r.inuse ) continue; Q_strncatz( msg, va("%3i: %s\n", PLAYERNUM(e), e->r.client->pers.netname), sizeof(msg) ); } G_PrintMsg( ent, "%s", msg ); } static qboolean G_VoteMuteValidate( callvotedata_t *vote, qboolean first ) { int who = -1; if( first ) { edict_t *tomute = G_PlayerForText( vote->argv[0] ); if( tomute ) who = PLAYERNUM(tomute); else who = -1; if( who != -1 ) { // we save the player id to be kicked, so we don't later get confused by new ids or players changing names vote->data = G_Malloc( sizeof(int) ); memcpy( vote->data, &who, sizeof(int) ); } else { G_PrintMsg( vote->caller, "%sNo such player\n", S_COLOR_RED ); return qfalse; } } else { memcpy( &who, vote->data, sizeof(int) ); } if( !game.edicts[who+1].r.inuse ) { return qfalse; } else { if( !vote->string || Q_stricmp(vote->string, game.edicts[who+1].r.client->pers.netname) ) { if( vote->string ) G_Free(vote->string); vote->string = G_CopyString( game.edicts[who+1].r.client->pers.netname ); } return qtrue; } } // chat mute static void G_VoteMutePassed( callvotedata_t *vote ) { int who; edict_t *ent; memcpy( &who, vote->data, sizeof(int) ); ent = &game.edicts[who+1]; if( !ent->r.inuse || !ent->r.client ) // may have disconnect along the callvote time return; ent->r.client->pers.muted |= 1; } // vsay mute static void G_VoteVMutePassed( callvotedata_t *vote ) { int who; edict_t *ent; memcpy( &who, vote->data, sizeof(int) ); ent = &game.edicts[who+1]; if( !ent->r.inuse || !ent->r.client ) // may have disconnect along the callvote time return; ent->r.client->pers.muted |= 2; } //==================== // addbots //==================== static qboolean G_VoteNumBotsValidate( callvotedata_t *vote, qboolean first ) { int numbots = atoi(vote->argv[0]); edict_t *e; int count; //count the active client spots not being bots for( count = 0, e = game.edicts + 1; PLAYERNUM(e) < game.maxclients; e++ ) { if( e->r.client && e->r.inuse && e->ai.type != AI_ISBOT ) count++; } if( numbots < 0 ) return qfalse; if( g_numbots->integer == numbots ) { if( first ) G_PrintMsg( vote->caller, "%sNumber of bots is already %i\n", S_COLOR_RED, numbots ); return qfalse; } if( !count ) count = 1; if( numbots > game.numBots ) { if( game.maxclients - (count+game.numBots) == 0 ) { if( first ) G_PrintMsg( vote->caller, "%sThere isn't room for any more bots\n", S_COLOR_RED ); return qfalse; } if( count + numbots > game.maxclients ) { if( first ) { G_PrintMsg( vote->caller, "%sThere is only room for %i more bots. Increase sv_maxclients.\n", S_COLOR_RED, game.maxclients - (count+game.numBots) ); } return qfalse; } } return qtrue; } static void G_VoteNumBotsPassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_numbots", vote->argv[0] ); } static char *G_VoteNumBotsCurrent( void ) { return va("%i", g_numbots->integer); } //==================== // allow_teamdamage //==================== static qboolean G_VoteAllowTeamDamageValidate( callvotedata_t *vote, qboolean first ) { int teamdamage = atoi(vote->argv[0]); if( teamdamage != 0 && teamdamage != 1 ) return qfalse; if( teamdamage && g_teams_teamdamage->integer ) { if( first ) G_PrintMsg( vote->caller, "%sTeam damage is already allowed\n", S_COLOR_RED ); return qfalse; } if( !teamdamage && !g_teams_teamdamage->integer ) { if( first ) G_PrintMsg( vote->caller, "%sTeam damage is already disabled\n", S_COLOR_RED ); return qfalse; } return qtrue; } static void G_VoteAllowTeamDamagePassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_teams_teamdamage", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteAllowTeamDamageCurrent( void ) { if( g_teams_teamdamage->integer ) return "1"; else return "0"; } //==================== // allow_falldamage //==================== static qboolean G_VoteAllowFallDamageValidate( callvotedata_t *vote, qboolean first ) { int falldamage = atoi(vote->argv[0]); if( falldamage != 0 && falldamage != 1 ) return qfalse; if( falldamage && g_allow_falldamage->integer ) { if( first ) G_PrintMsg( vote->caller, "%sFall damage is already allowed\n", S_COLOR_RED ); return qfalse; } if( !falldamage && !g_allow_falldamage->integer ) { if( first ) G_PrintMsg( vote->caller, "%sFall damage is already disabled\n", S_COLOR_RED ); return qfalse; } return qtrue; } static void G_VoteAllowFallDamagePassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_allow_falldamage", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteAllowFallDamageCurrent( void ) { if( g_allow_falldamage->integer ) return "1"; else return "0"; } //==================== // maxtimeouts //==================== static qboolean G_VoteMaxTimeoutsValidate( callvotedata_t *vote, qboolean first ) { int maxtimeouts = atoi(vote->argv[0]); if( !Q_stricmp(vote->argv[0], "unlimited") ) { maxtimeouts = -1; } else { if( maxtimeouts < 0 ) { if( first ) { G_PrintMsg( vote->caller, "%sThe maximum number of timeouts can't be negative\n", S_COLOR_RED ); } return qfalse; } } if( g_maxtimeouts->integer == maxtimeouts ) { if( first && maxtimeouts == -1 ) { G_PrintMsg( vote->caller, "%sMaximum number of timeouts is already unlimited\n", S_COLOR_RED, maxtimeouts ); } else { G_PrintMsg( vote->caller, "%sMaximum number of timeouts is already %i\n", S_COLOR_RED, maxtimeouts ); } return qfalse; } return qtrue; } static void G_VoteMaxTimeoutsPassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_maxtimeouts", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteMaxTimeoutsCurrent( void ) { if( g_maxtimeouts->integer == -1 ) { return "unlimited"; } else { return va("%i", g_maxtimeouts->integer); } } //==================== // timeout //==================== static qboolean G_VoteTimeoutValidate( callvotedata_t *vote, qboolean first ) { if( gtimeout.active && (gtimeout.endtime - gtimeout.time) >= 2 * TIMEIN_TIME ) { if( first ) G_PrintMsg( vote->caller, "%sTimeout already in progress\n", S_COLOR_RED ); return qfalse; } return qtrue; } static void G_VoteTimeoutPassed( callvotedata_t *vote ) { if( !gtimeout.active ) G_AnnouncerSound( NULL, trap_SoundIndex(va(S_ANNOUNCER_TIMEOUT_TIMEOUT_1_to_2, (rand()&1)+1)), GS_MAX_TEAMS, qtrue ); gtimeout.active = qtrue; gtimeout.caller = 0; gtimeout.endtime = gtimeout.time + TIMEOUT_TIME + FRAMETIME; } //==================== // timein //==================== static qboolean G_VoteTimeinValidate( callvotedata_t *vote, qboolean first ) { if( !gtimeout.active ) { if( first ) G_PrintMsg( vote->caller, "%sNo timeout in progress\n", S_COLOR_RED ); return qfalse; } if( gtimeout.endtime - gtimeout.time <= 2 * TIMEIN_TIME ) { if( first ) G_PrintMsg( vote->caller, "%sTimeout is about to end already\n", S_COLOR_RED ); return qfalse; } return qtrue; } static void G_VoteTimeinPassed( callvotedata_t *vote ) { G_AnnouncerSound( NULL, trap_SoundIndex(va(S_ANNOUNCER_TIMEOUT_TIMEIN_1_to_2, (rand()&1)+1)), GS_MAX_TEAMS, qtrue ); gtimeout.endtime = gtimeout.time + TIMEIN_TIME + FRAMETIME; } //==================== // challengers_queue //==================== static qboolean G_VoteChallengersValidate( callvotedata_t *vote, qboolean first ) { int challengers = atoi(vote->argv[0]); if( challengers != 0 && challengers != 1 ) return qfalse; if( challengers && g_challengers_queue->integer ) { if( first ) G_PrintMsg( vote->caller, "%sChallengers queue is already enabled\n", S_COLOR_RED ); return qfalse; } if( !challengers && !g_challengers_queue->integer ) { if( first ) G_PrintMsg( vote->caller, "%sChallengers queue is already disabled\n", S_COLOR_RED ); return qfalse; } return qtrue; } static void G_VoteChallengersPassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_challengers_queue", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteChallengersCurrent( void ) { if( g_challengers_queue->integer ) return "1"; else return "0"; } //==================== // deadbody_filter //==================== #ifndef WSW_RELEASE static qboolean G_VoteDeadBodyFilterValidate( callvotedata_t *vote, qboolean first ) { int bodyfilter = atoi(vote->argv[0]); if( bodyfilter != 0 && bodyfilter != 1 ) return qfalse; if( bodyfilter && g_deadbody_filter->integer ) { if( first ) G_PrintMsg( vote->caller, "%sDeadbody filter is already allowed\n", S_COLOR_RED ); return qfalse; } if( !bodyfilter && !g_deadbody_filter->integer ) { if( first ) G_PrintMsg( vote->caller, "%sDeadbody filter is already disabled\n", S_COLOR_RED ); return qfalse; } return qtrue; } static void G_VoteDeadBodyFilterPassed( callvotedata_t *vote ) { trap_Cvar_Set( "g_deadbody_filter", va("%i", atoi(vote->argv[0])) ); } static char *G_VoteDeadBodyFilterCurrent( void ) { if( g_deadbody_filter->integer ) return "1"; else return "0"; } #endif // WSW_RELEASE