From 1933b4fb90971c6945ec33b7745f967ea771b61d Mon Sep 17 00:00:00 2001 From: Paradox460 Date: Fri, 4 Apr 2008 02:59:00 +0000 Subject: [PATCH] 1.5.2 Release! ----- Added !info line endings fix Added chat channels Added console checking to !demo Added !flag series Added sprees Added new wiki pages chat and !flag, with documentation about their respective Features.wiki --- src/game/g_active.c | 6 + src/game/g_admin.c | 686 ++++++++++++++++++++++++++++++++++++++++---- src/game/g_admin.h | 14 + src/game/g_client.c | 3 + src/game/g_cmds.c | 298 ++++++++++++++++++- src/game/g_combat.c | 65 ++++- src/game/g_local.h | 9 + src/game/g_main.c | 17 +- 8 files changed, 1031 insertions(+), 67 deletions(-) diff --git a/src/game/g_active.c b/src/game/g_active.c index 4360330..ab2ca47 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -935,6 +935,12 @@ void ClientTimerActions( gentity_t *ent, int msec ) } } } + + // spree decay + if( ent->client->pers.statscounters.spreefeeds > 1 ) + ent->client->pers.statscounters.spreefeeds -= 2; + if( ent->client->pers.statscounters.spreekills > 1 ) + ent->client->pers.statscounters.spreekills -= 2; // turn off life support when a team admits defeat if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && diff --git a/src/game/g_admin.c b/src/game/g_admin.c index 8476448..159f0a0 100644 --- a/src/game/g_admin.c +++ b/src/game/g_admin.c @@ -104,7 +104,7 @@ g_admin_cmd_t g_admin_cmds[ ] = "[^3name|slot#^7] [^3#^7]" }, - {"demo", G_admin_demo, "W", + {"demo", G_admin_demo, "~", "turn admin chat off for the caller so it does not appear in demos. " "this is a toggle use !demo again to turn warnings back on", "" @@ -144,7 +144,12 @@ g_admin_cmd_t g_admin_cmds[ ] = "cause a player to explode. Caution: damages surrounding players and structures.", "[^3name|slot#^7]" }, - + + {"flag", G_admin_flag, "f", + "add an admin flag to a player, prefix flag with '-' to disallow", + "[^5slot#^7] [^3-^7][^3flag^7]" + }, + {"forcespec", G_admin_forcespec, "F", "disable joining of teams for a player", "[^3name|slot#^7]" @@ -281,6 +286,11 @@ g_admin_cmd_t g_admin_cmds[ ] = "(^5xnum^7) (^5#ID^7) (^5-name|num^7) (^5a|h^7)" }, + {"seen", G_admin_seen, "D", + "find the last time a player was on the server", + "[^3name|admin#^7]" + }, + {"setlevel", G_admin_setlevel, "s", "sets the admin level of a player", "[^3name|slot#|admin#^7] [^3level^7]" @@ -347,6 +357,11 @@ g_admin_cmd_t g_admin_cmds[ ] = "revoke designated builder privileges", "[^3name|slot#^7]" }, + + {"unflag", G_admin_flag, "f", + "clears an admin flag from a player", + "[^5slot#^7] [^3flag^7]" + }, {"unforcespec", G_admin_unforcespec, "F", "enable joining of teams for a player", @@ -784,6 +799,219 @@ static void admin_readconfig_int( char **cnf, int *v ) *v = atoi( t ); } +void G_admin_chat_writeconfig( void ) +{ + fileHandle_t f; + int len; + char keybuf[ 16 ]; + int i, j; + qboolean found; + + if( !g_chat.string[ 0 ] ) + { + G_Printf( "WARNING: g_chat is not set. " + " channel subscriptions will not be saved to a file.\n" ); + return; + } + len = trap_FS_FOpenFile( g_chat.string, &f, FS_WRITE ); + if( len < 0 ) + { + G_Printf( "chat_writeconfig: could not open g_chat file \"%s\"\n", + g_chat.string ); + return; + } + for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) + { + // don't write level 0 users + if( g_admin_admins[ i ]->level < 1 ) + continue; + + // don't write users not joined to a channel + found = qfalse; + for( j = 0; j < CHAT_MAXCHAN; j++ ) + { + if( g_admin_admins[ i ]->chat[ j ][ 0 ] ) + found = qtrue; + } + if( !found && !g_admin_admins[ i ]->seen ) + continue; + + trap_FS_Write( "[chat]\n", 7, f ); + trap_FS_Write( "guid = ", 10, f ); + admin_writeconfig_string( g_admin_admins[ i ]->guid, f ); + trap_FS_Write( "seen = ", 10, f ); + admin_writeconfig_int( g_admin_admins[ i ]->seen, f ); + + for( j = 0 ; j < CHAT_MAXCHAN; j++ ) + { + if( g_admin_admins[ i ]->chat[ j ][ 0 ] ) + { + Com_sprintf( keybuf, sizeof( keybuf ), "%d = ", j ); + trap_FS_Write( keybuf, 10, f ); + admin_writeconfig_string( g_admin_admins[ i ]->chat[ j ], f ); + } + } + + trap_FS_Write( "\n", 1, f ); + } + trap_FS_FCloseFile( f ); +} + +qboolean G_admin_chat_readconfig( gentity_t *ent ) +{ + g_admin_admin_t *a = NULL; + fileHandle_t f; + int len; + char *cnf, *cnf2; + char *t; + int uc = 0, cc = 0; + qboolean chat_open; + char guid[ 33 ]; + int i; + + if( !g_chat.string[ 0 ] ) + { + ADMP( "chat_readconfig: g_chat is not set, not loading channel subscriptions " + "from a file\n" ); + return qfalse; + } + + len = trap_FS_FOpenFile( g_chat.string, &f, FS_READ ) ; + if( len < 0 ) + { + ADMP( va( "chat_readconfig: could not open chat config file %s\n", + g_chat.string ) ); + return qfalse; + } + cnf = G_Alloc( len + 1 ); + cnf2 = cnf; + trap_FS_Read( cnf, len, f ); + *( cnf + len ) = '\0'; + trap_FS_FCloseFile( f ); + + t = COM_Parse( &cnf ); + chat_open = qfalse; + while( *t ) + { + if( !Q_stricmp( t, "[chat]" ) ) + { + chat_open = qtrue; + a = NULL; + } + else if( chat_open ) + { + int chan; + + if( !Q_stricmp( t, "guid" ) ) + { + admin_readconfig_string( &cnf, guid, sizeof( guid ) ); + for( i = 0 ; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) + { + if( !Q_stricmp( guid, g_admin_admins[ i ]->guid ) ) + { + a = g_admin_admins[ i ]; + memset( a->chat, 0, sizeof( a->chat) ); + uc++; + break; + } + } + } + else if( !Q_stricmp( t, "seen" ) ) + { + admin_readconfig_int( &cnf, &a->seen ); + } + else if( *t >= '0' && *t <= '9' ) + { + chan = atoi( t ); + if( chan >= 0 && chan < CHAT_MAXCHAN && a ) + { + admin_readconfig_string( &cnf, a->chat[ chan ], sizeof( a->chat[ chan ] ) ); + cc++; + } + } + else + { + ADMP( va( "chat_readconfig: [chat] parse error near %s on line %d\n", + t, COM_GetCurrentParseLine() ) ); + } + } + + t = COM_Parse( &cnf ); + } + + G_Free( cnf2 ); + ADMP( va( "chat_readconfig: loaded %d users with %d channels\n", uc, cc ) ); + return qtrue; +} + +void G_admin_chat_sync( gentity_t *ent ) +{ + gentity_t *target; + int i, j; + qboolean rejoin = qfalse; + + if( !ent || !ent->client || ent->client->pers.adminLevel < 1 ) + return; + + for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ ) + { + if( !Q_stricmp( ent->client->pers.guid, g_admin_admins[ i ]->guid ) ) + { + for( j = 0; j < CHAT_MAXCHAN; j++ ) + { + Q_strncpyz( ent->client->pers.chat[ j ], + g_admin_admins[ i ]->chat[ j ], + sizeof( g_admin_admins[ i ]->chat[ j ] ) ); + } + rejoin = qtrue; + break; + } + } + + if( !rejoin ) + return; + + for( j = 0; j < CHAT_MAXCHAN; j++ ) + { + if( !ent->client->pers.chat[ j ][ 0 ] ) + continue; + + for( i = 0; i < level.maxclients; i++ ) + { + target = &g_entities[ i ]; + if( target && target->client && + target->client->pers.connected == CON_CONNECTED && + !Q_stricmp( target->client->pers.chat[ j ], ent->client->pers.chat[ j ] ) ) + { + trap_SendServerCommand( i, va( "print \"join: %s^7 has rejoined channel #%d\n\"", + ent->client->pers.netname, j ) ); + } + } + } +} + +void G_admin_chat_update( gentity_t *ent, int chan ) +{ + int i; + + if( !ent || !ent->client || ent->client->pers.adminLevel < 1 ) + return; + + if( chan < 0 || chan > CHAT_MAXCHAN - 1 ) + return; + + for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ ) + { + if( !Q_stricmp( ent->client->pers.guid, g_admin_admins[ i ]->guid ) ) + { + Q_strncpyz( g_admin_admins[ i ]->chat[ chan ], + ent->client->pers.chat[ chan ], + sizeof( g_admin_admins[ i ]->chat[ chan ] ) ); + return; + } + } +} + // if we can't parse any levels from readconfig, set up default // ones to make new installs easier for admins static void admin_default_levels( void ) @@ -1372,6 +1600,8 @@ void G_admin_namelog_update( gclient_t *client, qboolean disconnect ) char n2[ MAX_NAME_LENGTH ]; int clientNum = ( client - level.clients ); + G_admin_seen_update( client->pers.guid ); + G_SanitiseName( client->pers.netname, n1 ); for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ ) { @@ -1637,6 +1867,7 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) *a->guid = '\0'; a->level = 0; *a->flags = '\0'; + a->seen = 0; admin_open = qtrue; } else if( !Q_stricmp( t, "[ban]" ) ) @@ -1695,6 +1926,9 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg ) admin_level_maxname = strlen( n ); } } + + G_admin_chat_readconfig( ent ); + return qtrue; } @@ -3604,6 +3838,10 @@ void G_admin_info_parse( char *unparsed, char *dest, int maxlength ) cursor++; //skip me } } + else if( unparsed[cursor] == '\r' ) + { + i--; // skip unprintable characters for this cycle + } else if( unparsed[cursor] == '$' ) { //read variable if( unparsed[1+cursor++] == '{' ) cursor++; // skip $ and { if there @@ -5748,6 +5986,126 @@ qboolean G_admin_L0(gentity_t *ent, int skiparg ){ return qtrue; } +qboolean G_admin_seen(gentity_t *ent, int skiparg ) +{ + char name[ MAX_NAME_LENGTH ]; + char search[ MAX_NAME_LENGTH ]; + char sduration[ 32 ]; + qboolean numeric = qtrue; + int i, j; + int id = -1; + int count = 0; + int t; + qtime_t qt; + gentity_t *vic; + qboolean ison; + + if( G_SayArgc() < 2 + skiparg ) + { + ADMP( "^3!seen: ^7usage: !seen [name|admin#]\n" ); + return qfalse; + } + G_SayArgv( 1 + skiparg, name, sizeof( name ) ); + G_SanitiseName( name, search ); + for( i = 0; i < sizeof( search ) && search[ i ] ; i++ ) + { + if( search[ i ] < '0' || search[ i ] > '9' ) + { + numeric = qfalse; + break; + } + } + + if( numeric ) + { + id = atoi( name ); + search[ 0 ] = '\0'; + } + + ADMBP_begin(); + t = trap_RealTime( &qt ); + + for( i = 0; i < level.maxclients && count < 10; i ++ ) + { + vic = &g_entities[ i ]; + + if( !vic->client || vic->client->pers.connected != CON_CONNECTED ) + continue; + + G_SanitiseName( vic->client->pers.netname, name ); + + if( i == id || (search[ 0 ] && strstr( name, search ) ) ) + { + ADMBP( va( "%4d %s^7 is currently playing\n", i, vic->client->pers.netname ) ); + count++; + } + } + for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] && count < 10; i++ ) + { + G_SanitiseName( g_admin_admins[ i ]->name, name ); + if( i + MAX_CLIENTS == id || (search[ 0 ] && strstr( name, search ) ) ) + { + ison = qfalse; + for( j = 0; j < level.maxclients; j++ ) + { + vic = &g_entities[ j ]; + if( !vic->client || vic->client->pers.connected != CON_CONNECTED ) + continue; + G_SanitiseName( vic->client->pers.netname, name ); + if( !Q_stricmp( vic->client->pers.guid, g_admin_admins[ i ]->guid ) + && strstr( name, search ) ) + { + ison = qtrue; + break; + } + } + + if( ison ) + { + if( id == -1 ) + continue; + ADMBP( va( "%4d %s^7 is currently playing\n", + i + MAX_CLIENTS, g_admin_admins[ i ]->name ) ); + } + else + { + G_admin_duration( t - g_admin_admins[ i ]->seen, + sduration, sizeof( sduration ) ); + ADMBP( va( "%4d %s^7 last seen %s%s\n", + i + MAX_CLIENTS, g_admin_admins[ i ]->name , + ( g_admin_admins[ i ]->seen ) ? sduration : "", + ( g_admin_admins[ i ]->seen ) ? " ago" : "time is unknown" ) ); + } + count++; + } + } + + if( search[ 0 ] ) + ADMBP( va( "^3!seen:^7 found %d player%s matching '%s'\n", + count, (count == 1) ? "" : "s", search ) ); + else if ( !count ) + ADMBP( "^3!seen:^7 no one connectd by that slot number\n" ); + + ADMBP_end(); + return qtrue; +} + +void G_admin_seen_update( char *guid ) +{ + int i; + + for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ ) + { + if( !Q_stricmp( g_admin_admins[ i ]->guid, guid ) ) + { + qtime_t qt; + + g_admin_admins[ i ]->seen = trap_RealTime( &qt ); + return; + } + } +} + void G_admin_adminlog_cleanup( void ) { int i; @@ -6278,31 +6636,125 @@ qboolean G_admin_listmaps( gentity_t *ent, int skiparg ) return qtrue; } -qboolean G_admin_immunity( gentity_t *ent, int skiparg ) +const char *G_admin_user_flag( int id, char flag, qboolean add, qboolean clear ) { - char command[ MAX_ADMIN_CMD_LEN ]; - char *cmd, *p, *action; - char guid[ 33 ]; - int id; - int i, j; - char c; - gentity_t *vic; - char adminname[ MAX_NAME_LENGTH ] = {""}; - char oldflags[ MAX_ADMIN_FLAGS ]; - char *newflags; - qboolean numeric = qtrue; + char add_flags[ MAX_ADMIN_FLAGS ]; + char sub_flags[ MAX_ADMIN_FLAGS ]; + char *flags; + int add_count = 0; + int sub_count = 0; + qboolean wildcard = qfalse; + qboolean allowed = qtrue; - if( G_SayArgc() < 2 + skiparg ) + if( id < 0 || id >= MAX_ADMIN_ADMINS + || g_admin_admins[ id ] == NULL ) { - ADMP( "^3!immunity: ^7usage: immunity [+|-]slot#\n" ); - return qfalse; + return "admin id out of range"; } - G_SayArgv( 1 + skiparg, command, sizeof( command ) ); - cmd = command; - action = command; - if( *cmd == '+' || *cmd == '-' ) cmd++; - p = cmd; + if( flag == '-' || flag == '+' || flag == '*') + { + return "invalid admin flag"; + } + + flags = g_admin_admins[ id ]->flags; + while( *flags ) + { + if( *flags == '*' ) + { + wildcard = qtrue; + allowed = qfalse; + } + else if( *flags == '-' ) + { + allowed = qfalse; + } + else if( *flags == '+' ) + { + allowed = qtrue; + } + else if( *flags != flag ) + { + if( allowed ) + { + if( add_count < MAX_ADMIN_FLAGS - 1 ) + { + add_flags[ add_count ] = *flags; + add_count++; + } + } + else + { + if( sub_count < MAX_ADMIN_FLAGS - 1 ) + { + sub_flags[ sub_count ] = *flags; + sub_count++; + } + } + } + + flags++; + } + + if( !clear ) + { + if( add ) + { + if( flag == '*' ) + { + wildcard = qtrue; + } + else if( add_count < MAX_ADMIN_FLAGS - 1 ) + { + add_flags[ add_count ] = flag; + add_count++; + } + } + else + { + if( flag == '*' ) + { + wildcard = qfalse; + } + else if( sub_count < MAX_ADMIN_FLAGS - 1 ) + { + sub_flags[ sub_count ] = flag; + sub_count++; + } + } + } + + add_flags[ add_count ] = '\0'; + sub_flags[ sub_count ] = '\0'; + + if( add_count + sub_count + ( (wildcard || sub_count) ? 1 : 0 ) + 1 > MAX_ADMIN_FLAGS ) + { + return "maximum admin flags exceeded"; + } + + Com_sprintf( g_admin_admins[ id ]->flags, sizeof(g_admin_admins[ id ]->flags), + "%s%s%s%s", + add_flags, + ( wildcard ) ? "*" : "", + ( sub_count && !wildcard ) ? "-" : "", + sub_flags ); + + return NULL; +} + +int G_admin_find_slot( gentity_t *ent, char *cmd, char *name, char *nick, int nick_len ) +{ + char guid[ 33 ]; + char *p; + int id; + int i; + qboolean numeric = qtrue; + gentity_t *vic; + + if ( nick ) + nick[ 0 ] = '\0'; + + p = name; while ( *p ) { if( *p < '0' || *p > '9' ) @@ -6312,26 +6764,26 @@ qboolean G_admin_immunity( gentity_t *ent, int skiparg ) } p++; } - if( !numeric || *cmd == '\0' ) + if( !numeric || name[ 0 ] == '\0' ) { - ADMP( "^3!immunity:^7 invalid slot number\n." ); - return qfalse; + ADMP( va( "^3!%s:^7 invalid slot number\n.", cmd ) ); + return -1; } - id = atoi( cmd ); - + guid[ 0 ] = '\0'; + id = atoi( name ); if( id >= 0 && id < level.maxclients ) { vic = &g_entities[ id ]; if( !vic || !(vic->client) || vic->client->pers.connected != CON_CONNECTED ) { - ADMP( "^3!immunity:^7 no one connectd by that slot number\n" ); + ADMP( va( "^3!%s:^7 no one connected by that slot number\n", cmd ) ); return qfalse; } Q_strncpyz( guid, vic->client->pers.guid, sizeof( guid ) ); - if( *guid == '\0' ) + if( *guid == 'X' ) { - ADMP( va( "^3!immunity:^7 player in slot %d has no GUID.\n", id ) ); + ADMP( va( "^3!%s:^7 player in slot %d has no GUID.\n", cmd, id ) ); return qfalse; } for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ ) @@ -6339,7 +6791,8 @@ qboolean G_admin_immunity( gentity_t *ent, int skiparg ) if( !Q_stricmp( guid, g_admin_admins[ i ]->guid ) ) { id = i + MAX_CLIENTS; - Q_strncpyz( adminname, vic->client->pers.netname, sizeof( adminname ) ); + if( nick ) + Q_strncpyz( nick, vic->client->pers.netname, nick_len ); break; } } @@ -6347,17 +6800,140 @@ qboolean G_admin_immunity( gentity_t *ent, int skiparg ) if( id < MAX_CLIENTS || id >= MAX_CLIENTS + MAX_ADMIN_ADMINS || g_admin_admins[ id - MAX_CLIENTS ] == NULL ) { - ADMP( "^3!immunity:^7 no match. use !listplayers or !listadmins to " - "find an appropriate slot # to use.\n" ); + if( *guid ) + ADMP( va( "^3!%s:^7 player is not !registered\n", cmd ) ); + else + ADMP( va( "^3!%s:^7 no match. use !listplayers or !listadmins to " + "find an appropriate slot # to use.\n", cmd ) ); + return -1; + } + id -= MAX_CLIENTS; + + if ( nick && !nick[ 0 ] ) + { + Q_strncpyz( nick, g_admin_admins[ id ]->name, nick_len ); + } + + return id; +} + +qboolean G_admin_flag( gentity_t *ent, int skiparg ) +{ + char command[ MAX_ADMIN_CMD_LEN ], *cmd; + char name[ MAX_NAME_LENGTH ]; + char flagbuf[ 4 ], *flag; + int id; + char adminname[ MAX_NAME_LENGTH ] = {""}; + const char *result; + qboolean add = qtrue; + qboolean clear = qfalse; + + G_SayArgv( skiparg, command, sizeof( command ) ); + cmd = command; + if( *cmd == '!' ) + cmd++; + + if( G_SayArgc() < 2 + skiparg ) + { + ADMP( va( "^3!%s: ^7usage: !%s slot# flag\n", cmd, cmd ) ); return qfalse; } - id -= MAX_CLIENTS; - if ( !(*adminname) ) - { - Q_strncpyz( adminname, g_admin_admins[ id ]->name, sizeof( adminname ) ); + G_SayArgv( 1 + skiparg, name, sizeof( name ) ); + id = G_admin_find_slot( ent, cmd, name, adminname, sizeof( adminname ) ); + if( id < 0 ) + return qfalse; + if( ent && !admin_higher_guid( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) + { + ADMP( va( "^3%s:^7 your intended victim has a higher admin level then you\n", cmd ) ); + return qfalse; } + if( G_SayArgc() < 3 + skiparg ) + { + ADMP( va( "^3%s:^7 flags for %s^7 are '^3%s^7'\n", + cmd, adminname, g_admin_admins[ id ]->flags ) ); + return qtrue; + } + + G_SayArgv( 2 + skiparg, flagbuf, sizeof( flagbuf ) ); + flag = flagbuf; + if( flag[ 0 ] == '-' && flag[ 1 ] != '\0' ) + { + add = qfalse; + flag++; + } + if( ent && !Q_stricmp( ent->client->pers.guid, g_admin_admins[ id ]->guid ) ) + { + ADMP( va( "^3%s:^7 you may not change your own flags (use rcon)\n", cmd ) ); + return qfalse; + } + if( !G_admin_permission( ent, flag[ 0 ] ) ) + { + ADMP( va( "^3%s:^7 you can only change flags that you also have\n", cmd ) ); + return qfalse; + } + + if( !Q_stricmp( cmd, "unflag" ) ) + { + clear = qtrue; + } + + result = G_admin_user_flag( id, flag[ 0 ], add, clear ); + if( result ) + { + ADMP( va( "^3!immunity: ^7an error occured setting flag '^3%c^7', %s\n", + flag[ 0 ], result ) ); + return qfalse; + } + + if( !Q_stricmp( cmd, "flag" ) ) + { + AP( va( + "print \"^3!%s: ^7%s^7 was %s admin flag '%c' by %s\n\"", + cmd, adminname, + ( add ) ? "given" : "denied", + flag[ 0 ], + ( ent ) ? ent->client->pers.netname : "console" ) ); + } + else + { + AP( va( + "print \"^3!%s: ^7admin flag '%c' for %s^7 cleared by %s\n\"", + cmd, flag[ 0 ], adminname, + ( ent ) ? ent->client->pers.netname : "console" ) ); + } + + if( !g_admin.string[ 0 ] ) + ADMP( va( "^3!%s: ^7WARNING g_admin not set, not saving admin record " + "to a file\n", cmd ) ); + else + admin_writeconfig(); + + return qtrue; +} + +qboolean G_admin_immunity( gentity_t *ent, int skiparg ) +{ + char command[ MAX_ADMIN_CMD_LEN ]; + char *cmd, *action; + int id; + char adminname[ MAX_NAME_LENGTH ] = {""}; + const char *result; + + if( G_SayArgc() < 2 + skiparg ) + { + ADMP( "^3!immunity: ^7usage: !immunity [+|-]slot#\n" ); + return qfalse; + } + G_SayArgv( 1 + skiparg, command, sizeof( command ) ); + cmd = command; + action = command; + if( *cmd == '+' || *cmd == '-' ) cmd++; + + id = G_admin_find_slot( ent, "immunity", cmd, adminname, sizeof( adminname ) ); + if( id < 0 ) + return qfalse; if( *action != '+' && *action != '-' ) { @@ -6367,32 +6943,12 @@ qboolean G_admin_immunity( gentity_t *ent, int skiparg ) return qfalse; } - Q_strncpyz( oldflags, g_admin_admins[ id ]->flags, sizeof( oldflags ) ); - newflags = g_admin_admins[ id ]->flags; - i = j = 0; - while( i < MAX_ADMIN_FLAGS - 1 && oldflags[ i ] ) + result = G_admin_user_flag( id, ADMF_BANIMMUNITY, qtrue, ( *action != '+' ) ); + if( result ) { - c = oldflags[ i ]; - if( c != ADMF_BANIMMUNITY ) - { - newflags[ j ] = c; - j++; - } - i++; + ADMP( va( "^3!immunity: ^7an error occured setting flag, %s\n", result ) ); + return qfalse; } - if( *action == '+' ) - { - if( j < MAX_ADMIN_FLAGS - 1 ) - { - newflags[ j ] = ADMF_BANIMMUNITY; - j++; - } - else - { - ADMP( "^3!immunity:^7 flag count exceeded, no actual ban immunity.\n" ); - } - } - newflags[ j ] = '\0'; if( *action == '+' ) { @@ -6408,7 +6964,7 @@ qboolean G_admin_immunity( gentity_t *ent, int skiparg ) } if( !g_admin.string[ 0 ] ) - ADMP( "^3!setlevel: ^7WARNING g_admin not set, not saving admin record " + ADMP( "^3!immunity: ^7WARNING g_admin not set, not saving admin record " "to a file\n" ); else admin_writeconfig(); @@ -6486,6 +7042,12 @@ qboolean G_admin_suspendban( gentity_t *ent, int skiparg ) qboolean G_admin_demo( gentity_t *ent, int skiparg ) { + if( !ent ) + { + ADMP( "!demo: console can not use demo.\n"); + return qfalse; + } + ent->client->pers.ignoreAdminWarnings = !( ent->client->pers.ignoreAdminWarnings ); ADMP( va( "^3!demo: ^7your visibility of admin chat is now %s\n", diff --git a/src/game/g_admin.h b/src/game/g_admin.h index b1c4489..f100114 100644 --- a/src/game/g_admin.h +++ b/src/game/g_admin.h @@ -45,6 +45,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define MAX_ADMIN_BANSUSPEND_DAYS 14 +#define CHAT_MAXCHAN 10 +#define CHAT_MAXPASS 12 + /* * 1 - cannot be vote kicked, vote muted * 2 - cannot be censored or flood protected TODO @@ -114,6 +117,8 @@ typedef struct g_admin_admin char name[ MAX_NAME_LENGTH ]; int level; char flags[ MAX_ADMIN_FLAGS ]; + int seen; + char chat[ CHAT_MAXCHAN ][ CHAT_MAXPASS ]; } g_admin_admin_t; @@ -214,7 +219,10 @@ qboolean G_admin_buildlog( gentity_t *ent, int skiparg ); qboolean G_admin_revert( gentity_t *ent, int skiparg ); qboolean G_admin_slap( gentity_t *ent, int skiparg ); qboolean G_admin_L0( gentity_t *ent, int skiparg ); +qboolean G_admin_seen(gentity_t *ent, int skiparg ); +void G_admin_seen_update( char *guid ); qboolean G_admin_listmaps( gentity_t *ent, int skiparg ); +qboolean G_admin_flag( gentity_t *ent, int skiparg ); qboolean G_admin_immunity( gentity_t *ent, int skiparg ); qboolean G_admin_suspendban( gentity_t *ent, int skiparg ); qboolean G_admin_demo( gentity_t *ent, int skiparg ); @@ -224,6 +232,12 @@ void G_admin_adminlog_log( gentity_t *ent, char *command, char *args, int skipar qboolean G_admin_maplog( gentity_t *ent, int skiparg ); void G_admin_maplog_update( void ); void G_admin_maplog_result( char *flag ); + +void G_admin_chat_writeconfig( void ); +qboolean G_admin_chat_readconfig( gentity_t *ent ); +void G_admin_chat_sync( gentity_t *ent ); +void G_admin_chat_update( gentity_t *ent, int chan ); + //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 a8a8359..6ea2b07 100644 --- a/src/game/g_client.c +++ b/src/game/g_client.c @@ -1532,6 +1532,9 @@ void ClientBegin( int clientNum ) // name can change between ClientConnect() and ClientBegin() G_admin_namelog_update( client, qfalse ); + // rejoin any saved chat channels + G_admin_chat_sync( ent ); + // request the clients PTR code trap_SendServerCommand( ent - g_entities, "ptrcrequest" ); diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index ccb6b54..a5f6703 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -1375,6 +1375,223 @@ static void Cmd_Tell_f( gentity_t *ent ) G_Say( ent, ent, SAY_TELL, p ); } +void Cmd_Join_f( gentity_t *ent ) +{ + char pass[CHAT_MAXPASS]; + char arg[MAX_TOKEN_CHARS]; + int chan; + int i; + gentity_t *target; + + if( g_floodMinTime.integer && + G_Flood_Limited( ent ) ) + { + trap_SendServerCommand( ent-g_entities, + "print \"Your chat is flood-limited; wait before chatting again\n\"" ); + return; + } + + if( trap_Argc( ) < 2 ) + { + char message[ 64 ]; + int n; + + n = 0; + for( i = 0; i < CHAT_MAXCHAN && n < 64 - 3; i++ ) + { + if( ent->client->pers.chat[i][0] ) + { + message[ n ] = ' '; n++; + message[ n ] = '0' + i; n++; + } + } + if( n == 0) + Com_sprintf( message, sizeof( message ), " no channels, use: /join [0-%d] (password)", CHAT_MAXCHAN - 1 ); + else + message[ n ] = '\0'; + + trap_SendServerCommand( ent-g_entities, va( "print \"joined in:%s\n\"", message ) ); + return; + } + + trap_Argv( 1, arg, sizeof( arg ) ); + chan = atoi( arg ); + if( chan < 0 || chan >= CHAT_MAXCHAN ) + { + trap_SendServerCommand( ent-g_entities, + va( "print \"^3/join^7: invalid channel, usage: /join [0-%d] (password)\n\"", CHAT_MAXCHAN - 1 ) ); + return; + } + + trap_Argv( 2, pass, sizeof( pass ) ); + if( pass[0] == '\0' ) + Q_strncpyz( pass, "default", sizeof( pass ) ); + + if( !Q_stricmp( ent->client->pers.chat[chan], pass ) ) + { + trap_SendServerCommand( ent-g_entities, + va( "print \"^3/join^7: already join to channel %d\n\"", chan ) ); + return; + } + + G_LogPrintf( "join: channel %d %s^7\n", chan, ent->client->pers.netname ); + Q_strncpyz( ent->client->pers.chat[chan], pass, sizeof( ent->client->pers.chat[chan] ) ); + G_admin_chat_update( ent, chan ); + + for( i = 0; i < level.maxclients; i++ ) + { + target = &g_entities[ i ]; + if( target && target->client && + target->client->pers.connected == CON_CONNECTED && + !Q_stricmp( target->client->pers.chat[ chan ], pass ) ) + { + trap_SendServerCommand( i, va( "print \"join: %s^7 has joined channel #%d\n\"", + ent->client->pers.netname, chan ) ); + } + } +} + +void Cmd_Part_f( gentity_t *ent ) +{ + char arg[MAX_TOKEN_CHARS]; + int chan; + int i; + gentity_t *target; + + if( trap_Argc( ) < 2 ) + { + trap_SendServerCommand( ent-g_entities, va( "print \"^3/part^7 usage: /part [0-%d]\n\"", CHAT_MAXCHAN - 1 ) ); + return; + } + + if( g_floodMinTime.integer && + G_Flood_Limited( ent ) ) + { + trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" ); + return; + } + + trap_Argv( 1, arg, sizeof( arg ) ); + chan = atoi( arg ); + if( chan < 0 || chan >= CHAT_MAXCHAN ) + { + trap_SendServerCommand( ent-g_entities, + va( "print \"^3/part^7: invalid channel, available channels are 0 to %d\n\"", CHAT_MAXCHAN - 1 ) ); + return; + } + + if( ent->client->pers.chat[chan][0] == '\0' ) + { + trap_SendServerCommand( ent-g_entities, + "print \"^3/part^7: not in that channel\n\"" ); + return; + } + + for( i = 0; i < level.maxclients; i++ ) + { + target = &g_entities[ i ]; + if( target && target->client && + target->client->pers.connected == CON_CONNECTED && + !Q_stricmp( target->client->pers.chat[ chan ], ent->client->pers.chat[ chan ] ) ) + { + trap_SendServerCommand( i, va( "print \"part: %s^7 has left channel #%d\n\"", + ent->client->pers.netname, chan ) ); + } + } + + G_LogPrintf( "part: channel %d %s^7\n", chan, ent->client->pers.netname ); + Q_strncpyz( ent->client->pers.chat[chan], "", sizeof( ent->client->pers.chat[chan] ) ); + G_admin_chat_update( ent, chan ); +} + +void Cmd_Channel_f( gentity_t *ent ) +{ + char arg[MAX_TOKEN_CHARS]; + char str[MAX_STRING_CHARS]; + char *p; + char location[ 64 ]; + char escaped[ 64 ]; + int chan; + int i; + int num = 0; + qboolean who = qfalse; + gentity_t *target; + + if( g_floodMinTime.integer && + G_Flood_Limited( ent ) ) + { + trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" ); + return; + } + + trap_Argv( 0, arg, sizeof( arg ) ); + chan = atoi( arg ); + if( chan < 0 || chan >= CHAT_MAXCHAN || + ent->client->pers.chat[chan][0] == '\0' ) + { + trap_SendServerCommand( ent-g_entities, + va( "print \"^3/%s^7: you are not joined to that channel, use /join\n\"", arg ) ); + return; + } + + p = ConcatArgs( 1 ); + if( !p || p[0] == '\0' ) + who = qtrue; + + if( Team_GetLocationMsg( ent, location, sizeof( location ) ) ) + { + Com_sprintf( escaped, sizeof( escaped ), " (^4%s^7)", location ); + } + else + { + escaped[ 0 ] = '\0'; + } + + str[ 0 ] = '\0'; + for( i = 0; i < level.maxclients; i++ ) + { + target = &g_entities[ i ]; + if( target && target->client && + target->client->pers.connected == CON_CONNECTED && + !Q_stricmp( target->client->pers.chat[ chan ], ent->client->pers.chat[ chan ] ) ) + { + if( num > 0 ) + Q_strcat( str, sizeof( str ), "^7, " ); + Q_strcat( str, sizeof( str ), target->client->pers.netname ); + num++; + } + } + + if( who ) + { + trap_SendServerCommand( ent-g_entities, + va( "print \"channel ^3%d^7 has %s%s and contains %d player%s%s%s\n\"", + chan, + ( !Q_stricmp( ent->client->pers.chat[ chan ], "default" ) ) ? "no password" : "password ", + ( !Q_stricmp( ent->client->pers.chat[ chan ], "default" ) ) ? "" : ent->client->pers.chat[ chan ], + num, + ( num == 1 ) ? "" : "s", + ( num ) ? ": " : "", + str ) ); + return; + } + + G_LogPrintf( "channel%d: %s^7 %s\n", chan, ent->client->pers.netname, p ); + for( i = 0; i < level.maxclients; i++ ) + { + target = &g_entities[ i ]; + if( target && target->client && + target->client->pers.connected == CON_CONNECTED && + !Q_stricmp( target->client->pers.chat[ chan ], ent->client->pers.chat[ chan ] ) ) + { + trap_SendServerCommand( i, va( "print \"(^1#%d^7 %d) [%s^7]%s: ^1%s^7\n\"", + chan, num, ent->client->pers.netname, + ( OnSameTeam( target, ent ) ) ? escaped : "", + p ) ); + } + } +} + /* ================== Cmd_Where_f @@ -2650,7 +2867,39 @@ void Cmd_Destroy_f( gentity_t *ent ) BG_FindHumanNameForBuildable( traceEnt->s.modelindex ) ) ); return; } - + + // Prevent destruction of critical buildables (per g_deconTime) + if( g_deconTime.integer && + ( traceEnt->s.modelindex == BA_A_OVERMIND || traceEnt->s.modelindex == BA_H_REACTOR ) ) + { + if( g_deconTime.integer > 0 && + level.time - level.startTime < g_deconTime.integer * 1000 ) + { + trap_SendServerCommand( ent-g_entities, + va( "print \"%s can't be moved until %d:%02d into the game\n\"", + BG_FindHumanNameForBuildable( traceEnt->s.modelindex ), + g_deconTime.integer / 60, g_deconTime.integer % 60 ) ); + return; + } + if( g_deconTime.integer < 0 && + level.time - level.startTime < 60 * 1000 && level.numConnectedClients ) + { + int numTeammates; + + if( ent->client->pers.teamSelection == PTE_HUMANS ) + numTeammates = level.numHumanClients; + else + numTeammates = level.numAlienClients; + + if( numTeammates * 100 / level.numConnectedClients < 33 ) + { + trap_SendServerCommand( ent-g_entities, + va( "print \"%s can't be moved until more players join, or after 1:00\n\"", + BG_FindHumanNameForBuildable( traceEnt->s.modelindex ) ) ); + return; + } + } + } // Prevent destruction of the last spawn if( g_markDeconstruct.integer != 1 ) @@ -2812,6 +3061,39 @@ void Cmd_Mark_f( gentity_t *ent ) return; } + // Prevent marking of critical buildables (per g_deconTime) + if( g_deconTime.integer && + ( traceEnt->s.modelindex == BA_A_OVERMIND || traceEnt->s.modelindex == BA_H_REACTOR ) ) + { + if( g_deconTime.integer > 0 && + level.time - level.startTime < g_deconTime.integer * 1000 ) + { + trap_SendServerCommand( ent-g_entities, + va( "print \"%s can't be moved until %d:%02d into the game\n\"", + BG_FindHumanNameForBuildable( traceEnt->s.modelindex ), + g_deconTime.integer / 60, g_deconTime.integer % 60 ) ); + return; + } + if( g_deconTime.integer < 0 && + level.time - level.startTime < 60 * 1000 && level.numConnectedClients ) + { + int numTeammates; + + if( ent->client->pers.teamSelection == PTE_HUMANS ) + numTeammates = level.numHumanClients; + else + numTeammates = level.numAlienClients; + + if( numTeammates * 100 / level.numConnectedClients < 33 ) + { + trap_SendServerCommand( ent-g_entities, + va( "print \"%s can't be moved until more players join, or after 1:00\n\"", + BG_FindHumanNameForBuildable( traceEnt->s.modelindex ) ) ); + return; + } + } + } + // Don't allow marking of buildables that cannot be rebuilt if(g_suddenDeath.integer && traceEnt->health > 0 && ( ( g_suddenDeathMode.integer == SDMODE_SELECTIVE && @@ -4366,6 +4648,20 @@ commands_t cmds[ ] = { { "me", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Say_f }, { "me_team", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Say_f }, + // channel chat + { "join", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Join_f }, + { "part", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Part_f }, + { "0", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, + { "1", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, + { "2", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, + { "3", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, + { "4", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, + { "5", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, + { "6", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, + { "7", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, + { "8", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, + { "9", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Channel_f }, + { "score", CMD_INTERMISSION, ScoreboardMessage }, { "mystats", CMD_TEAM|CMD_INTERMISSION, Cmd_MyStats_f }, diff --git a/src/game/g_combat.c b/src/game/g_combat.c index 4bd0918..b922fd0 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -140,6 +140,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int float percentDamage = 0.0f; gentity_t *player; qboolean tk = qfalse; + int spreeRate = 0; if( self->client->ps.pm_type == PM_DEAD ) @@ -207,6 +208,16 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int //TA: deactivate all upgrades for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ ) BG_DeactivateUpgrade( i, self->client->ps.stats ); + + // killing spree over + if( self->client->pers.statscounters.spreekills == -1 ) + { + spreeRate = 2; + trap_SendServerCommand( -1, + va( "print \"%s^7's killing spree has come to an end\n\"", + self->client->pers.netname ) ); + } + self->client->pers.statscounters.spreekills = 0; if( meansOfDeath == MOD_SLAP ) { @@ -314,6 +325,19 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int { level.humanStatsCounters.kills++; } + + if( g_killingSpree.integer > 2 ) + { + if( attacker->client->pers.statscounters.spreekills >= 0 ) + attacker->client->pers.statscounters.spreekills += 60; + if( attacker->client->pers.statscounters.spreekills > ( g_killingSpree.integer - 1 ) * 60 ) + { + attacker->client->pers.statscounters.spreekills = -1; + trap_SendServerCommand( -1, + va( "print \"%s^3 is on a killing spree! killer gets a double reward bonus\n\"", + attacker->client->pers.netname ) ); + } + } } if( attacker == self ) @@ -456,6 +480,13 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int { self->client->pers.statscounters.feeds++; level.humanStatsCounters.feeds++; + + if( g_feedingSpree.integer && + level.reactorPresent && + !G_BuildableRange( self->client->ps.origin, 900, BA_H_REACTOR ) ) + { + self->client->pers.statscounters.spreefeeds += 60; + } } } else if( self->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) @@ -465,6 +496,13 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int { self->client->pers.statscounters.feeds++; level.alienStatsCounters.feeds++; + + if( g_feedingSpree.integer && + level.overmindPresent && + !G_BuildableRange( self->client->ps.origin, 900, BA_A_OVERMIND ) ) + { + self->client->pers.statscounters.spreefeeds += 60; + } } } } @@ -497,6 +535,9 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int } + if( spreeRate && player == attacker ) + percentDamage *= (float)spreeRate; + //add credit G_AddCreditToClient( player->client, (int)( classValue * percentDamage ), qtrue ); @@ -539,7 +580,10 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int if( frags > 0 ) { //add kills - G_AddCreditToClient( player->client, frags, qtrue ); + if( spreeRate && player == attacker ) + G_AddCreditToClient( player->client, frags * spreeRate, qtrue ); + else + G_AddCreditToClient( player->client, frags, qtrue ); //can't revist this account later self->credits[ i ] = 0; @@ -580,7 +624,10 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int player = g_entities + topClient; //add kills - G_AddCreditToClient( player->client, 1, qtrue ); + if( spreeRate && player == attacker ) + G_AddCreditToClient( player->client, spreeRate, qtrue ); + else + G_AddCreditToClient( player->client, 1, qtrue ); //can't revist this account again self->credits[ topClient ] = 0; @@ -633,6 +680,20 @@ finish_dying: // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 1700; + if( g_feedingSpree.integer > 2 ) + { + int maxfeed; + + maxfeed = (g_feedingSpree.integer - 1) * 60; + if( self->client->pers.statscounters.spreefeeds > maxfeed ) + { + self->client->respawnTime += 100 * (self->client->pers.statscounters.spreefeeds - maxfeed ); + trap_SendServerCommand( self->client->ps.clientNum, + va( "print \"You are on a feeding spree! respawn delayed %d seconds\n\"", + (self->client->respawnTime - level.time) / 1000 ) ); + } + } + // remove powerups memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) ); diff --git a/src/game/g_local.h b/src/game/g_local.h index 3548540..ffb05d8 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -337,6 +337,8 @@ typedef struct int headshots; int hits; int hitslocational; + int spreekills; + int spreefeeds; int teamkills; int dretchbasytime; int jetpackusewallwalkusetime; @@ -394,6 +396,7 @@ typedef struct statsCounters_t statscounters; qboolean cusgrav; int cusgravlvl; + char chat[ CHAT_MAXCHAN ][ CHAT_MAXPASS ]; qboolean ignoreAdminWarnings; qboolean firstConnect; @@ -1337,6 +1340,7 @@ extern vmCvar_t g_adminRegisterAdminPass; extern vmCvar_t g_adminRegisterAdminLevel; extern vmCvar_t g_minLevelToJoinTeam; extern vmCvar_t g_minLevelToSpecMM1; +extern vmCvar_t g_chat; extern vmCvar_t g_KillerHP; @@ -1348,6 +1352,9 @@ extern vmCvar_t g_publicSayadmins; extern vmCvar_t g_myStats; extern vmCvar_t g_antiSpawnBlock; +extern vmCvar_t g_killingSpree; +extern vmCvar_t g_feedingSpree; + extern vmCvar_t g_sayAreaLocations; extern vmCvar_t g_dretchPunt; @@ -1377,6 +1384,8 @@ extern vmCvar_t g_banNotice; extern vmCvar_t g_voteMinTime; extern vmCvar_t g_mapvoteMaxTime; +extern vmCvar_t g_deconTime; + extern vmCvar_t g_specAspec; extern vmCvar_t g_modBuildableHealth; // Buildable health diff --git a/src/game/g_main.c b/src/game/g_main.c index 474090f..4ac34ef 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -159,6 +159,7 @@ vmCvar_t g_adminRegisterLevel; vmCvar_t g_adminRegisterAdminPass; vmCvar_t g_adminRegisterAdminLevel; vmCvar_t g_minLevelToJoinTeam; +vmCvar_t g_chat; vmCvar_t g_privateMessages; vmCvar_t g_buildLogMaxLength; @@ -167,6 +168,9 @@ vmCvar_t g_publicSayadmins; vmCvar_t g_myStats; vmCvar_t g_antiSpawnBlock; +vmCvar_t g_killingSpree; +vmCvar_t g_feedingSpree; + vmCvar_t g_KillerHP; vmCvar_t g_decolorLogfiles; @@ -206,6 +210,8 @@ vmCvar_t g_banNotice; vmCvar_t g_voteMinTime; vmCvar_t g_mapvoteMaxTime; +vmCvar_t g_deconTime; + vmCvar_t g_specAspec; vmCvar_t g_modBuildableHealth; @@ -386,19 +392,24 @@ static cvarTable_t gameCvarTable[ ] = { &g_adminRegisterAdminPass, "g_adminRegisterAdminPass", "", CVAR_ARCHIVE, 0, qfalse }, { &g_adminRegisterAdminLevel, "g_adminRegisterAdminLevel", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_privateMessages, "g_privateMessages", "1", CVAR_ARCHIVE, 0, qfalse }, + { &g_chat, "g_chat", "chat.dat", CVAR_ARCHIVE, 0, qfalse }, { &g_radiationDamage, "g_radiationDamage", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_radiationTime, "g_radiationTime", "60", CVAR_ARCHIVE, 0, qfalse }, { &g_radiationCredits, "g_radiationCredits", "1000", CVAR_ARCHIVE, 0, qfalse }, { &g_radiationCreditsCount, "g_radiationCreditsCount", "2", CVAR_ARCHIVE, 0, qfalse }, - - + + { &g_deconTime, "g_deconTime", "0", CVAR_ARCHIVE, 0, qfalse }, + { &g_buildLogMaxLength, "g_buildLogMaxLength", "25", CVAR_ARCHIVE, 0, qfalse }, { &g_myStats, "g_myStats", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_publicSayadmins, "g_publicSayadmins", "1", CVAR_ARCHIVE, 0, qfalse }, { &g_minLevelToSpecMM1, "g_minLevelToSpecMM1", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_antiSpawnBlock, "g_antiSpawnBlock", "0", CVAR_ARCHIVE, 0, qfalse }, + + { &g_killingSpree, "g_killingSpree", "0", CVAR_ARCHIVE, 0, qfalse }, + { &g_feedingSpree, "g_feedingSpree", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_KillerHP, "g_KillerHP", "0", CVAR_ARCHIVE, 0, qtrue }, @@ -885,6 +896,8 @@ void G_ShutdownGame( int restart ) // write all the client session data so we can get it back G_WriteSessionData( ); + G_admin_chat_writeconfig(); + G_admin_cleanup( ); G_admin_namelog_cleanup( ); G_admin_adminlog_cleanup( );