summaryrefslogtreecommitdiff
path: root/src/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/game')
-rw-r--r--src/game/bg_misc.c81
-rw-r--r--src/game/bg_public.h19
-rw-r--r--src/game/g_active.c17
-rw-r--r--src/game/g_admin.c73
-rw-r--r--src/game/g_admin.h1
-rw-r--r--src/game/g_buildable.c20
-rw-r--r--src/game/g_client.c11
-rw-r--r--src/game/g_cmds.c453
-rw-r--r--src/game/g_local.h5
-rw-r--r--src/game/g_main.c23
-rw-r--r--src/game/g_session.c15
-rw-r--r--src/game/g_svcmds.c47
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;