diff options
Diffstat (limited to 'src/game')
-rw-r--r-- | src/game/bg_misc.c | 81 | ||||
-rw-r--r-- | src/game/bg_public.h | 19 | ||||
-rw-r--r-- | src/game/g_active.c | 17 | ||||
-rw-r--r-- | src/game/g_admin.c | 73 | ||||
-rw-r--r-- | src/game/g_admin.h | 1 | ||||
-rw-r--r-- | src/game/g_buildable.c | 20 | ||||
-rw-r--r-- | src/game/g_client.c | 11 | ||||
-rw-r--r-- | src/game/g_cmds.c | 453 | ||||
-rw-r--r-- | src/game/g_local.h | 5 | ||||
-rw-r--r-- | src/game/g_main.c | 23 | ||||
-rw-r--r-- | src/game/g_session.c | 15 | ||||
-rw-r--r-- | src/game/g_svcmds.c | 47 |
12 files changed, 627 insertions, 138 deletions
diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c index fd8b87d7..fd79c380 100644 --- a/src/game/bg_misc.c +++ b/src/game/bg_misc.c @@ -5529,3 +5529,84 @@ qboolean BG_BuildableIsAllowed( buildable_t buildable ) return qtrue; } + +/* +============ +BG_ClientListTest +============ +*/ +qboolean BG_ClientListTest( clientList_t *list, int clientNum ) +{ + if( clientNum < 0 || clientNum >= MAX_CLIENTS || !list ) + return qfalse; + if( clientNum < 32 ) + return ( ( list->lo & ( 1 << clientNum ) ) != 0 ); + else + return ( ( list->hi & ( 1 << ( clientNum - 32 ) ) ) != 0 ); +} + +/* +============ +BG_ClientListAdd +============ +*/ +void BG_ClientListAdd( clientList_t *list, int clientNum ) +{ + if( clientNum < 0 || clientNum >= MAX_CLIENTS || !list ) + return; + if( clientNum < 32 ) + list->lo |= ( 1 << clientNum ); + else + list->hi |= ( 1 << ( clientNum - 32 ) ); +} + +/* +============ +BG_ClientListRemove +============ +*/ +void BG_ClientListRemove( clientList_t *list, int clientNum ) +{ + if( clientNum < 0 || clientNum >= MAX_CLIENTS || !list ) + return; + if( clientNum < 32 ) + list->lo &= ~( 1 << clientNum ); + else + list->hi &= ~( 1 << ( clientNum - 32 ) ); +} + +/* +============ +BG_ClientListString +============ +*/ +char *BG_ClientListString( clientList_t *list ) +{ + static char s[ 17 ]; + + s[ 0 ] = '\0'; + if( !list ) + return s; + Com_sprintf( s, sizeof( s ), "%08x%08x", list->hi, list->lo ); + return s; +} + +/* +============ +BG_ClientListParse +============ +*/ +void BG_ClientListParse( clientList_t *list, const char *s ) +{ + if( !list ) + return; + list->lo = 0; + list->hi = 0; + if( !s ) + return; + if( strlen( s ) != 16 ) + return; + sscanf( s, "%x%x", &list->hi, &list->lo ); +} + + diff --git a/src/game/bg_public.h b/src/game/bg_public.h index 877b8b8c..722aca47 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -64,7 +64,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define CS_GAME_VERSION 20 #define CS_LEVEL_START_TIME 21 // so the timer only shows the current level #define CS_INTERMISSION 22 // when 1, fraglimit/timelimit has been hit and intermission will start in a second or two -#define CS_FLAGSTATUS 23 // string indicating flag status in CTF +#define CS_WINNER 23 // string indicating round winner #define CS_SHADERSTATE 24 #define CS_BOTINFO 25 #define CS_CLIENTS_READY 26 //TA: following suggestion in STAT_ enum STAT_CLIENTS_READY becomes a configstring @@ -1294,3 +1294,20 @@ qboolean BG_UpgradeIsAllowed( upgrade_t upgrade ); qboolean BG_ClassIsAllowed( pClass_t class ); qboolean BG_BuildableIsAllowed( buildable_t buildable ); qboolean BG_UpgradeClassAvailable( playerState_t *ps ); + +typedef struct +{ + unsigned int hi; + unsigned int lo; +} clientList_t; +qboolean BG_ClientListTest( clientList_t *list, int clientNum ); +void BG_ClientListAdd( clientList_t *list, int clientNum ); +void BG_ClientListRemove( clientList_t *list, int clientNum ); +char *BG_ClientListString( clientList_t *list ); +void BG_ClientListParse( clientList_t *list, const char *s ); + +// Friendly Fire Flags +#define FFF_HUMANS 1 +#define FFF_ALIENS 2 +#define FFF_BUILDABLES 4 + diff --git a/src/game/g_active.c b/src/game/g_active.c index d7776991..b3e8f2b9 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -762,7 +762,8 @@ void ClientTimerActions( gentity_t *ent, int msec ) } //replenish alien health - if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) + if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && + level.surrenderTeam != PTE_ALIENS ) { int entityList[ MAX_GENTITIES ]; vec3_t range = { LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE }; @@ -801,6 +802,20 @@ void ClientTimerActions( gentity_t *ent, int msec ) if( ent->health > client->ps.stats[ STAT_MAX_HEALTH ] ) ent->health = client->ps.stats[ STAT_MAX_HEALTH ]; } + + // turn off life support when a team admits defeat + if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && + level.surrenderTeam == PTE_ALIENS ) + { + G_Damage( ent, NULL, NULL, NULL, NULL, + BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] ), + DAMAGE_NO_ARMOR, MOD_SUICIDE ); + } + else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS && + level.surrenderTeam == PTE_HUMANS ) + { + G_Damage( ent, NULL, NULL, NULL, NULL, 5, DAMAGE_NO_ARMOR, MOD_SUICIDE ); + } } while( client->time10000 >= 10000 ) diff --git a/src/game/g_admin.c b/src/game/g_admin.c index 8cf374d3..73fe3060 100644 --- a/src/game/g_admin.c +++ b/src/game/g_admin.c @@ -42,6 +42,11 @@ g_admin_cmd_t g_admin_cmds[ ] = "display your current admin level", "" }, + + {"allowbuild", G_admin_denybuild, "d", + "restore a player's ablity to build", + "[^3name|slot#^7]" + }, {"allready", G_admin_allready, "y", "makes everyone ready in intermission", @@ -60,6 +65,11 @@ g_admin_cmd_t g_admin_cmds[ ] = "" }, + {"denybuild", G_admin_denybuild, "d", + "take away a player's ablity to build", + "[^3name|slot#^7]" + }, + {"help", G_admin_help, "h", "display commands available to you or help on a specific command", "(^5command^7)" @@ -2042,6 +2052,68 @@ qboolean G_admin_mute( gentity_t *ent, int skiparg ) return qtrue; } +qboolean G_admin_denybuild( gentity_t *ent, int skiparg ) +{ + int pids[ MAX_CLIENTS ]; + char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; + char command[ MAX_ADMIN_CMD_LEN ], *cmd; + gentity_t *vic; + + G_SayArgv( skiparg, command, sizeof( command ) ); + cmd = command; + if( cmd && *cmd == '!' ) + cmd++; + if( G_SayArgc() < 2 + skiparg ) + { + ADMP( va( "^3!%s: ^7usage: !%s [name|slot#]\n", cmd, cmd ) ); + return qfalse; + } + G_SayArgv( 1 + skiparg, name, sizeof( name ) ); + if( G_ClientNumbersFromString( name, pids ) != 1 ) + { + G_MatchOnePlayer( pids, err, sizeof( err ) ); + ADMP( va( "^3!%s: ^7%s\n", cmd, err ) ); + return qfalse; + } + if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) ) + { + ADMP( va( "^3!%s: ^7sorry, but your intended victim has a higher admin" + " level than you\n", cmd ) ); + return qfalse; + } + vic = &g_entities[ pids[ 0 ] ]; + if( vic->client->pers.denyBuild ) + { + if( !Q_stricmp( cmd, "denybuild" ) ) + { + ADMP( "^3!denybuild: ^7player already has no building rights\n" ); + return qtrue; + } + vic->client->pers.denyBuild = qfalse; + CPx( pids[ 0 ], "cp \"^1You've regained your building rights\"" ); + AP( va( + "print \"^3!allowbuild: ^7building rights for ^7%s^7 restored by %s\n\"", + vic->client->pers.netname, + ( ent ) ? ent->client->pers.netname : "console" ) ); + } + else + { + if( !Q_stricmp( cmd, "allowbuild" ) ) + { + ADMP( "^3!allowbuild: ^7player already has building rights\n" ); + return qtrue; + } + vic->client->pers.denyBuild = qtrue; + CPx( pids[ 0 ], "cp \"^1You've lost your building rights\"" ); + AP( va( + "print \"^3!denybuild: ^7building rights for ^7%s^7 revoked by ^7%s\n\"", + vic->client->pers.netname, + ( ent ) ? ent->client->pers.netname : "console" ) ); + } + ClientUserinfoChanged( pids[ 0 ] ); + return qtrue; +} + qboolean G_admin_listadmins( gentity_t *ent, int skiparg ) { int i, found = 0; @@ -2724,6 +2796,7 @@ qboolean G_admin_nextmap( gentity_t *ent, int skiparg ) AP( va( "print \"^3!nextmap: ^7%s^7 decided to load the next map\n\"", ( ent ) ? ent->client->pers.netname : "console" ) ); level.lastWin = PTE_NONE; + trap_SetConfigstring( CS_WINNER, "Evacuation" ); LogExit( va( "nextmap was run by %s", ( ent ) ? ent->client->pers.netname : "console" ) ); return qtrue; diff --git a/src/game/g_admin.h b/src/game/g_admin.h index 21e63fa1..c4bf3928 100644 --- a/src/game/g_admin.h +++ b/src/game/g_admin.h @@ -153,6 +153,7 @@ qboolean G_admin_listlayouts( gentity_t *ent, int skiparg ); qboolean G_admin_listplayers( gentity_t *ent, int skiparg ); qboolean G_admin_map( gentity_t *ent, int skiparg ); qboolean G_admin_mute( gentity_t *ent, int skiparg ); +qboolean G_admin_denybuild( gentity_t *ent, int skiparg ); qboolean G_admin_showbans( gentity_t *ent, int skiparg ); qboolean G_admin_help( gentity_t *ent, int skiparg ); qboolean G_admin_admintest( gentity_t *ent, int skiparg ); diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c index 5aba0ece..aeac1be6 100644 --- a/src/game/g_buildable.c +++ b/src/game/g_buildable.c @@ -3691,3 +3691,23 @@ void G_LayoutLoad( void ) } } +void G_BaseSelfDestruct( pTeam_t team ) +{ + int i; + gentity_t *ent; + + for( i = MAX_CLIENTS; i < level.num_entities; i++ ) + { + ent = &level.gentities[ i ]; + if( ent->health <= 0 ) + continue; + if( ent->s.eType != ET_BUILDABLE ) + continue; + if( team == PTE_HUMANS && ent->biteam != BIT_HUMANS ) + continue; + if( team == PTE_ALIENS && ent->biteam != BIT_ALIENS ) + continue; + G_Damage( ent, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE ); + } +} + diff --git a/src/game/g_client.c b/src/game/g_client.c index 6184bad0..a2607cdf 100644 --- a/src/game/g_client.c +++ b/src/game/g_client.c @@ -1141,10 +1141,12 @@ void ClientUserinfoChanged( int clientNum ) // print scoreboards, display models, and play custom sounds Com_sprintf( userinfo, sizeof( userinfo ), - "n\\%s\\t\\%i\\model\\%s\\hmodel\\%s" - "\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d", + "n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\" + "hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\" + "tl\\%d\\ig\\%16s", client->pers.netname, team, model, model, c1, c2, - client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader ); + client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, + teamLeader, BG_ClientListString( &client->sess.ignoreList ) ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); @@ -1663,6 +1665,9 @@ void ClientDisconnect( int clientNum ) // stop any following clients for( i = 0; i < level.maxclients; i++ ) { + // remove any /ignore settings for this clientNum + BG_ClientListRemove( &level.clients[ i ].sess.ignoreList, clientNum ); + if( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR && level.clients[ i ].sess.spectatorState == SPECTATOR_FOLLOW && level.clients[ i ].sess.spectatorClient == clientNum ) diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index cc8e0a5e..4ffd1144 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -777,6 +777,8 @@ G_Say */ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message ) { + qboolean ignore = qfalse; + if( !other ) return; @@ -800,8 +802,12 @@ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, cons // specs with ADMF_SPEC_ALLCHAT flag can see team chat } - trap_SendServerCommand( other-g_entities, va( "%s \"%s%c%c%s\"", + if( BG_ClientListTest( &other->client->sess.ignoreList, ent-g_entities ) ) + ignore = qtrue; + + trap_SendServerCommand( other-g_entities, va( "%s \"%s%s%c%c%s\"", mode == SAY_TEAM ? "tchat" : "chat", + ( ignore ) ? "[skipnotify]" : "", name, Q_COLOR_ESCAPE, color, message ) ); } @@ -994,6 +1000,8 @@ void Cmd_CallVote_f( gentity_t *ent ) int i; char arg1[ MAX_STRING_TOKENS ]; char arg2[ MAX_STRING_TOKENS ]; + int clientNum = -1; + char name[ MAX_NETNAME ]; if( !g_allowVote.integer ) { @@ -1017,12 +1025,6 @@ void Cmd_CallVote_f( gentity_t *ent ) return; } - if( ent->client->pers.teamSelection == PTE_NONE ) - { - trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator\n\"" ); - return; - } - // make sure it is a valid command to vote on trap_Argv( 1, arg1, sizeof( arg1 ) ); trap_Argv( 2, arg2, sizeof( arg2 ) ); @@ -1040,74 +1042,108 @@ void Cmd_CallVote_f( gentity_t *ent ) trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.voteString ) ); } - if( !Q_stricmp( arg1, "kick" ) ) + // detect clientNum for partial name match votes + if( !Q_stricmp( arg1, "kick" ) || + !Q_stricmp( arg1, "mute" ) || + !Q_stricmp( arg1, "unmute" ) ) { - int clientNum; int clientNums[ MAX_CLIENTS ] = { -1 }; if( G_ClientNumbersFromString( arg2, clientNums ) == 1 ) { - // there was one partial name match name was clientNum + // there was only one partial name match clientNum = clientNums[ 0 ]; } else { - // look for an exact name match before bailing out + // look for an exact name match (sets clientNum to -1 if it fails) clientNum = G_ClientNumberFromString( ent, arg2 ); - if( clientNum == -1 ) - { - trap_SendServerCommand( ent-g_entities, - "print \"callvote: invalid player\n\"" ); - return; - } } - Q_strncpyz( arg1, "clientkick", sizeof( arg1 ) ); - Q_strncpyz( arg2, va( "%d", clientNum ), sizeof( arg2 ) ); - } - if( !Q_stricmp( arg1, "clientkick" ) ) + if( clientNum != -1 && + level.clients[ clientNum ].pers.connected == CON_DISCONNECTED ) + { + clientNum = -1; + } + + if( clientNum != -1 ) + { + Q_strncpyz( name, level.clients[ clientNum ].pers.netname, + sizeof( name ) ); + Q_CleanStr( name ); + } + } + + if( !Q_stricmp( arg1, "kick" ) ) { - char kickee[ MAX_NETNAME ]; - int clientNum = 0; + if( clientNum == -1 ) + { + trap_SendServerCommand( ent-g_entities, + "print \"callvote: invalid player\n\"" ); + return; + } - //check arg2 is a number - for( i = 0; arg2[ i ]; i++ ) + if( G_admin_permission( &g_entities[ clientNum ], ADMF_IMMUNITY ) ) { - if( arg2[ i ] < '0' || arg2[ i ] > '9' ) - { - clientNum = -1; - break; - } + trap_SendServerCommand( ent-g_entities, + "print \"callvote: admin is immune from vote kick\n\"" ); + return; } - if( clientNum > -1 ) - clientNum = atoi( arg2 ); - if( clientNum >= 0 && clientNum < level.maxclients ) + // use ip in case this player disconnects before the vote ends + Com_sprintf( level.voteString, sizeof( level.voteString ), + "!ban %s %d vote kick", level.clients[ clientNum ].pers.ip, + g_adminTempBan.integer + 1 ); + Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), + "Kick player \'%s\'", name ); + } + else if( !Q_stricmp( arg1, "mute" ) ) + { + + if( clientNum == -1 ) { - if( G_admin_permission( &g_entities[ clientNum ], ADMF_IMMUNITY ) ) - { - trap_SendServerCommand( ent-g_entities, - "print \"callvote: admin is immune from vote kick\n\"" ); - return; - } + trap_SendServerCommand( ent-g_entities, + "print \"callvote: invalid player\n\"" ); + return; + } + + if( level.clients[ clientNum ].pers.muted ) + { + trap_SendServerCommand( ent-g_entities, + "print \"callvote: player is already muted\n\"" ); + return; + } - if( level.clients[ clientNum ].pers.connected != CON_DISCONNECTED ) - { - Q_strncpyz( kickee, level.clients[ clientNum ].pers.netname, - sizeof( kickee ) ); - Q_CleanStr( kickee ); - // use ip in case this player disconnects before the vote ends - Com_sprintf( level.voteString, sizeof( level.voteString ), - "!ban %s %d vote kick", level.clients[ clientNum ].pers.ip, - g_adminTempBan.integer + 1 ); - Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), - "Kick player \'%s\'", kickee ); - } - else - return; + if( G_admin_permission( &g_entities[ clientNum ], ADMF_IMMUNITY ) ) + { + trap_SendServerCommand( ent-g_entities, + "print \"callvote: admin is immune from vote mute\n\"" ); + return; } - else + Com_sprintf( level.voteString, sizeof( level.voteString ), + "!mute %i", clientNum ); + Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), + "Mute player \'%s\'", name ); + } + else if( !Q_stricmp( arg1, "unmute" ) ) + { + + if( clientNum == -1 ) + { + trap_SendServerCommand( ent-g_entities, + "print \"callvote: invalid player\n\"" ); + return; + } + if( !level.clients[ clientNum ].pers.muted ) + { + trap_SendServerCommand( ent-g_entities, + "print \"callvote: player is not currently muted\n\"" ); return; + } + Com_sprintf( level.voteString, sizeof( level.voteString ), + "!unmute %i", clientNum ); + Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), + "Un-Mute player \'%s\'", name ); } else if( !Q_stricmp( arg1, "map_restart" ) ) { @@ -1126,19 +1162,19 @@ void Cmd_CallVote_f( gentity_t *ent ) Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 ); Com_sprintf( level.voteDisplayString, - sizeof( level.voteDisplayString ), "Change to map \'%s\'", arg2 ); + sizeof( level.voteDisplayString ), "Change to map '%s'", arg2 ); } - else if( !Q_stricmp( arg1, "nextmap" ) ) + else if( !Q_stricmp( arg1, "draw" ) ) { - Com_sprintf( level.voteString, sizeof( level.voteString ), "advanceMapRotation" ); + Com_sprintf( level.voteString, sizeof( level.voteString ), "evacuation" ); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), - "Skip to next map in rotation" ); + "End match in a draw" ); } else { trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string\n\"" ); - trap_SendServerCommand( ent-g_entities, "print \"Valid vote commands are: map_restart, nextmap, " - "map <mapname>, kick <player>, clientkick <clientnum>\n\"" ); + trap_SendServerCommand( ent-g_entities, "print \"Valid vote commands are: " + "map_restart, draw, kick, mute and unmute\n" ); return; } @@ -1184,12 +1220,6 @@ void Cmd_Vote_f( gentity_t *ent ) return; } - if( ent->client->pers.teamSelection == PTE_NONE ) - { - trap_SendServerCommand( ent-g_entities, "print \"Not allowed to vote as spectator\n\"" ); - return; - } - trap_SendServerCommand( ent-g_entities, "print \"Vote cast\n\"" ); ent->client->ps.eFlags |= EF_VOTED; @@ -1221,6 +1251,15 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) int i, team, cs_offset; char arg1[ MAX_STRING_TOKENS ]; char arg2[ MAX_STRING_TOKENS ]; + int clientNum = -1; + char name[ MAX_NETNAME ]; + + if( ent->client->pers.teamSelection == PTE_NONE ) + { + trap_SendServerCommand( ent-g_entities, + "print \"Not allowed to call a team vote as a spectator\n\"" ); + return; + } team = ent->client->ps.stats[ STAT_PTEAM ]; @@ -1253,12 +1292,6 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) return; } - if( ent->client->pers.teamSelection == PTE_NONE ) - { - trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator\n\"" ); - return; - } - // make sure it is a valid command to vote on trap_Argv( 1, arg1, sizeof( arg1 ) ); trap_Argv( 2, arg2, sizeof( arg2 ) ); @@ -1268,61 +1301,52 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) trap_SendServerCommand( ent-g_entities, "print \"Invalid team vote string\n\"" ); return; } - - if( !Q_stricmp( arg1, "teamkick" ) ) + + // detect clientNum for partial name match votes + if( !Q_stricmp( arg1, "kick" ) || + !Q_stricmp( arg1, "denybuild" ) || + !Q_stricmp( arg1, "allowbuild" ) ) { - int clientNum; int clientNums[ MAX_CLIENTS ] = { -1 }; if( G_ClientNumbersFromString( arg2, clientNums ) == 1 ) { - // there was one partial name match or name was clientNum + // there was only one partial name match clientNum = clientNums[ 0 ]; } else { - // look for an exact name match before bailing out + // look for an exact name match (sets clientNum to -1 if it fails) clientNum = G_ClientNumberFromString( ent, arg2 ); - if( clientNum == -1 ) - { - trap_SendServerCommand( ent-g_entities, - "print \"callvote: invalid player\n\"" ); - return; - } } - Q_strncpyz( arg1, "teamclientkick", sizeof( arg1 ) ); - Q_strncpyz( arg2, va( "%d", clientNum ), sizeof( arg2 ) ); - } - - if( !Q_stricmp( arg1, "teamclientkick" ) ) - { - int clientNum = 0; - char kickee[ MAX_NETNAME ]; - //check arg2 is a number - for( i = 0; arg2[ i ]; i++ ) + // make sure this player is on the same team + if( clientNum != -1 && level.clients[ clientNum ].pers.teamSelection != + ent->client->pers.teamSelection ) { - if( arg2[ i ] < '0' || arg2[ i ] > '9' ) - { - clientNum = -1; - break; - } + clientNum = -1; + } + + if( clientNum != -1 && + level.clients[ clientNum ].pers.connected == CON_DISCONNECTED ) + { + clientNum = -1; } - if( clientNum > -1 ) - clientNum = atoi( arg2 ); - if( clientNum >= 0 && clientNum < level.maxclients ) + if( clientNum != -1 ) { - if( level.clients[ clientNum ].pers.connected == CON_DISCONNECTED ) - clientNum = -1; - else if( level.clients[ clientNum ].pers.teamSelection != team ) - clientNum = -1; + Q_strncpyz( name, level.clients[ clientNum ].pers.netname, + sizeof( name ) ); + Q_CleanStr( name ); } + } - if( clientNum < 0 ) + if( !Q_stricmp( arg1, "kick" ) ) + { + if( clientNum == -1 ) { - trap_SendServerCommand( ent-g_entities, va( "print \"client %s " - S_COLOR_WHITE "is not a valid player on your team\n\"", arg2 ) ); + trap_SendServerCommand( ent-g_entities, + "print \"callvote: invalid player\n\"" ); return; } @@ -1333,9 +1357,6 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) return; } - Q_strncpyz( kickee, level.clients[ clientNum ].pers.netname, - sizeof( kickee ) ); - Q_CleanStr( kickee ); // use ip in case this player disconnects before the vote ends Com_sprintf( level.teamVoteString[ cs_offset ], @@ -1344,13 +1365,73 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) g_adminTempBan.integer + 1 ); Com_sprintf( level.teamVoteDisplayString[ cs_offset ], sizeof( level.teamVoteDisplayString[ cs_offset ] ), - "Kick player \'%s\'", kickee ); + "Kick player '%s'", name ); + } + else if( !Q_stricmp( arg1, "denybuild" ) ) + { + if( clientNum == -1 ) + { + trap_SendServerCommand( ent-g_entities, + "print \"callvote: invalid player\n\"" ); + return; + } + + if( level.clients[ clientNum ].pers.denyBuild ) + { + trap_SendServerCommand( ent-g_entities, + "print \"callvote: player already lost building rights\n\"" ); + return; + } + + if( G_admin_permission( &g_entities[ clientNum ], ADMF_IMMUNITY ) ) + { + trap_SendServerCommand( ent-g_entities, + "print \"callteamvote: admin is immune from denybuild\n\"" ); + return; + } + + Com_sprintf( level.teamVoteString[ cs_offset ], + sizeof( level.teamVoteString[ cs_offset ] ), "!denybuild %i", clientNum ); + Com_sprintf( level.teamVoteDisplayString[ cs_offset ], + sizeof( level.teamVoteDisplayString[ cs_offset ] ), + "Take away building rights from '%s'", name ); + } + else if( !Q_stricmp( arg1, "allowbuild" ) ) + { + if( clientNum == -1 ) + { + trap_SendServerCommand( ent-g_entities, + "print \"callvote: invalid player\n\"" ); + return; + } + + if( !level.clients[ clientNum ].pers.denyBuild ) + { + trap_SendServerCommand( ent-g_entities, + "print \"callvote: player already has building rights\n\"" ); + return; + } + + Com_sprintf( level.teamVoteString[ cs_offset ], + sizeof( level.teamVoteString[ cs_offset ] ), "!allowbuild %i", clientNum ); + Com_sprintf( level.teamVoteDisplayString[ cs_offset ], + sizeof( level.teamVoteDisplayString[ cs_offset ] ), + "Allow '%s' to build", name ); + } + else if( !Q_stricmp( arg1, "admitdefeat" ) ) + { + Com_sprintf( level.teamVoteString[ cs_offset ], + sizeof( level.teamVoteString[ cs_offset ] ), "admitdefeat %i", team ); + Com_sprintf( level.teamVoteDisplayString[ cs_offset ], + sizeof( level.teamVoteDisplayString[ cs_offset ] ), + "Admit Defeat" ); } else { trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string\n\"" ); - trap_SendServerCommand( ent-g_entities, "print \"Valid team vote commands are: teamkick <player>, " - "teamclientkick <client>\n\"" ); + trap_SendServerCommand( ent-g_entities, + "print \"Valid team vote commands are: " + "kick, denybuild, allowbuild and surrender\n\"" ); return; } ent->client->pers.voteCount++; @@ -1714,6 +1795,13 @@ void Cmd_Destroy_f( gentity_t *ent, qboolean deconstruct ) trace_t tr; gentity_t *traceEnt; + if( ent->client->pers.denyBuild ) + { + trap_SendServerCommand( ent-g_entities, + "print \"Your building rights have been revoked\n\"" ); + return; + } + if( ent->client->ps.stats[ STAT_STATE ] & SS_HOVELING ) G_Damage( ent->client->hovel, ent, ent, forward, ent->s.origin, 10000, 0, MOD_SUICIDE ); @@ -2293,6 +2381,13 @@ void Cmd_Build_f( gentity_t *ent ) vec3_t origin; pTeam_t team; + if( ent->client->pers.denyBuild ) + { + trap_SendServerCommand( ent-g_entities, + "print \"Your building rights have been revoked\n\"" ); + return; + } + trap_Argv( 1, s, sizeof( s ) ); buildable = BG_FindBuildNumForName( s ); @@ -2670,6 +2765,70 @@ void Cmd_Test_f( gentity_t *ent ) ent->client->lastPoisonClient = ent;*/ } +static void Cmd_Ignore_f( gentity_t *ent, qboolean ignore ) +{ + int pids[ MAX_CLIENTS ]; + char name[ MAX_NAME_LENGTH ]; + const char *cmd; + int matches = 0; + int i; + + cmd = ( ignore ) ? "ignore" : "unignore"; + + if( trap_Argc() < 2 ) + { + trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" + "%s: usage \\%s [clientNum | partial name match]\n\"", cmd, cmd ) ); + return; + } + + Q_strncpyz( name, ConcatArgs( 1 ), sizeof( name ) ); + matches = G_ClientNumbersFromString( name, pids ); + if( matches < 1 ) + { + trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" + "%s: no clients match the name '%s'\n\"", cmd, name ) ); + return; + } + + for( i = 0; i < matches; i++ ) + { + if( ignore ) + { + if( !BG_ClientListTest( &ent->client->sess.ignoreList, pids[ i ] ) ) + { + BG_ClientListAdd( &ent->client->sess.ignoreList, pids[ i ] ); + ClientUserinfoChanged( ent->client->ps.clientNum ); + trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" + "ignore: added %s^7 to your ignore list\n\"", + level.clients[ pids[ i ] ].pers.netname ) ); + } + else + { + trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" + "ignore: %s^7 is already on your ignore list\n\"", + level.clients[ pids[ i ] ].pers.netname ) ); + } + } + else + { + if( BG_ClientListTest( &ent->client->sess.ignoreList, pids[ i ] ) ) + { + BG_ClientListRemove( &ent->client->sess.ignoreList, pids[ i ] ); + ClientUserinfoChanged( ent->client->ps.clientNum ); + trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" + "unignore: removed %s^7 from your ignore list\n\"", + level.clients[ pids[ i ] ].pers.netname ) ); + } + else + { + trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" + "unignore: %s^7 is not on your ignore list\n\"", + level.clients[ pids[ i ] ].pers.netname ) ); + } + } + } +} /* ================= @@ -2717,6 +2876,18 @@ void ClientCommand( int clientNum ) return; } + if( !Q_stricmp( cmd, "ignore" ) ) + { + Cmd_Ignore_f( ent, qtrue ); + return; + } + + if( !Q_stricmp( cmd, "unignore" ) ) + { + Cmd_Ignore_f( ent, qfalse ); + return; + } + if( G_admin_cmd_check( ent, qfalse ) ) return; @@ -2906,12 +3077,13 @@ void G_DecolorString( char *in, char *out ) void G_PrivateMessage( gentity_t *ent ) { int pids[ MAX_CLIENTS ]; + int ignoreids[ MAX_CLIENTS ]; char name[ MAX_NAME_LENGTH ]; char cmd[ 12 ]; char str[ MAX_STRING_CHARS ]; char *msg; char color; - int pcount, count = 0; + int pcount, matches, ignored = 0; int i; int skipargs = 0; qboolean teamonly = qfalse; @@ -2941,27 +3113,40 @@ void G_PrivateMessage( gentity_t *ent ) if( ent ) { - if( teamonly ) + int count = 0; + + for( i=0; i < pcount; i++ ) { - for( i=0; i < pcount; i++ ) + tmpent = &g_entities[ pids[ i ] ]; + + if( teamonly && !OnSameTeam( ent, tmpent ) ) + continue; + + if( BG_ClientListTest( &tmpent->client->sess.ignoreList, + ent-g_entities ) ) { - if( !OnSameTeam( ent, &g_entities[ pids[ i ] ] ) ) - continue; - pids[ count ] = pids[ i ]; - count++; + ignoreids[ ignored++ ] = pids[ i ]; + continue; } - pcount = count; + + pids[ count ] = pids[ i ]; + count++; } + matches = count; + } + else + { + matches = pcount; } color = teamonly ? COLOR_CYAN : COLOR_YELLOW; Q_strncpyz( str, - va( "^%csent to %i player%s: ^7", color, pcount, - ( pcount == 1 ) ? "" : "s" ), + va( "^%csent to %i player%s: ^7", color, matches, + ( matches == 1 ) ? "" : "s" ), sizeof( str ) ); - for( i=0; i < pcount; i++ ) + for( i=0; i < matches; i++ ) { tmpent = &g_entities[ pids[ i ] ]; @@ -2973,7 +3158,7 @@ void G_PrivateMessage( gentity_t *ent ) ( ent ) ? ent->client->pers.netname : "console", color, name, - pcount, + matches, color, msg, ent ? ent-g_entities : -1 ) ); @@ -2988,7 +3173,7 @@ void G_PrivateMessage( gentity_t *ent ) ( ent ) ? ent->client->pers.netname : "console" ) ); } - if( !pcount ) + if( !matches ) ADMP( va( "^3No player matching ^7\'%s^7\' ^3to send message to.\n", name ) ); else @@ -3001,5 +3186,19 @@ void G_PrivateMessage( gentity_t *ent ) ( ent ) ? ent->client->pers.netname : "console", name, msg ); } + + if( ignored ) + { + Q_strncpyz( str, va( "^%cignored by %i player%s: ^7", color, ignored, + ( ignored == 1 ) ? "" : "s" ), sizeof( str ) ); + for( i=0; i < ignored; i++ ) + { + tmpent = &g_entities[ ignoreids[ i ] ]; + if( i > 0 ) + Q_strcat( str, sizeof( str ), "^7, " ); + Q_strcat( str, sizeof( str ), tmpent->client->pers.netname ); + } + ADMP( va( "%s\n", str ) ); + } } diff --git a/src/game/g_local.h b/src/game/g_local.h index 3b423dab..8536cc64 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -299,6 +299,7 @@ typedef struct int spectatorClient; // for chasecam and follow mode int wins, losses; // tournament stats qboolean teamLeader; // true when this client is a team leader + clientList_t ignoreList; } clientSession_t; #define MAX_NETNAME 36 @@ -350,6 +351,7 @@ typedef struct char guid[ 33 ]; char ip[ 16 ]; qboolean muted; + qboolean denyBuild; int adminLevel; } clientPersistant_t; @@ -653,6 +655,8 @@ typedef struct int unlaggedTimes[ MAX_UNLAGGED_MARKERS ]; char layout[ MAX_QPATH ]; + + pTeam_t surrenderTeam; } level_locals_t; // @@ -741,6 +745,7 @@ void G_LayoutSave( char *name ); int G_LayoutList( const char *map, char *list, int len ); void G_LayoutSelect( void ); void G_LayoutLoad( void ); +void G_BaseSelfDestruct( pTeam_t team ); // // g_utils.c diff --git a/src/game/g_main.c b/src/game/g_main.c index a1525a9a..4f4423d4 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -144,6 +144,7 @@ static cvarTable_t gameCvarTable[ ] = { &g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse }, { NULL, "sv_mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, { NULL, "P", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, + { NULL, "ff", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse }, // latched vars @@ -469,8 +470,12 @@ void G_UpdateCvars( void ) cv->modificationCount = cv->vmCvar->modificationCount; if( cv->trackChange ) + { trap_SendServerCommand( -1, va( "print \"Server: %s changed to %s\n\"", cv->cvarName, cv->vmCvar->string ) ); + // update serverinfo in case this cvar is passed to clients indirectly + CalculateRanks( ); + } if( cv->teamShader ) remapped = qtrue; @@ -595,6 +600,7 @@ void G_InitGame( int levelTime, int randomSeed, int restart ) &level.clients[ 0 ].ps, sizeof( level.clients[ 0 ] ) ); trap_SetConfigstring( CS_INTERMISSION, "0" ); + trap_SetConfigstring( CS_WINNER, "" ); // test to see if a custom buildable layout will be loaded G_LayoutSelect( ); @@ -681,6 +687,7 @@ void G_ShutdownGame( int restart ) G_admin_namelog_cleanup( ); level.restarted = qfalse; + level.surrenderTeam = PTE_NONE; } @@ -1271,7 +1278,8 @@ void CalculateRanks( void ) int score; int newScore; gclient_t *cl; - char P[ MAX_CLIENTS + 1 ] = {""}; + char P[ MAX_CLIENTS + 1 ] = {""}; + int ff = 0; level.follow1 = -1; level.follow2 = -1; @@ -1342,6 +1350,16 @@ void CalculateRanks( void ) P[ i + 1 ] = '\0'; trap_Cvar_Set( "P", P ); + if( g_friendlyFire.integer ) + ff |= ( FFF_HUMANS | FFF_ALIENS ); + if( g_friendlyFireHumans.integer ) + ff |= FFF_HUMANS; + if( g_friendlyFireAliens.integer ) + ff |= FFF_ALIENS; + if( g_friendlyBuildableFire.integer ) + ff |= FFF_BUILDABLES; + trap_Cvar_Set( "ff", va( "%i", ff ) ); + qsort( level.sortedClients, level.numConnectedClients, sizeof( level.sortedClients[ 0 ] ), SortRanks ); @@ -1885,6 +1903,7 @@ void CheckExitRules( void ) { level.lastWin = PTE_NONE; trap_SendServerCommand( -1, "print \"Timelimit hit\n\"" ); + trap_SetConfigstring( CS_WINNER, "Stalemate" ); LogExit( "Timelimit hit." ); return; } @@ -1910,6 +1929,7 @@ void CheckExitRules( void ) //humans win level.lastWin = PTE_HUMANS; trap_SendServerCommand( -1, "print \"Humans win\n\""); + trap_SetConfigstring( CS_WINNER, "Humans Win" ); LogExit( "Humans win." ); } else if( level.uncondAlienWin || @@ -1920,6 +1940,7 @@ void CheckExitRules( void ) //aliens win level.lastWin = PTE_ALIENS; trap_SendServerCommand( -1, "print \"Aliens win\n\""); + trap_SetConfigstring( CS_WINNER, "Aliens Win" ); LogExit( "Aliens win." ); } } diff --git a/src/game/g_session.c b/src/game/g_session.c index ad9addc1..81eed74f 100644 --- a/src/game/g_session.c +++ b/src/game/g_session.c @@ -46,14 +46,15 @@ void G_WriteClientSessionData( gclient_t *client ) const char *s; const char *var; - s = va( "%i %i %i %i %i %i %i", + s = va( "%i %i %i %i %i %i %i %s", client->sess.sessionTeam, client->sess.spectatorTime, client->sess.spectatorState, client->sess.spectatorClient, client->sess.wins, client->sess.losses, - client->sess.teamLeader + client->sess.teamLeader, + BG_ClientListString( &client->sess.ignoreList ) ); var = va( "session%i", client - level.clients ); @@ -81,16 +82,19 @@ void G_ReadSessionData( gclient_t *client ) var = va( "session%i", client - level.clients ); trap_Cvar_VariableStringBuffer( var, s, sizeof(s) ); - sscanf( s, "%i %i %i %i %i %i %i", + // FIXME: should be using BG_ClientListParse() for ignoreList, but + // bg_lib.c's sscanf() currently lacks %s + sscanf( s, "%i %i %i %i %i %i %i %x%x", &sessionTeam, &client->sess.spectatorTime, &spectatorState, &client->sess.spectatorClient, &client->sess.wins, &client->sess.losses, - &teamLeader + &teamLeader, + &client->sess.ignoreList.hi, + &client->sess.ignoreList.lo ); - // bk001205 - format issues client->sess.sessionTeam = (team_t)sessionTeam; client->sess.spectatorState = (spectatorState_t)spectatorState; @@ -131,6 +135,7 @@ void G_InitSessionData( gclient_t *client, char *userinfo ) sess->spectatorState = SPECTATOR_FREE; sess->spectatorTime = level.time; sess->spectatorClient = -1; + memset( &sess->ignoreList, 0, sizeof( sess->ignoreList ) ); G_WriteClientSessionData( client ); } diff --git a/src/game/g_svcmds.c b/src/game/g_svcmds.c index df656446..e65015af 100644 --- a/src/game/g_svcmds.c +++ b/src/game/g_svcmds.c @@ -544,6 +544,38 @@ void Svcmd_LayoutLoad_f( void ) level.restarted = qtrue; } +static void Svcmd_AdmitDefeat_f( void ) +{ + int team; + char teamNum[ 2 ]; + + if( trap_Argc( ) != 2 ) + { + G_Printf("admitdefeat: must provide a team\n"); + return; + } + trap_Argv( 1, teamNum, sizeof( teamNum ) ); + team = atoi( teamNum ); + if( team == PTE_ALIENS || teamNum[ 0 ] == 'a' ) + { + level.surrenderTeam = PTE_ALIENS; + G_BaseSelfDestruct( PTE_ALIENS ); + G_TeamCommand( PTE_ALIENS, "cp \"Hivemind Link Broken\" 1"); + trap_SendServerCommand( -1, "print \"Alien team has admitted defeat\n\"" ); + } + else if( team == PTE_HUMANS || teamNum[ 0 ] == 'h' ) + { + level.surrenderTeam = PTE_HUMANS; + G_BaseSelfDestruct( PTE_HUMANS ); + G_TeamCommand( PTE_HUMANS, "cp \"Life Support Terminated\" 1"); + trap_SendServerCommand( -1, "print \"Human team has admitted defeat\n\"" ); + } + else + { + G_Printf("admitdefeat: invalid team\n"); + } +} + /* ================= ConsoleCommand @@ -656,6 +688,21 @@ qboolean ConsoleCommand( void ) return qtrue; } + if( !Q_stricmp( cmd, "admitdefeat" ) ) + { + Svcmd_AdmitDefeat_f( ); + return qtrue; + } + + if( !Q_stricmp( cmd, "evacuation" ) ) + { + trap_SendServerCommand( -1, "print \"Evacuation ordered\n\"" ); + level.lastWin = PTE_NONE; + trap_SetConfigstring( CS_WINNER, "Evacuation" ); + LogExit( "Evacuation." ); + return qtrue; + } + // see if this is a a admin command if( G_admin_cmd_check( NULL, qfalse ) ) return qtrue; |