diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cgame/cg_draw.c | 16 | ||||
-rw-r--r-- | src/cgame/cg_drawtools.c | 30 | ||||
-rw-r--r-- | src/cgame/cg_local.h | 1 | ||||
-rw-r--r-- | src/cgame/cg_servercmds.c | 22 | ||||
-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 | ||||
-rw-r--r-- | src/ui/ui_gameinfo.c | 29 | ||||
-rw-r--r-- | src/ui/ui_local.h | 6 | ||||
-rw-r--r-- | src/ui/ui_main.c | 165 | ||||
-rw-r--r-- | src/ui/ui_shared.c | 23 |
20 files changed, 880 insertions, 177 deletions
diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c index 1a96e75f..89740c7c 100644 --- a/src/cgame/cg_draw.c +++ b/src/cgame/cg_draw.c @@ -3048,6 +3048,7 @@ static void CG_DrawVote( void ) char *s; int sec; vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f }; + char yeskey[ 32 ], nokey[ 32 ]; if( !cgs.voteTime ) return; @@ -3063,8 +3064,10 @@ static void CG_DrawVote( void ) if( sec < 0 ) sec = 0; - - s = va( "VOTE(%i): \"%s\" Yes:%i No:%i", sec, cgs.voteString, cgs.voteYes, cgs.voteNo ); + Q_strncpyz( yeskey, CG_KeyBinding( "vote yes" ), sizeof( yeskey ) ); + Q_strncpyz( nokey, CG_KeyBinding( "vote no" ), sizeof( nokey ) ); + s = va( "VOTE(%i): \"%s\" [%s]Yes:%i [%s]No:%i", sec, cgs.voteString, + yeskey, cgs.voteYes, nokey, cgs.voteNo ); CG_Text_Paint( 8, 340, 0.3f, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL ); } @@ -3078,6 +3081,7 @@ static void CG_DrawTeamVote( void ) char *s; int sec, cs_offset; vec4_t white = { 1.0f, 1.0f, 1.0f, 1.0f }; + char yeskey[ 32 ], nokey[ 32 ]; if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS ) cs_offset = 0; @@ -3101,8 +3105,12 @@ static void CG_DrawTeamVote( void ) if( sec < 0 ) sec = 0; - s = va( "TEAMVOTE(%i): \"%s\" Yes:%i No:%i", sec, cgs.teamVoteString[ cs_offset ], - cgs.teamVoteYes[cs_offset], cgs.teamVoteNo[ cs_offset ] ); + Q_strncpyz( yeskey, CG_KeyBinding( "teamvote yes" ), sizeof( yeskey ) ); + Q_strncpyz( nokey, CG_KeyBinding( "teamvote no" ), sizeof( nokey ) ); + s = va( "TEAMVOTE(%i): \"%s\" [%s]Yes:%i [%s]No:%i", sec, + cgs.teamVoteString[ cs_offset ], + yeskey, cgs.teamVoteYes[cs_offset], + nokey, cgs.teamVoteNo[ cs_offset ] ); CG_Text_Paint( 8, 360, 0.3f, white, s, 0, 0, ITEM_TEXTSTYLE_NORMAL ); } diff --git a/src/cgame/cg_drawtools.c b/src/cgame/cg_drawtools.c index 3151f66f..06ae0713 100644 --- a/src/cgame/cg_drawtools.c +++ b/src/cgame/cg_drawtools.c @@ -346,3 +346,33 @@ qboolean CG_WorldToScreen( vec3_t point, float *x, float *y ) return qtrue; } + +/* +================ +CG_KeyBinding +================ +*/ +char *CG_KeyBinding( const char *bind ) +{ + static char key[ 32 ]; + char bindbuff[ MAX_CVAR_VALUE_STRING ]; + int i; + + key[ 0 ] = '\0'; + // NOTE: change K_LAST_KEY to MAX_KEYS for full key support (eventually) + for( i = 0; i < K_LAST_KEY; i++ ) + { + trap_Key_GetBindingBuf( i, bindbuff, sizeof( bindbuff ) ); + if( !Q_stricmp( bindbuff, bind ) ) + { + trap_Key_KeynumToStringBuf( i, key, sizeof( key ) ); + break; + } + } + if( !key[ 0 ] ) + { + Q_strncpyz( key, "\\", sizeof( key ) ); + Q_strcat( key, sizeof( key ), bind ); + } + return key; +} diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h index 5bebfaf0..1f0fa005 100644 --- a/src/cgame/cg_local.h +++ b/src/cgame/cg_local.h @@ -1595,6 +1595,7 @@ void CG_DrawRect( float x, float y, float width, float height, float size void CG_DrawSides(float x, float y, float w, float h, float size); void CG_DrawTopBottom(float x, float y, float w, float h, float size); qboolean CG_WorldToScreen( vec3_t point, float *x, float *y ); +char *CG_KeyBinding( const char *bind ); // diff --git a/src/cgame/cg_servercmds.c b/src/cgame/cg_servercmds.c index 2665df16..6fcae7eb 100644 --- a/src/cgame/cg_servercmds.c +++ b/src/cgame/cg_servercmds.c @@ -377,8 +377,9 @@ static void CG_ConfigStringModified( void ) CG_NewClientInfo( num - CS_PLAYERS ); CG_BuildSpectatorString( ); } - else if( num == CS_FLAGSTATUS ) + else if( num == CS_WINNER ) { + trap_Cvar_Set( "ui_winner", str ); } else if( num == CS_SHADERSTATE ) { @@ -806,8 +807,9 @@ static void CG_ServerCommand( void ) { if( !cg_teamChatsOnly.integer ) { - trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); Q_strncpyz( text, CG_Argv( 1 ), MAX_SAY_TEXT ); + if( Q_stricmpn( text, "[skipnotify]", 12 ) ) + trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); CG_RemoveChatEscapeChar( text ); CG_Printf( "%s\n", text ); } @@ -817,14 +819,16 @@ static void CG_ServerCommand( void ) if( !strcmp( cmd, "tchat" ) ) { - if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) - trap_S_StartLocalSound( cgs.media.alienTalkSound, CHAN_LOCAL_SOUND ); - else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - trap_S_StartLocalSound( cgs.media.humanTalkSound, CHAN_LOCAL_SOUND ); - else - trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); - Q_strncpyz( text, CG_Argv( 1 ), MAX_SAY_TEXT ); + if( Q_stricmpn( text, "[skipnotify]", 12 ) ) + { + if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) + trap_S_StartLocalSound( cgs.media.alienTalkSound, CHAN_LOCAL_SOUND ); + else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) + trap_S_StartLocalSound( cgs.media.humanTalkSound, CHAN_LOCAL_SOUND ); + else + trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); + } CG_RemoveChatEscapeChar( text ); CG_Printf( "%s\n", text ); return; 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; diff --git a/src/ui/ui_gameinfo.c b/src/ui/ui_gameinfo.c index ab3ce6da..90047b9d 100644 --- a/src/ui/ui_gameinfo.c +++ b/src/ui/ui_gameinfo.c @@ -162,7 +162,7 @@ void UI_LoadArenas( void ) { strcat(filename, dirptr); UI_LoadArenasFromFile(filename); } - trap_Print( va( "%i arenas parsed\n", ui_numArenas ) ); + trap_Print( va( "[skipnotify]%i arenas parsed\n", ui_numArenas ) ); if (UI_OutOfMemory()) { trap_Print(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all arenas\n"); } @@ -305,3 +305,30 @@ char *UI_GetBotNameByNumber( int num ) { } return "Sarge"; } + +void UI_ServerInfo( void ) +{ + char info[ MAX_INFO_VALUE ]; + int i; + + info[0] = '\0'; + if( trap_GetConfigString( CS_SERVERINFO, info, sizeof( info ) ) ) + { + trap_Cvar_Set( "ui_serverinfo_mapname", + Info_ValueForKey( info, "mapname" ) ); + trap_Cvar_Set( "ui_serverinfo_timelimit", + Info_ValueForKey( info, "timelimit" ) ); + trap_Cvar_Set( "ui_serverinfo_sd", + Info_ValueForKey( info, "g_suddenDeathTime" ) ); + trap_Cvar_Set( "ui_serverinfo_hostname", + Info_ValueForKey( info, "sv_hostname" ) ); + trap_Cvar_Set( "ui_serverinfo_maxclients", + Info_ValueForKey( info, "sv_maxclients" ) ); + trap_Cvar_Set( "ui_serverinfo_version", + Info_ValueForKey( info, "version" ) ); + trap_Cvar_Set( "ui_serverinfo_unlagged", + Info_ValueForKey( info, "g_unlagged" ) ); + trap_Cvar_Set( "ui_serverinfo_ff", + Info_ValueForKey( info, "ff" ) ); + } +} diff --git a/src/ui/ui_local.h b/src/ui/ui_local.h index def61dc3..7afdf653 100644 --- a/src/ui/ui_local.h +++ b/src/ui/ui_local.h @@ -360,6 +360,7 @@ int UI_AdjustTimeByGame(int time); void UI_ShowPostGame(qboolean newHigh); void UI_ClearScores( void ); void UI_LoadArenas(void); +void UI_ServerInfo(void); // // ui_menu.c @@ -818,11 +819,16 @@ typedef struct { int playerRefresh; int playerIndex; int playerNumber; + int myPlayerIndex; + int ignoreIndex; qboolean teamLeader; char playerNames[MAX_CLIENTS][MAX_NAME_LENGTH]; + char rawPlayerNames[MAX_CLIENTS][MAX_NAME_LENGTH]; char teamNames[MAX_CLIENTS][MAX_NAME_LENGTH]; + char rawTeamNames[MAX_CLIENTS][MAX_NAME_LENGTH]; int clientNums[MAX_CLIENTS]; int teamClientNums[MAX_CLIENTS]; + clientList_t ignoreList[MAX_CLIENTS]; int mapCount; mapInfo mapList[MAX_MAPS]; diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c index f3a81f5a..ea7f84f4 100644 --- a/src/ui/ui_main.c +++ b/src/ui/ui_main.c @@ -1964,6 +1964,28 @@ static void UI_DrawAllMapsSelection(rectDef_t *rect, float scale, vec4_t color, } } +static void UI_DrawPlayerListSelection( rectDef_t *rect, float scale, + vec4_t color, int textStyle ) +{ + if( uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount ) + { + Text_Paint(rect->x, rect->y, scale, color, + uiInfo.rawPlayerNames[ uiInfo.playerIndex ], + 0, 0, textStyle); + } +} + +static void UI_DrawTeamListSelection( rectDef_t *rect, float scale, + vec4_t color, int textStyle ) +{ + if( uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount ) + { + Text_Paint(rect->x, rect->y, scale, color, + uiInfo.rawTeamNames[ uiInfo.teamIndex ], + 0, 0, textStyle); + } +} + static void UI_DrawOpponentName(rectDef_t *rect, float scale, vec4_t color, int textStyle) { Text_Paint(rect->x, rect->y, scale, color, UI_Cvar_VariableString("ui_opponentName"), 0, 0, textStyle); } @@ -2064,6 +2086,10 @@ static int UI_OwnerDrawWidth(int ownerDraw, float scale) { break; case UI_ALLMAPS_SELECTION: break; + case UI_PLAYERLIST_SELECTION: + break; + case UI_TEAMLIST_SELECTION: + break; case UI_OPPONENT_NAME: break; case UI_KEYBINDSTATUS: @@ -2127,18 +2153,29 @@ static void UI_BuildPlayerList( void ) { count = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); uiInfo.playerCount = 0; uiInfo.myTeamCount = 0; + uiInfo.myPlayerIndex = 0; playerTeamNumber = 0; for( n = 0; n < count; n++ ) { trap_GetConfigString( CS_PLAYERS + n, info, MAX_INFO_STRING ); if (info[0]) { - Q_strncpyz( uiInfo.playerNames[uiInfo.playerCount], Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH ); + BG_ClientListParse( &uiInfo.ignoreList[ uiInfo.playerCount ], + Info_ValueForKey( info, "ig" ) ); + Q_strncpyz( uiInfo.rawPlayerNames[uiInfo.playerCount], + Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH ); + Q_strncpyz( uiInfo.playerNames[uiInfo.playerCount], + Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH ); Q_CleanStr( uiInfo.playerNames[uiInfo.playerCount] ); uiInfo.clientNums[uiInfo.playerCount] = n; + if( n == uiInfo.playerNumber ) + uiInfo.myPlayerIndex = uiInfo.playerCount; uiInfo.playerCount++; team2 = atoi(Info_ValueForKey(info, "t")); if (team2 == team) { - Q_strncpyz( uiInfo.teamNames[uiInfo.myTeamCount], Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH ); + Q_strncpyz( uiInfo.rawTeamNames[uiInfo.myTeamCount], + Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH ); + Q_strncpyz( uiInfo.teamNames[uiInfo.myTeamCount], + Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH ); Q_CleanStr( uiInfo.teamNames[uiInfo.myTeamCount] ); uiInfo.teamClientNums[uiInfo.myTeamCount] = n; if (uiInfo.playerNumber == n) { @@ -2164,11 +2201,19 @@ static void UI_BuildPlayerList( void ) { static void UI_DrawSelectedPlayer(rectDef_t *rect, float scale, vec4_t color, int textStyle) { + char name[ MAX_NAME_LENGTH ]; + char *s; + if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) { uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; UI_BuildPlayerList(); } - Text_Paint(rect->x, rect->y, scale, color, (uiInfo.teamLeader) ? UI_Cvar_VariableString("cg_selectedPlayerName") : UI_Cvar_VariableString("name") , 0, 0, textStyle); + if( uiInfo.teamLeader ) + s = UI_Cvar_VariableString("cg_selectedPlayerName"); + else + s = UI_Cvar_VariableString("name"); + Q_strncpyz( name, s, sizeof( name ) ); + Text_Paint(rect->x, rect->y, scale, color, name, 0, 0, textStyle); } static void UI_DrawServerRefreshDate(rectDef_t *rect, float scale, vec4_t color, int textStyle) { @@ -2478,6 +2523,12 @@ static void UI_OwnerDraw( float x, float y, float w, float h, case UI_MAPS_SELECTION: UI_DrawAllMapsSelection(&rect, scale, color, textStyle, qfalse); break; + case UI_PLAYERLIST_SELECTION: + UI_DrawPlayerListSelection(&rect, scale, color, textStyle); + break; + case UI_TEAMLIST_SELECTION: + UI_DrawTeamListSelection(&rect, scale, color, textStyle); + break; case UI_OPPONENT_NAME: UI_DrawOpponentName(&rect, scale, color, textStyle); break; @@ -3904,6 +3955,8 @@ static void UI_RunMenuScript(char **args) { UI_LoadArenas(); UI_MapCountByGameType(qfalse); Menu_SetFeederSelection(NULL, FEEDER_ALLMAPS, 0, "createserver"); + } else if (Q_stricmp(name, "loadServerInfo") == 0) { + UI_ServerInfo(); } else if (Q_stricmp(name, "saveControls") == 0) { Controls_SetConfig(qtrue); } else if (Q_stricmp(name, "loadControls") == 0) { @@ -4142,15 +4195,47 @@ static void UI_RunMenuScript(char **args) { { if( uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount ) { - trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote clientkick %d\n", + trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote kick %d\n", + uiInfo.clientNums[ uiInfo.playerIndex ] ) ); + } + } + else if( Q_stricmp( name, "voteMute" ) == 0 ) + { + if( uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount ) + { + trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote mute %d\n", + uiInfo.clientNums[ uiInfo.playerIndex ] ) ); + } + } + else if( Q_stricmp( name, "voteUnMute" ) == 0 ) + { + if( uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount ) + { + trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote unmute %d\n", uiInfo.clientNums[ uiInfo.playerIndex ] ) ); } } else if( Q_stricmp( name, "voteTeamKick" ) == 0 ) { - if( uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.playerCount ) + if( uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount ) { - trap_Cmd_ExecuteText( EXEC_APPEND, va( "callteamvote teamclientkick %d\n", + trap_Cmd_ExecuteText( EXEC_APPEND, va( "callteamvote kick %d\n", + uiInfo.teamClientNums[ uiInfo.teamIndex ] ) ); + } + } + else if( Q_stricmp( name, "voteTeamDenyBuild" ) == 0 ) + { + if( uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount ) + { + trap_Cmd_ExecuteText( EXEC_APPEND, va( "callteamvote denybuild %d\n", + uiInfo.teamClientNums[ uiInfo.teamIndex ] ) ); + } + } + else if( Q_stricmp( name, "voteTeamAllowBuild" ) == 0 ) + { + if( uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount ) + { + trap_Cmd_ExecuteText( EXEC_APPEND, va( "callteamvote allowbuild %d\n", uiInfo.teamClientNums[ uiInfo.teamIndex ] ) ); } } @@ -4271,6 +4356,51 @@ static void UI_RunMenuScript(char **args) { } else if (Q_stricmp(name, "update") == 0) { if (String_Parse(args, &name2)) UI_Update(name2); + } else if (Q_stricmp(name, "InitIgnoreList") == 0) { + UI_BuildPlayerList(); + } else if (Q_stricmp(name, "ToggleIgnore") == 0) { + if( uiInfo.ignoreIndex >= 0 && uiInfo.ignoreIndex < uiInfo.playerCount ) + { + if( BG_ClientListTest( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ], + uiInfo.clientNums[ uiInfo.ignoreIndex ] ) ) + { + BG_ClientListRemove( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ], + uiInfo.clientNums[ uiInfo.ignoreIndex ] ); + trap_Cmd_ExecuteText( EXEC_NOW, va( "unignore %i\n", + uiInfo.clientNums[ uiInfo.ignoreIndex ] ) ); + } + else + { + BG_ClientListAdd( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ], + uiInfo.clientNums[ uiInfo.ignoreIndex ] ); + trap_Cmd_ExecuteText( EXEC_NOW, va( "ignore %i\n", + uiInfo.clientNums[ uiInfo.ignoreIndex ] ) ); + } + } + } else if (Q_stricmp(name, "IgnorePlayer") == 0) { + if( uiInfo.ignoreIndex >= 0 && uiInfo.ignoreIndex < uiInfo.playerCount ) + { + if( !BG_ClientListTest( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ], + uiInfo.clientNums[ uiInfo.ignoreIndex ] ) ) + { + BG_ClientListAdd( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ], + uiInfo.clientNums[ uiInfo.ignoreIndex ] ); + trap_Cmd_ExecuteText( EXEC_NOW, va( "ignore %i\n", + uiInfo.clientNums[ uiInfo.ignoreIndex ] ) ); + } + } + } else if (Q_stricmp(name, "UnIgnorePlayer") == 0) { + if( uiInfo.ignoreIndex >= 0 && uiInfo.ignoreIndex < uiInfo.playerCount ) + { + if( BG_ClientListTest( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ], + uiInfo.clientNums[ uiInfo.ignoreIndex ] ) ) + { + BG_ClientListRemove( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ], + uiInfo.clientNums[ uiInfo.ignoreIndex ] ); + trap_Cmd_ExecuteText( EXEC_NOW, va( "unignore %i\n", + uiInfo.clientNums[ uiInfo.ignoreIndex ] ) ); + } + } } else if (Q_stricmp(name, "setPbClStatus") == 0) { int stat; if ( Int_Parse( args, &stat ) ) @@ -4917,6 +5047,7 @@ UI_FeederCount ================== */ static int UI_FeederCount(float feederID) { + if (feederID == FEEDER_HEADS) { return UI_HeadCountByTeam(); } else if (feederID == FEEDER_Q3HEADS) { @@ -4943,6 +5074,8 @@ static int UI_FeederCount(float feederID) { UI_BuildPlayerList(); } return uiInfo.myTeamCount; + } else if (feederID == FEEDER_IGNORE_LIST) { + return uiInfo.playerCount; } else if (feederID == FEEDER_MODS) { return uiInfo.modCount; } else if (feederID == FEEDER_DEMOS) { @@ -5114,6 +5247,22 @@ static const char *UI_FeederItemText(float feederID, int index, int column, qhan if (index >= 0 && index < uiInfo.myTeamCount) { return uiInfo.teamNames[index]; } + } else if (feederID == FEEDER_IGNORE_LIST) { + if (index >= 0 && index < uiInfo.playerCount) { + switch( column ) + { + case 1: + // am I ignoring him + return ( BG_ClientListTest(&uiInfo.ignoreList[ uiInfo.myPlayerIndex ], + uiInfo.clientNums[ index ] ) ) ? "X" : ""; + case 2: + // is he ignoring me + return ( BG_ClientListTest( &uiInfo.ignoreList[ index ], + uiInfo.playerNumber ) ) ? "X" : ""; + default: + return uiInfo.playerNames[index]; + } + } } else if (feederID == FEEDER_MODS) { if (index >= 0 && index < uiInfo.modCount) { if (uiInfo.modList[index].modDescr && *uiInfo.modList[index].modDescr) { @@ -5277,6 +5426,8 @@ static void UI_FeederSelection(float feederID, int index) { uiInfo.playerIndex = index; } else if (feederID == FEEDER_TEAM_LIST) { uiInfo.teamIndex = index; + } else if (feederID == FEEDER_IGNORE_LIST) { + uiInfo.ignoreIndex = index; } else if (feederID == FEEDER_MODS) { uiInfo.modIndex = index; } else if (feederID == FEEDER_CINEMATICS) { @@ -6055,6 +6206,7 @@ vmCvar_t ui_serverStatusTimeOut; //TA: bank values vmCvar_t ui_bank; +vmCvar_t ui_winner; // bk001129 - made static to avoid aliasing @@ -6085,6 +6237,7 @@ static cvarTable_t cvarTable[] = { { &ui_spSkill, "g_spSkill", "2", CVAR_ARCHIVE }, { &ui_spSelection, "ui_spSelection", "", CVAR_ROM }, + { &ui_winner, "ui_winner", "", CVAR_ROM }, { &ui_browserMaster, "ui_browserMaster", "0", CVAR_ARCHIVE }, { &ui_browserGameType, "ui_browserGameType", "0", CVAR_ARCHIVE }, diff --git a/src/ui/ui_shared.c b/src/ui/ui_shared.c index f3132378..2ca88cb6 100644 --- a/src/ui/ui_shared.c +++ b/src/ui/ui_shared.c @@ -3436,25 +3436,10 @@ static bind_t g_bindings[] = { "weapnext", ']', -1, -1, -1 }, { "+button3", K_MOUSE3, -1, -1, -1 }, { "+button4", K_MOUSE4, -1, -1, -1 }, - { "prevTeamMember", 'w', -1, -1, -1 }, - { "nextTeamMember", 'r', -1, -1, -1 }, - { "nextOrder", 't', -1, -1, -1 }, - { "confirmOrder", 'y', -1, -1, -1 }, - { "denyOrder", 'n', -1, -1, -1 }, - { "taskOffense", 'o', -1, -1, -1 }, - { "taskDefense", 'd', -1, -1, -1 }, - { "taskPatrol", 'p', -1, -1, -1 }, - { "taskCamp", 'c', -1, -1, -1 }, - { "taskFollow", 'f', -1, -1, -1 }, - { "taskRetrieve", 'v', -1, -1, -1 }, - { "taskEscort", 'l', -1, -1, -1 }, - { "taskOwnFlag", 'i', -1, -1, -1 }, - { "taskSuicide", 'k', -1, -1, -1 }, - { "tauntKillInsult", K_F1, -1, -1, -1 }, - { "tauntPraise", K_F2, -1, -1, -1 }, - { "tauntTaunt", K_F3, -1, -1, -1 }, - { "tauntDeathInsult", K_F4, -1, -1, -1 }, - { "tauntGauntlet", K_F5, -1, -1, -1 }, + { "vote yes", K_F1, -1, -1, -1 }, + { "vote no", K_F2, -1, -1, -1 }, + { "teamvote yes", K_F3, -1, -1, -1 }, + { "teamvote no", K_F4, -1, -1, -1 }, { "scoresUp", K_KP_PGUP, -1, -1, -1 }, { "scoresDown", K_KP_PGDN, -1, -1, -1 }, // bk001205 - this one below was: '-1' |