diff --git a/src/game/g_active.c b/src/game/g_active.c index 5bdf19b..67e0ab7 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -1634,6 +1634,11 @@ void ClientThink_real( gentity_t *ent ) ent->client->pers.cmd.buttons |= BUTTON_GESTURE; } + if( ent->client->pers.muted ) + { + ent->client->pers.cmd.buttons &= ~BUTTON_GESTURE; + } + pm.ps = &client->ps; pm.pmext = &client->pmext; pm.cmd = *ucmd; diff --git a/src/game/g_admin.c b/src/game/g_admin.c index 47b99f5..1e122b9 100644 --- a/src/game/g_admin.c +++ b/src/game/g_admin.c @@ -67,11 +67,27 @@ g_admin_cmd_t g_admin_cmds[ ] = "specified", "[^3name|slot#|IP^7] (^5time^7) (^5reason^7)" }, - + + {"buildlog", G_admin_buildlog, "U", + "display a list of recent builds and deconstructs, optionally specifying" + " a team", + "(^5xnum^7) (^5#skip^7) (^5-name|num^7) (^5a|h^7)", + }, + {"cancelvote", G_admin_cancelvote, "c", "cancel a vote taking place", "" }, + + {"cp", G_admin_cp, "Z", + "display a message to all users", + "[^3message^7]" + }, + + {"customgrav", G_admin_customgrav, "Q", + "set the gravity for a specific player. Normal gives normal server gravity", + "[^3name|slot#^7] [^3#/normal^7]" + }, {"denybuild", G_admin_denybuild, "d", "take away a player's ability to build", @@ -88,10 +104,35 @@ g_admin_cmd_t g_admin_cmds[ ] = "[^3mapname^7] (^5layout^7)" }, + {"drop", G_admin_drop, "o", + "kick a client from the server without log", + "[^3name|slot#^7]" + }, + + {"drug", G_admin_drug, "Q", + "induce a gas like effect on a player", + "[^3name|slot#^7]" + }, + + {"explode", G_admin_explode, "Q", + "cause a player to explode. Caution: damages surrounding players and structures.", + "[^3name|slot#^7]" + }, + + {"forcespec", G_admin_forcespec, "F", + "disable joining of teams for a player", + "[^3name|slot#^7]" + }, + {"help", G_admin_help, "h", "display commands available to you or help on a specific command", "(^5command^7)" }, + + {"immunity", G_admin_immunity, "b", + "give a player ban immunity", + "[^3+|-^7](^5slot#^7)" + }, {"info", G_admin_info, "H", "read the server's information files", @@ -102,6 +143,11 @@ g_admin_cmd_t g_admin_cmds[ ] = "kick a player with an optional reason", "(^5reason^7)" }, + + {"L0", G_admin_L0, "l", + "Sets a level 1 to level 0", + "[^3name|slot#^7]" + }, {"L1", G_admin_L1, "l", "Sets a level 0 to level 1", @@ -122,6 +168,11 @@ g_admin_cmd_t g_admin_cmds[ ] = "display a list of all available layouts for a map", "(^5mapname^7)" }, + + {"listmaps", G_admin_listmaps, "j", + "display a list of available maps on the server", + "" + }, {"listplayers", G_admin_listplayers, "i", "display a list of players, their client numbers and their levels", @@ -137,6 +188,16 @@ g_admin_cmd_t g_admin_cmds[ ] = "load a map (and optionally force layout)", "[^3mapname^7] (^5layout^7)" }, + + {"maplog", G_admin_maplog, "j", + "show recently played maps", + "" + }, + + {"mix", G_admin_mix, "X", + "mix another player into yourself", + "[^3name|slot#^7]" + }, {"mute", G_admin_mute, "m", "mute a player, to prevent them from speaking, calling votes, using private messages, or renaming", @@ -157,6 +218,12 @@ g_admin_cmd_t g_admin_cmds[ ] = "pass a vote currently taking place", "" }, + + {"pause", G_admin_pause, "S", + "prevent a player from interacting with the game." + " * will pause all players, using no argument will pause game clock", + "(^5name|slot|*^7)" + }, {"putteam", G_admin_putteam, "p", "move a player to a specified team", @@ -182,6 +249,11 @@ g_admin_cmd_t g_admin_cmds[ ] = "restart the current map (optionally using named layout or keeping/switching teams)", "(^5layout^7) (^5keepteams|switchteams|keepteamslock|switchteamslock^7)" }, + + {"revert", G_admin_revert, "U", + "revert one or more buildlog events, optionally of only one team", + "(^5xnum^7) (^5#ID^7) (^5-name|num^7) (^5a|h^7)" + }, {"setlevel", G_admin_setlevel, "s", "sets the admin level of a player", @@ -192,6 +264,11 @@ g_admin_cmd_t g_admin_cmds[ ] = "display a (partial) list of active bans.", "(^5start at ban#^7|name|IP)" }, + + {"slap", G_admin_slap, "x", + "Do damage to a player, and send them flying", + "[^3name|slot^7] (damage)" + }, {"spec999", G_admin_spec999, "P", "move 999 pingers to the spectator team", @@ -203,10 +280,32 @@ g_admin_cmd_t g_admin_cmds[ ] = "moves you to the spectators", "" }, + + {"steal", G_admin_steal, "T", + "steal health from another player", + "[^3name|slot#^7]" + }, + + {"suspendban", G_admin_suspendban, "b", + "suspend a ban for a length of time. time is specified as numbers " + "followed by units 'w' (weeks), 'd' (days), 'h' (hours) or 'm' (minutes)," + " or seconds if no units are specified", + "[^5ban #^7] [^5length^7]" + }, + + {"switch", G_admin_switch, "X", + "switch places with somenone", + "[^3name|slot#^7]" + }, {"time", G_admin_time, "C", "show the current local server time", ""}, + + {"trade", G_admin_trade, "t", + "trade health with another player", + "[^3name|slot#^7]" + }, {"unban", G_admin_unban, "b", "unbans a player specified by the slot as seen in showbans", @@ -222,127 +321,28 @@ g_admin_cmd_t g_admin_cmds[ ] = "revoke designated builder privileges", "[^3name|slot#^7]" }, + + {"unforcespec", G_admin_unforcespec, "F", + "enable joining of teams for a player", + "[^3name|slot#^7]" + }, {"unmute", G_admin_mute, "m", "unmute a muted player", "[^3name|slot#^7]" }, + + {"unpause", G_admin_pause, "S", + "allow a player to interact with the game." + " * will unpause all players, using no argument will unpause game clock", + "(^5name|slot|*^7)" + }, { "warn", G_admin_warn, "w", "Warn a player to cease or face admin intervention", "[^3name|slot#^7] [reason]" }, - //added commands - {"forcespec", G_admin_forcespec, "F", - "disable joining of teams for a player", - "[^3name|slot#^7]" - }, - - {"unforcespec", G_admin_unforcespec, "F", - "enable joining of teams for a player", - "[^3name|slot#^7]" - }, - - {"pause", G_admin_pause, "S", - "prevent a player from interacting with the game." - " * will pause all players, using no argument will pause game clock", - "(^5name|slot|*^7)" - }, - {"unpause", G_admin_pause, "S", - "allow a player to interact with the game." - " * will unpause all players, using no argument will unpause game clock", - "(^5name|slot|*^7)" - }, - - {"buildlog", G_admin_buildlog, "U", - "display a list of recent builds and deconstructs, optionally specifying" - " a team", - "(^5xnum^7) (^5#skip^7) (^5-name|num^7) (^5a|h^7)", - }, - - {"revert", G_admin_revert, "U", - "revert one or more buildlog events, optionally of only one team", - "(^5xnum^7) (^5#ID^7) (^5-name|num^7) (^5a|h^7)" - }, - - {"slap", G_admin_slap, "x", - "Do damage to a player, and send them flying", - "[^3name|slot^7] (damage)" - }, - - - {"L0", G_admin_L0, "l", - "Sets a level 1 to level 0", - "[^3name|slot#^7]" - }, - //the long, long ass list of my commands - {"switch", G_admin_switch, "X", - "switch places with somenone", - "[^3name|slot#^7]" - }, - - {"mix", G_admin_mix, "X", - "mix another player into yourself", - "[^3name|slot#^7]" - }, - - {"steal", G_admin_steal, "T", - "steal health from another player", - "[^3name|slot#^7]" - }, - - {"trade", G_admin_trade, "t", - "trade health with another player", - "[^3name|slot#^7]" - }, - - {"drug", G_admin_drug, "Q", - "induce a gas like effect on a player", - "[^3name|slot#^7]" - }, - - {"drop", G_admin_drop, "o", - "kick a client from the server without log", - "[^3name|slot#^7]" - }, - - {"cp", G_admin_cp, "Z", - "display a message to all users", - "[^3message^7]" - }, - - {"customgrav", G_admin_customgrav, "Q", - "set the gravity for a specific player. Normal gives normal server gravity", - "[^3name|slot#^7] [^3#/normal^7]" - }, - - {"explode", G_admin_explode, "Q", - "cause a player to explode. Caution: damages surrounding players and structures.", - "[^3name|slot#^7]" - }, - - {"listmaps", G_admin_listmaps, "j", - "display a list of available maps on the server", - "" - }, - - {"maplog", G_admin_maplog, "j", - "show recently played maps", - "" - }, - - {"immunity", G_admin_immunity, "b", - "give a player ban immunity", - "[^3+|-^7](^5slot#^7)" - }, - - {"suspendban", G_admin_suspendban, "b", - "suspend a ban for a length of time. time is specified as numbers " - "followed by units 'w' (weeks), 'd' (days), 'h' (hours) or 'm' (minutes)," - " or seconds if no units are specified", - "[^5ban #^7] [^5length^7]" - }, }; @@ -1108,81 +1108,92 @@ void G_admin_duration( int secs, char *duration, int dursize ) qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen ) { - char *guid, *ip; - int i; + static char lastConnectIP[ 16 ] = {""}; + static int lastConnectTime = 0; + char guid[ 33 ]; + char ip[ 16 ]; + char *value; qtime_t qt; int t; + int i; *reason = '\0'; - t = trap_RealTime( &qt ); if( !*userinfo ) return qfalse; - ip = Info_ValueForKey( userinfo, "ip" ); + + value = Info_ValueForKey( userinfo, "ip" ); + Q_strncpyz( ip, value, sizeof( ip ) ); + // strip port + value = strchr( ip, ':' ); + if ( value ) + *value = '\0'; if( !*ip ) return qfalse; - guid = Info_ValueForKey( userinfo, "cl_guid" ); + + value = Info_ValueForKey( userinfo, "cl_guid" ); + Q_strncpyz( guid, value, sizeof( guid ) ); + + t = trap_RealTime( &qt ); for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) { // 0 is for perm ban if( g_admin_bans[ i ]->expires != 0 && - ( g_admin_bans[ i ]->expires - t ) < 1 ) + ( g_admin_bans[ i ]->expires - t ) < 1 ) continue; - if( g_admin_bans[ i ]->suspend >= t ) + if( g_admin_bans[ i ]->suspend >= t ) continue; if( strstr( ip, g_admin_bans[ i ]->ip ) == ip ) { char duration[ 32 ]; + qboolean immune = qfalse; - - /* ban immunity check */ - if (*guid) + // ban immunity + if( *guid ) { int j; - char *flags; for( j = 0; j < MAX_ADMIN_ADMINS && g_admin_admins[ j ]; j++ ) { - if( !Q_stricmp( guid, g_admin_admins[ j ]->guid ) ) + if( !Q_stricmp( guid, g_admin_admins[ j ]->guid ) && + strchr( g_admin_admins[ j ]->flags, ADMF_BANIMMUNITY ) ) { - flags = g_admin_admins[ j ]->flags; - while( *flags ) - { - if( *flags == ADMF_BANIMMUNITY ) - { - G_Printf("Player with ban immunity connected from IP %s\n", ip); - return qfalse; - } - flags++; - } + immune = qtrue; + break; } } } + if( !immune ) + { + G_admin_duration( ( g_admin_bans[ i ]->expires - t ), + duration, sizeof( duration ) ); - - G_admin_duration( ( g_admin_bans[ i ]->expires - t ), - duration, sizeof( duration ) ); - Com_sprintf( - reason, - rlen, - "Banned player %s^7 (%s) tried to connnect (ban #%i by %s^7 expires %s reason: %s^7 )", - Info_ValueForKey( userinfo, "name" ), - g_admin_bans[ i ]->name, - i+1, - g_admin_bans[ i ]->banner, - duration, - g_admin_bans[ i ]->reason - ); - G_AdminsPrintf("%s\n",reason); - Com_sprintf( - reason, - rlen, - "You have been banned by %s^7 reason: %s^7 expires: %s", - g_admin_bans[ i ]->banner, - g_admin_bans[ i ]->reason, - duration - ); - G_LogPrintf("Banned player tried to connect from IP %s\n", ip); - return qtrue; + if( t - lastConnectTime >= 10 || + Q_stricmp( lastConnectIP, ip ) != 0 ) + { + lastConnectTime = t; + Q_strncpyz( lastConnectIP, ip, sizeof( lastConnectIP ) ); + + G_AdminsPrintf( + "Banned player %s^7 (%s^7) tried to connect (ban #%i by %s^7 expires %s reason: %s^7 )\n", + Info_ValueForKey( userinfo, "name" ), + g_admin_bans[ i ]->name, + i+1, + g_admin_bans[ i ]->banner, + duration, + g_admin_bans[ i ]->reason ); + } + + Com_sprintf( + reason, + rlen, + "You have been banned by %s^7 reason: %s^7 expires: %s", + g_admin_bans[ i ]->banner, + g_admin_bans[ i ]->reason, + duration ); + G_LogPrintf("Banned player tried to connect from IP %s\n", ip); + + return qtrue; + } } if( *guid && !Q_stricmp( g_admin_bans[ i ]->guid, guid ) ) { @@ -1848,6 +1859,48 @@ qboolean G_admin_setlevel( gentity_t *ent, int skiparg ) return qtrue; } +static void admin_check_duplicate_ban( int ban ) +{ + qtime_t qt; + int t; + int i, j; + qboolean immune; + + if ( ban < 0 || ban >= MAX_ADMIN_BANS || !g_admin_bans[ ban ] ) + return; + + t = trap_RealTime( &qt ); + for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ ) + { + if( g_admin_bans[ i ]->expires != 0 + && ( g_admin_bans[ i ]->expires - t ) < 1 ) + { + continue; + } + + if( i != ban && + strstr( g_admin_bans[ ban ]->ip, g_admin_bans[ i ]->ip ) == g_admin_bans[ ban ]->ip ) + { + immune = qfalse; + + for( j = 0; j < MAX_ADMIN_ADMINS && g_admin_admins[ j ]; j++ ) + { + if( !Q_stricmp( g_admin_bans[ ban ]->guid, g_admin_admins[ j ]->guid ) && + strchr( g_admin_admins[ j ]->flags, ADMF_BANIMMUNITY ) != NULL ) + { + immune = qtrue; + } + } + + G_AdminsPrintf( "ban #%d duplicates %sban #%d%s.\n", + ban + 1, + ( g_admin_bans[ i ]->suspend > t ) ? "SUSPENDED " : "", + i + 1, + ( immune ) ? ", player has immunity" : "" ); + } + } +} + static qboolean admin_create_ban( gentity_t *ent, char *netname, char *guid, @@ -1911,6 +1964,9 @@ static qboolean admin_create_ban( gentity_t *ent, return qfalse; } g_admin_bans[ i ] = b; + + admin_check_duplicate_ban( i ); + return qtrue; } @@ -2012,7 +2068,10 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) char n2[ MAX_NAME_LENGTH ]; char s2[ MAX_NAME_LENGTH ]; char guid_stub[ 9 ]; - + char notice[51]; + + trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) ); + if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) && G_admin_permission( ent, ADMF_UNACCOUNTABLE ) ) { @@ -2176,10 +2235,10 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg ) trap_SendServerCommand( g_admin_namelog[ logmatch ]->slot, va( "disconnect \"You have been banned.\n" - "admin:\n%s^7\nduration:\n%s\nreason:\n%s\"", + "admin:\n%s^7\nduration:\n%s\nreason:\n%s\n%s\"", ( ent ) ? ent->client->pers.netname : "console", duration, - ( *reason ) ? reason : "kicked by admin" ) ); + ( *reason ) ? reason : "kicked by admin", notice ) ); trap_DropClient( g_admin_namelog[ logmatch ]->slot, va( "has been banned by %s^7 duration: %s, reason: %s", @@ -2370,6 +2429,8 @@ qboolean G_admin_map( gentity_t *ent, int skiparg ) } } + G_admin_maplog_result( "M" ); + trap_SendConsoleCommand( EXEC_APPEND, va( "map %s", map ) ); level.restarted = qtrue; AP( va( "print \"^3!map: ^7map '%s' started by %s^7 %s\n\"", map, @@ -2552,7 +2613,8 @@ qboolean G_admin_denybuild( gentity_t *ent, int skiparg ) } vic->client->pers.denyBuild = qtrue; vic->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE; - if( vic->client->ps.stats[ STAT_PCLASS ]== PCL_ALIEN_BUILDER0 || ent->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0_UPG ) + if( vic->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0 || + vic->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0_UPG ) { vic->client->ps.stats[ STAT_HEALTH ] = vic->health = 0; player_die( vic, vic, vic, 100000, MOD_SUICIDE ); @@ -3581,6 +3643,8 @@ qboolean G_admin_restart( gentity_t *ent, int skiparg ) trap_Cvar_Set( "g_lockTeamsAtStart", "1" ); } + G_admin_maplog_result( "R" ); + trap_SendConsoleCommand( EXEC_APPEND, "map_restart" ); if(teampref[ 0 ]) @@ -3601,6 +3665,7 @@ qboolean G_admin_nextmap( gentity_t *ent, int skiparg ) trap_SetConfigstring( CS_WINNER, "Evacuation" ); LogExit( va( "nextmap was run by %s", ( ent ) ? ent->client->pers.netname : "console" ) ); + G_admin_maplog_result( "N" ); return qtrue; } @@ -5372,6 +5437,43 @@ void G_admin_maplog_update( void ) maplog ) ); } +void G_admin_maplog_result( char *flag ) +{ + char maplog[ MAX_CVAR_VALUE_STRING ]; + int t; + + if( !flag ) + return; + + if( g_adminMapLog.string[ 0 ] && + g_adminMapLog.string[ 1 ] == ';' ) + { + // only one result allowed + return; + } + + if ( level.surrenderTeam != PTE_NONE ) + { + if( flag[ 0 ] == 'a' ) + { + if( level.surrenderTeam == PTE_HUMANS ) + flag = "A"; + } + else if( flag[ 0 ] == 'h' ) + { + if( level.surrenderTeam == PTE_ALIENS ) + flag = "H"; + } + } + + t = ( level.time - level.startTime ) / 1000; + Q_strncpyz( maplog, g_adminMapLog.string, sizeof( maplog ) ); + trap_Cvar_Set( "g_adminMapLog", va( "%1s;%03d:%02d;%s", + flag, + t / 60, t % 60, + maplog ) ); +} + qboolean G_admin_maplog( gentity_t *ent, int skiparg ) { char maplog[ MAX_CVAR_VALUE_STRING ]; @@ -5385,6 +5487,9 @@ qboolean G_admin_maplog( gentity_t *ent, int skiparg ) while( *ptr != '\0' && count < MAX_ADMIN_MAPLOG_LENGTH + 1 ) { char *end; + const char *result = NULL; + char *clock = NULL; + char *colon; end = ptr; while( *end != ' ' && *end != '\0' ) end++; @@ -5394,10 +5499,75 @@ qboolean G_admin_maplog( gentity_t *ent, int skiparg ) end++; } - ADMBP( va( "%s%s%s\n", + if( ptr[ 0 ] && ptr[ 1 ] == ';' ) + { + switch( ptr[ 0 ] ) + { + case 't': + result = "^7tie"; + break; + case 'a': + result = "^1Alien win"; + break; + case 'A': + result = "^1Alien win ^7/ Humans admitted defeat"; + break; + case 'h': + result = "^4Human win"; + break; + case 'H': + result = "^4Human win ^7/ Aliens admitted defeat"; + break; + case 'd': + result = "^5draw vote"; + break; + case 'N': + result = "^6admin loaded next map"; + break; + case 'r': + result = "^2restart vote"; + break; + case 'R': + result = "^6admin restarted map"; + break; + case 'm': + result = "^2map vote"; + break; + case 'M': + result = "^6admin changed map"; + break; + default: + result = ""; + break; + } + ptr += 2; + colon = strchr( ptr, ';' ); + if ( colon ) + { + clock = ptr; + ptr = colon + 1; + *colon = '\0'; + + // right justification with -6%s doesnt work.. + if( clock[ 0 ] == '0' && clock[ 1 ] != ':' ) + { + if( clock[ 1 ] == '0' && clock[ 2 ] != ':' ) + clock[ 1 ] = ' '; + clock[ 0 ] = ' '; + } + } + } + else if( count == 0 ) + { + result = "^3current map"; + clock = " -:--"; + } + + ADMBP( va( "%s%20s %-6s %s^7\n", ( count == 0 ) ? "^3" : "^7", ptr, - ( count == 0 ) ? " (current map)" : "" ) ); + ( clock ) ? clock : "", + ( result ) ? result : "" ) ); ptr = end; count++; diff --git a/src/game/g_admin.h b/src/game/g_admin.h index 8fc20cc..9944d83 100644 --- a/src/game/g_admin.h +++ b/src/game/g_admin.h @@ -203,6 +203,7 @@ qboolean G_admin_immunity( gentity_t *ent, int skiparg ); qboolean G_admin_suspendban( gentity_t *ent, int skiparg ); qboolean G_admin_maplog( gentity_t *ent, int skiparg ); void G_admin_maplog_update( void ); +void G_admin_maplog_result( char *flag ); //all of my commands WEEEEEEEEEE qboolean G_admin_switch( gentity_t *ent, int skiparg ); qboolean G_admin_mix( gentity_t *ent, int skiparg ); diff --git a/src/game/g_client.c b/src/game/g_client.c index 7324dbd..be63923 100644 --- a/src/game/g_client.c +++ b/src/game/g_client.c @@ -1300,6 +1300,11 @@ char *ClientConnect( int clientNum, qboolean firstTime ) G_InitSessionData( client, userinfo ); G_ReadSessionData( client ); + + if( firstTime ) + client->pers.firstConnect = qtrue; + else + client->pers.firstConnect = qfalse; // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index b86c659..484613f 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -1403,6 +1403,18 @@ void Cmd_CallVote_f( gentity_t *ent ) trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here\n\"" ); return; } + + if( g_voteMinTime.integer + && ent->client->pers.firstConnect + && level.time - ent->client->pers.enterTime < g_voteMinTime.integer * 1000 + && !G_admin_permission( ent, ADMF_NO_VOTE_LIMIT ) + && (level.numPlayingClients > 0 && level.numConnectedClients>1) ) + { + trap_SendServerCommand( ent-g_entities, va( + "print \"You must wait %d seconds after connecting before calling a vote\n\"", + g_voteMinTime.integer ) ); + return; + } if( level.voteTime ) { @@ -1569,6 +1581,16 @@ void Cmd_CallVote_f( gentity_t *ent ) } else if( !Q_stricmp( arg1, "map_restart" ) ) { + if( g_mapvoteMaxTime.integer + && level.time >= g_mapvoteMaxTime.integer * 1000 + && !G_admin_permission( ent, ADMF_NO_VOTE_LIMIT ) + && (level.numPlayingClients > 0 && level.numConnectedClients>1) ) + { + trap_SendServerCommand( ent-g_entities, va( + "print \"You cannot call for a restart after %d seconds\n\"", + g_mapvoteMaxTime.integer ) ); + return; + } Com_sprintf( level.voteString, sizeof( level.voteString ), "%s", arg1 ); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "Restart current map" ); @@ -1576,6 +1598,17 @@ void Cmd_CallVote_f( gentity_t *ent ) } else if( !Q_stricmp( arg1, "map" ) ) { + if( g_mapvoteMaxTime.integer + && level.time >= g_mapvoteMaxTime.integer * 1000 + && !G_admin_permission( ent, ADMF_NO_VOTE_LIMIT ) + && (level.numPlayingClients > 0 && level.numConnectedClients>1) ) + { + trap_SendServerCommand( ent-g_entities, va( + "print \"You cannot call for a mapchange after %d seconds\n\"", + g_mapvoteMaxTime.integer ) ); + return; + } + if( !trap_FS_FOpenFile( va( "maps/%s.bsp", arg2 ), NULL, FS_READ ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"callvote: " @@ -1705,7 +1738,24 @@ void Cmd_Vote_f( gentity_t *ent ) char msg[ 64 ]; if( !level.voteTime ) - { + { + if( ent->client->pers.teamSelection != PTE_NONE ) + { + // If there is a teamvote going on but no global vote, forward this vote on as a teamvote + // (ugly hack for 1.1 cgames + noobs who can't figure out how to use any command that isn't bound by default) + int cs_offset = 0; + if( ent->client->pers.teamSelection == PTE_ALIENS ) + cs_offset = 1; + + if( level.teamVoteTime[ cs_offset ] ) + { + if( !(ent->client->ps.eFlags & EF_TEAMVOTED ) ) + { + Cmd_TeamVote_f(ent); + return; + } + } + } trap_SendServerCommand( ent-g_entities, "print \"No vote in progress\n\"" ); return; } @@ -1789,6 +1839,18 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) "print \"You are muted and cannot call teamvotes\n\"" ); return; } + + if( g_voteMinTime.integer + && ent->client->pers.firstConnect + && level.time - ent->client->pers.enterTime < g_voteMinTime.integer * 1000 + && !G_admin_permission( ent, ADMF_NO_VOTE_LIMIT ) + && (level.numPlayingClients > 0 && level.numConnectedClients>1) ) + { + trap_SendServerCommand( ent-g_entities, va( + "print \"You must wait %d seconds after connecting before calling a vote\n\"", + g_voteMinTime.integer ) ); + return; + } // make sure it is a valid command to vote on trap_Argv( 1, arg1, sizeof( arg1 ) ); @@ -2382,19 +2444,34 @@ DBCommand Send command to all designated builders of selected team ================= */ -void DBCommand( pTeam_t team, const char *text ) +void DBCommand( gentity_t *builder, pTeam_t team, const char *text ) { int i; gentity_t *ent; + if( g_floodMinTime.integer && + G_Flood_Limited( builder ) ) + { + trap_SendServerCommand( builder-g_entities, + "print \"Your deconstruct attempt is flood-limited; wait before trying again\n\"" ); + return; + } + + trap_SendServerCommand( builder-g_entities, + "print \"This structure is protected by designated builder\n\"" ); + for( i = 0, ent = g_entities + i; i < level.maxclients; i++, ent++ ) { - if( !ent->client || ( ent->client->pers.connected != CON_CONNECTED ) || - ( ent->client->pers.teamSelection != team ) || - !ent->client->pers.designatedBuilder ) + if( !ent->client || ent->client->pers.connected != CON_CONNECTED ) continue; - trap_SendServerCommand( i, text ); + if( ( ent->client->pers.teamSelection == team && + ent->client->pers.designatedBuilder ) || + ( ent->client->pers.teamSelection == PTE_NONE && + G_admin_permission( ent, ADMF_SPEC_ALLCHAT ) ) ) + { + trap_SendServerCommand( i, text ); + } } } @@ -2435,11 +2512,10 @@ void Cmd_Destroy_f( gentity_t *ent ) if( ( ent->client->hovel->s.eFlags & EF_DBUILDER ) && !ent->client->pers.designatedBuilder ) { - trap_SendServerCommand( ent-g_entities, - "print \"This structure is protected by designated builder\n\"" ); - DBCommand( ent->client->pers.teamSelection, - va( "print \"%s^3 has attempted to decon a protected structure!\n\"", - ent->client->pers.netname ) ); + DBCommand( ent, ent->client->pers.teamSelection, + va( "print \"%s^3 has attempted to decon a protected %s!\n\"", + ent->client->pers.netname, + BG_FindHumanNameForBuildable( ent->client->hovel->s.modelindex ) ) ); return; } G_Damage( ent->client->hovel, ent, ent, forward, ent->s.origin, @@ -2469,11 +2545,10 @@ void Cmd_Destroy_f( gentity_t *ent ) if( ( traceEnt->s.eFlags & EF_DBUILDER ) && !ent->client->pers.designatedBuilder ) { - trap_SendServerCommand( ent-g_entities, - "print \"This structure is protected by designated builder\n\"" ); - DBCommand( ent->client->pers.teamSelection, - va( "print \"%s^3 has attempted to decon a protected structure!\n\"", - ent->client->pers.netname ) ); + DBCommand( ent, ent->client->pers.teamSelection, + va( "print \"%s^3 has attempted to decon a protected %s!\n\"", + ent->client->pers.netname, + BG_FindHumanNameForBuildable( traceEnt->s.modelindex ) ) ); return; } diff --git a/src/game/g_combat.c b/src/game/g_combat.c index aba1f8a..7cd9035 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -259,8 +259,8 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int // tjw: obviously this is a hack and belongs in the client, but // this works as a temporary fix. trap_SendServerCommand( -1, - va( "print \"%s^7 was killed by ^1TEAMMATE^7 %s\n\"", - self->client->pers.netname, attacker->client->pers.netname ) ); + va( "print \"%s^7 was killed by ^1TEAMMATE^7 %s^7 (Did %d damage to %d max)\n\"", + self->client->pers.netname, attacker->client->pers.netname, self->client->tkcredits[ attacker->s.number ], self->client->ps.stats[ STAT_MAX_HEALTH ] ) ); trap_SendServerCommand( attacker - g_entities, va( "cp \"You killed ^1TEAMMATE^7 %s\"", self->client->pers.netname ) ); } @@ -1100,6 +1100,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, int save; int asave = 0; int knockback; + int takeNoOverkill; float damagemodifier=0.0; if( !targ->takedamage ) @@ -1393,8 +1394,13 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, } - //Do the damage - targ->health = targ->health - take; + takeNoOverkill = take; + if( takeNoOverkill > targ->health ) + { + takeNoOverkill = targ->health; + } + + targ->health = targ->health - take; // do the damage if( targ->client ) targ->client->ps.stats[ STAT_HEALTH ] = targ->health; @@ -1406,6 +1412,8 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, { if( attacker != targ && !OnSameTeam( targ, attacker ) ) targ->credits[ attacker->client->ps.clientNum ] += take; + else if( attacker != targ && OnSameTeam( targ, attacker ) ) + targ->client->tkcredits[ attacker->client->ps.clientNum ] += takeNoOverkill; } if( targ->health <= 0 ) diff --git a/src/game/g_local.h b/src/game/g_local.h index bf63f89..15b752e 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -369,7 +369,7 @@ typedef struct int nameChangeTime; int nameChanges; - // used to save persistant[] values while in SPECTATOR_FOLLOW mode + // used to save persistant[] values while in SPECTATOR_FOLLOW mode int savedScore; int savedCredit; @@ -389,6 +389,8 @@ typedef struct statsCounters_t statscounters; qboolean cusgrav; int cusgravlvl; + + qboolean firstConnect; } clientPersistant_t; #define MAX_UNLAGGED_MARKERS 10 @@ -496,7 +498,9 @@ struct gclient_s unlagged_t unlaggedBackup; unlagged_t unlaggedCalc; int unlaggedTime; - + + int tkcredits[ MAX_CLIENTS ]; + }; @@ -786,6 +790,7 @@ void G_LeaveTeam( gentity_t *self ); void G_ChangeTeam( gentity_t *ent, pTeam_t newTeam ); void G_SanitiseName( char *in, char *out ); void G_PrivateMessage( gentity_t *ent ); +void Cmd_TeamVote_f( gentity_t *ent ); char *G_statsString( statsCounters_t *sc, pTeam_t *pt ); void Cmd_Share_f( gentity_t *ent ); void Cmd_Donate_f( gentity_t *ent ); @@ -1003,8 +1008,7 @@ void G_AddCreditToClient( gclient_t *client, short credit, qboolean cap ); team_t TeamCount( int ignoreClientNum, int team ); void SetClientViewAngle( gentity_t *ent, vec3_t angle ); gentity_t *SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t origin, vec3_t angles ); -gentity_t *SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles ); -void SpawnCorpse( gentity_t *ent ); +gentity_t *SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles );void SpawnCorpse( gentity_t *ent ); void respawn( gentity_t *ent ); void BeginIntermission( void ); void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles ); @@ -1332,6 +1336,11 @@ extern vmCvar_t g_multipleWeapons; extern vmCvar_t g_slapKnockback; extern vmCvar_t g_slapDamage; +extern vmCvar_t g_banNotice; + +extern vmCvar_t g_voteMinTime; +extern vmCvar_t g_mapvoteMaxTime; + void trap_Printf( const char *fmt ); void trap_Error( const char *fmt ); int trap_Milliseconds( void ); diff --git a/src/game/g_main.c b/src/game/g_main.c index 98776b8..28a6b61 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -183,6 +183,11 @@ vmCvar_t g_slapDamage; vmCvar_t g_buyAll; vmCvar_t g_multipleWeapons; +vmCvar_t g_banNotice; + +vmCvar_t g_voteMinTime; +vmCvar_t g_mapvoteMaxTime; + static cvarTable_t gameCvarTable[ ] = { // don't override the cheat state set by the system @@ -256,6 +261,8 @@ static cvarTable_t gameCvarTable[ ] = { &g_allowVote, "g_allowVote", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_voteLimit, "g_voteLimit", "5", CVAR_ARCHIVE, 0, qfalse }, + { &g_voteMinTime, "g_voteMinTime", "120", CVAR_ARCHIVE, 0, qfalse }, + { &g_mapvoteMaxTime, "g_mapvoteMaxTime", "240", CVAR_ARCHIVE, 0, qfalse }, { &g_suddenDeathVotePercent, "g_suddenDeathVotePercent", "75", CVAR_ARCHIVE, 0, qfalse }, { &g_pollVotes, "g_pollVotes", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_extendVotesPercent, "g_extendVotesPercent", "75", CVAR_ARCHIVE, 0, qfalse }, @@ -351,7 +358,8 @@ static cvarTable_t gameCvarTable[ ] = { &g_rankings, "g_rankings", "0", 0, 0, qfalse }, { &g_allowShare, "g_allowShare", "1", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse}, { &g_allowActions, "g_allowActions", "1", CVAR_ARCHIVE, 0, qfalse }, - { &g_actionPrefix, "g_actionPrefix", "***", CVAR_ARCHIVE, 0, qfalse } + { &g_actionPrefix, "g_actionPrefix", "***", CVAR_ARCHIVE, 0, qfalse }, + { &g_banNotice, "g_banNotice", "", CVAR_ARCHIVE, 0, qtrue }, }; static int gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[ 0 ] ); @@ -2116,6 +2124,7 @@ void CheckExitRules( void ) trap_SendServerCommand( -1, "print \"Timelimit hit\n\"" ); trap_SetConfigstring( CS_WINNER, "Stalemate" ); LogExit( "Timelimit hit." ); + G_admin_maplog_result( "t" ); return; } else if( level.time - level.startTime >= ( g_timelimit.integer - 5 ) * 60000 && @@ -2142,6 +2151,7 @@ void CheckExitRules( void ) trap_SendServerCommand( -1, "print \"Humans win\n\""); trap_SetConfigstring( CS_WINNER, "Humans Win" ); LogExit( "Humans win." ); + G_admin_maplog_result( "h" ); } else if( level.uncondAlienWin || ( ( level.time > level.startTime + 1000 ) && @@ -2153,6 +2163,7 @@ void CheckExitRules( void ) trap_SendServerCommand( -1, "print \"Aliens win\n\""); trap_SetConfigstring( CS_WINNER, "Aliens Win" ); LogExit( "Aliens win." ); + G_admin_maplog_result( "a" ); } } @@ -2181,6 +2192,15 @@ void CheckVote( void ) { level.voteExecuteTime = 0; + if( !Q_stricmp( level.voteString, "map_restart" ) ) + { + G_admin_maplog_result( "r" ); + } + else if( !Q_stricmpn( level.voteString, "map", 3 ) ) + { + G_admin_maplog_result( "m" ); + } + trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.voteString ) ); if( !Q_stricmp( level.voteString, "map_restart" ) || !Q_stricmpn( level.voteString, "map", 3 ) ) @@ -2253,7 +2273,16 @@ void CheckTeamVote( int team ) if( level.time - level.teamVoteTime[ cs_offset ] >= VOTE_TIME ) { - trap_SendServerCommand( -1, "print \"Team vote failed\n\"" ); + if( level.teamVoteYes[ cs_offset ] > level.teamVoteNo[ cs_offset ] && level.teamVoteYes[ cs_offset ] >= 2 ) + { + // execute the command, then remove the vote + trap_SendServerCommand( -1, "print \"Team vote passed\n\"" ); + trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.teamVoteString[ cs_offset ] ) ); + } + else + { + trap_SendServerCommand( -1, "print \"Team vote failed\n\"" ); + } } else { diff --git a/src/game/g_svcmds.c b/src/game/g_svcmds.c index dd5fbd3..1c403a1 100644 --- a/src/game/g_svcmds.c +++ b/src/game/g_svcmds.c @@ -700,6 +700,7 @@ qboolean ConsoleCommand( void ) level.lastWin = PTE_NONE; trap_SetConfigstring( CS_WINNER, "Evacuation" ); LogExit( "Evacuation." ); + G_admin_maplog_result( "d" ); return qtrue; } diff --git a/src/game/tremulous.h b/src/game/tremulous.h index 93da388..2ea98dd 100644 --- a/src/game/tremulous.h +++ b/src/game/tremulous.h @@ -195,7 +195,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define LEVEL4_VALUE AVM(800) #define LEVEL4_HEALTH AHM(400) #define LEVEL4_REGEN 7 -#define LEVEL4_COST 1 +#define LEVEL4_COST 2