summaryrefslogtreecommitdiff
path: root/src/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/game')
-rw-r--r--src/game/bg_lib.c8
-rw-r--r--src/game/bg_misc.c134
-rw-r--r--src/game/bg_pmove.c10
-rw-r--r--src/game/bg_public.h21
-rw-r--r--src/game/g_active.c201
-rw-r--r--src/game/g_admin.c2907
-rw-r--r--src/game/g_admin.h60
-rw-r--r--src/game/g_buildable.c1038
-rw-r--r--src/game/g_client.c144
-rw-r--r--src/game/g_cmds.c1040
-rw-r--r--src/game/g_combat.c238
-rw-r--r--src/game/g_local.h156
-rw-r--r--src/game/g_main.c708
-rw-r--r--src/game/g_maprotation.c171
-rw-r--r--src/game/g_mem.c2
-rw-r--r--src/game/g_missile.c31
-rw-r--r--src/game/g_session.c2
-rw-r--r--src/game/g_weapon.c17
-rw-r--r--src/game/tremulous.h3
19 files changed, 4958 insertions, 1933 deletions
diff --git a/src/game/bg_lib.c b/src/game/bg_lib.c
index 69bca48..b9c8762 100644
--- a/src/game/bg_lib.c
+++ b/src/game/bg_lib.c
@@ -67,7 +67,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93";
#endif
static const char rcsid[] =
- "$Id: bg_lib.c 965 2007-08-09 13:54:12Z msk $";
+ "$Id: bg_lib.c,v 1.1.1.1.4.1 2009/02/17 02:38:26 johne Exp $";
#endif /* LIBC_SCCS and not lint */
// bk001127 - needed for DLL's
@@ -1663,7 +1663,6 @@ unsigned int _hextoi( const char **stringPtr )
#define SHORTINT 0x00000040 /* short integer */
#define ZEROPAD 0x00000080 /* zero (as opposed to blank) pad */
#define FPT 0x00000100 /* floating point number */
-#define UNSIGNED 0x00000200 /* unsigned integer */
#define to_digit(c) ((c) - '0')
#define is_digit(c) ((unsigned)to_digit(c) <= 9)
@@ -1677,9 +1676,6 @@ void AddInt( char **buf_p, int val, int width, int flags )
digits = 0;
- if( flags & UNSIGNED )
- val = (unsigned) val;
-
if( flags & HEX )
{
char c;
@@ -1944,8 +1940,6 @@ reswitch:
arg++;
break;
- case 'u':
- flags |= UNSIGNED;
case 'd':
case 'i':
AddInt( &buf_p, *arg, width, flags );
diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c
index 79a8e2a..1a55ed0 100644
--- a/src/game/bg_misc.c
+++ b/src/game/bg_misc.c
@@ -32,6 +32,8 @@ void trap_FS_Write( const void *buffer, int len, fileHandle_t f );
void trap_FS_FCloseFile( fileHandle_t f );
void trap_FS_Seek( fileHandle_t f, long offset, fsOrigin_t origin ); // fsOrigin_t
+modExtremeType_t modEntry[ MOD_BG_COUNT ];
+
buildableAttributes_t bg_buildableList[ ] =
{
{
@@ -5730,4 +5732,136 @@ void BG_ClientListParse( clientList_t *list, const char *s )
sscanf( s, "%x%x", &list->hi, &list->lo );
}
+void BG_MOD_set( modExtremeType_t entry, int value )
+{
+ if( entry >= 0 && entry < MOD_BG_COUNT )
+ {
+ if( value < 0 )
+ value = 0;
+
+ modEntry[ entry ] = value;
+ }
+}
+
+int BG_MOD_get( modExtremeType_t entry)
+{
+ if( entry >= 0 && entry < MOD_BG_COUNT )
+ return modEntry[ entry ];
+
+ return 0;
+}
+
+void BG_MOD_update( void )
+{
+ static qboolean updated = qfalse;
+ int i;
+
+ if( updated ) return;
+ updated = qtrue;
+
+ for( i = 0; i < bg_numBuildables; i++ )
+ {
+ if( modEntry[ MOD_BG_BUILDABLE_HEALTH ] )
+ {
+ bg_buildableList[ i ].health =
+ bg_buildableList[ i ].health * modEntry[ MOD_BG_BUILDABLE_HEALTH ] / 100;
+ }
+
+ if( modEntry[ MOD_BG_BUILDABLE_SPEED ] )
+ {
+ bg_buildableList[ i ].turretFireSpeed =
+ bg_buildableList[ i ].turretFireSpeed * 100 / modEntry[ MOD_BG_BUILDABLE_SPEED ];
+ }
+
+ if( modEntry[ MOD_BG_TURRET_ANGLE ] &&
+ ( bg_buildableList[ i ].buildNum == BA_H_MGTURRET ||
+ bg_buildableList[ i ].buildNum == BA_H_TESLAGEN ) )
+ {
+ bg_buildableList[ i ].minNormal = 0.0f;
+ }
+ }
+
+ for( i = 0; i < bg_numPclasses; i++ )
+ {
+ if( bg_classList[ i ].classNum == PCL_HUMAN ||
+ bg_classList[ i ].classNum == PCL_HUMAN_BSUIT )
+ {
+ if( modEntry[ MOD_BG_HUMAN_HEALTH ] )
+ {
+ bg_classList[ i ].health =
+ bg_classList[ i ].health * modEntry[ MOD_BG_HUMAN_HEALTH ] / 100;
+ }
+ }
+ else
+ {
+ if( modEntry[ MOD_BG_ALIEN_HEALTH ] )
+ bg_classList[ i ].health =
+ bg_classList[ i ].health * modEntry[ MOD_BG_ALIEN_HEALTH ] / 100;
+ }
+ }
+
+ for( i = 0; i < bg_numWeapons; i++ )
+ {
+ if( modEntry[ MOD_BG_WEAPON_AMMO ] &&
+ bg_weapons[ i ].weaponNum != WP_ALEVEL3_UPG )
+ {
+ bg_weapons[ i ].maxAmmo =
+ bg_weapons[ i ].maxAmmo * modEntry[ MOD_BG_WEAPON_AMMO ] / 100;
+ }
+
+ if( modEntry[ MOD_BG_ALIEN_RATE ] &&
+ bg_weapons[ i ].weaponNum >= WP_ALEVEL0 &&
+ bg_weapons[ i ].weaponNum <= WP_ALEVEL4 )
+ {
+ bg_weapons[ i ].repeatRate1 =
+ bg_weapons[ i ].repeatRate1 * 100 / modEntry[ MOD_BG_ALIEN_RATE ];
+ bg_weapons[ i ].repeatRate2 =
+ bg_weapons[ i ].repeatRate2 * 100 / modEntry[ MOD_BG_ALIEN_RATE ];
+ bg_weapons[ i ].repeatRate3 =
+ bg_weapons[ i ].repeatRate3 * 100 / modEntry[ MOD_BG_ALIEN_RATE ];
+ }
+
+ if( modEntry[ MOD_BG_HUMAN_RATE ] &&
+ bg_weapons[ i ].weaponNum >= WP_BLASTER &&
+ bg_weapons[ i ].weaponNum <= WP_LUCIFER_CANNON )
+ {
+ bg_weapons[ i ].repeatRate1 =
+ bg_weapons[ i ].repeatRate1 * 100 / modEntry[ MOD_BG_HUMAN_RATE ];
+ bg_weapons[ i ].repeatRate2 =
+ bg_weapons[ i ].repeatRate2 * 100 / modEntry[ MOD_BG_HUMAN_RATE ];
+ bg_weapons[ i ].repeatRate3 =
+ bg_weapons[ i ].repeatRate3 * 100 / modEntry[ MOD_BG_HUMAN_RATE ];
+ }
+
+ if( modEntry[ MOD_BG_BUILDABLE_HEALTH ] &&
+ modEntry[ MOD_BG_HUMAN_RATE ] &&
+ ( bg_weapons[ i ].weaponNum == WP_ABUILD || bg_weapons[ i ].weaponNum == WP_ABUILD2 ) )
+ {
+ int avg;
+
+ avg = ( modEntry[ MOD_BG_BUILDABLE_HEALTH ] + MOD_BG_HUMAN_RATE ) / 2;
+ bg_weapons[ i ].repeatRate1 = bg_weapons[ i ].repeatRate1 * 100 / avg;
+ bg_weapons[ i ].repeatRate2 = bg_weapons[ i ].repeatRate2 * 100 / avg;
+ bg_weapons[ i ].repeatRate3 = bg_weapons[ i ].repeatRate3 * 100 / avg;
+ }
+
+ if( modEntry[ MOD_BG_BUILDABLE_HEALTH ] &&
+ modEntry[ MOD_BG_ALIEN_RATE ] &&
+ ( bg_weapons[ i ].weaponNum == WP_HBUILD || bg_weapons[ i ].weaponNum == WP_HBUILD2 ) )
+ {
+ int avg;
+
+ avg = ( modEntry[ MOD_BG_BUILDABLE_HEALTH ] + MOD_BG_ALIEN_RATE ) / 2;
+ bg_weapons[ i ].repeatRate1 = bg_weapons[ i ].repeatRate1 * 100 / avg;
+ bg_weapons[ i ].repeatRate2 = bg_weapons[ i ].repeatRate2 * 100 / avg;
+ bg_weapons[ i ].repeatRate3 = bg_weapons[ i ].repeatRate3 * 100 / avg;
+ }
+
+ if( modEntry[ MOD_BG_WEAPON_RELOAD ] )
+ {
+ bg_weapons[ i ].reloadTime =
+ bg_weapons[ i ].reloadTime * 100 / modEntry[ MOD_BG_WEAPON_RELOAD ];
+ }
+ }
+}
diff --git a/src/game/bg_pmove.c b/src/game/bg_pmove.c
index b0e5c96..88707bd 100644
--- a/src/game/bg_pmove.c
+++ b/src/game/bg_pmove.c
@@ -714,7 +714,15 @@ static qboolean PM_CheckJump( void )
//TA: take some stamina off
if( pm->ps->stats[ STAT_PTEAM ] == PTE_HUMANS )
- pm->ps->stats[ STAT_STAMINA ] -= 500;
+ {
+ int rate;
+
+ rate = BG_MOD_get( MOD_BG_HUMAN_STAMINA );
+ if( rate )
+ pm->ps->stats[ STAT_STAMINA ] -= 500 * 100 / rate;
+ else
+ pm->ps->stats[ STAT_STAMINA ] -= 500;
+ }
pm->ps->groundEntityNum = ENTITYNUM_NONE;
diff --git a/src/game/bg_public.h b/src/game/bg_public.h
index 6642d91..e0e6233 100644
--- a/src/game/bg_public.h
+++ b/src/game/bg_public.h
@@ -1330,3 +1330,24 @@ void BG_ClientListParse( clientList_t *list, const char *s );
#define FFF_ALIENS 2
#define FFF_BUILDABLES 4
+// value mods equal to g_mod cvars on server
+typedef enum
+{
+ MOD_BG_BUILDABLE_HEALTH, // Buildable health
+ MOD_BG_BUILDABLE_SPEED, // Buildable fire rate
+ MOD_BG_HUMAN_STAMINA, // Human stamina
+ MOD_BG_HUMAN_HEALTH, // Human health
+ MOD_BG_ALIEN_HEALTH, // Alien health
+ MOD_BG_HUMAN_RATE, // Human fire rate
+ MOD_BG_ALIEN_RATE, // Alien fire rate
+ MOD_BG_WEAPON_AMMO, // Weapon ammo per clip
+ MOD_BG_WEAPON_RELOAD, // Weapon reload time
+ MOD_BG_TURRET_ANGLE, // Allow high turret build angles
+ MOD_BG_STAGE3_STRENGTH, // alter stage 3 buildables
+ MOD_BG_COUNT // number of entries
+} modExtremeType_t;
+
+void BG_MOD_set( modExtremeType_t entry, int value );
+int BG_MOD_get( modExtremeType_t entry);
+void BG_MOD_update( void );
+
diff --git a/src/game/g_active.c b/src/game/g_active.c
index 57f58fe..2c18f00 100644
--- a/src/game/g_active.c
+++ b/src/game/g_active.c
@@ -428,7 +428,7 @@ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd )
if( ( client->pers.teamSelection == PTE_ALIENS &&
G_SearchSpawnQueue( &level.alienSpawnQueue, ent-g_entities ) ) ||
( client->pers.teamSelection == PTE_HUMANS &&
- G_SearchSpawnQueue( &level.humanSpawnQueue, ent-g_entities ) ) )
+ G_SearchSpawnQueue( &level.alienSpawnQueue, ent-g_entities ) ) )
{
client->ps.pm_flags |= PMF_QUEUED;
}
@@ -521,8 +521,12 @@ ClientInactivityTimer
Returns qfalse if the client is dropped
=================
*/
-qboolean ClientInactivityTimer( gclient_t *client )
+qboolean ClientInactivityTimer( gentity_t *ent )
{
+ gclient_t *client;
+
+ client = ent->client;
+
if( ! g_inactivity.integer )
{
// give everyone some time, so if the operator sets g_inactivity during
@@ -542,6 +546,22 @@ qboolean ClientInactivityTimer( gclient_t *client )
{
if( level.time > client->inactivityTime )
{
+ if( g_inactivityMode.integer == 1 )
+ {
+ char buf[ MAX_STRING_CHARS ];
+
+ Com_sprintf( buf, sizeof( buf ),
+ "%s^7 moved from %s to spectators due to inactivity\n",
+ client->pers.netname,
+ ( client->pers.teamSelection == PTE_ALIENS ) ? "aliens" : "humans" );
+ trap_SendServerCommand( -1, va( "print \"%s\"", buf ) );
+ G_LogOnlyPrintf( "Inactivity: %s", buf );
+
+ G_ChangeTeam( ent, PTE_NONE );
+ client->inactivityTime = level.time + g_inactivity.integer * 1000;
+ client->inactivityWarning = qfalse;
+ return qtrue;
+ }
trap_DropClient( client - level.clients, "Dropped due to inactivity" );
return qfalse;
}
@@ -549,13 +569,77 @@ qboolean ClientInactivityTimer( gclient_t *client )
if( level.time > client->inactivityTime - 10000 && !client->inactivityWarning )
{
client->inactivityWarning = qtrue;
- trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" );
+ if( g_inactivityMode.integer == 1 )
+ trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity spectate!\n\"" );
+ else
+ trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" );
}
}
return qtrue;
}
+static void ClientSpreeDecay( gentity_t *ent, int msec )
+{
+ gclient_t *client;
+
+ client = ent->client;
+ client->pers.spreeTime1000 += msec;
+
+ while( client->pers.spreeTime1000 >= 1000 )
+ {
+ client->pers.spreeTime1000 -= 1000;
+
+ // spree decay
+ if( ent->client->pers.statscounters.spreefeeds )
+ {
+ ent->client->pers.statscounters.spreefeeds -= SPREE_FEED_FADE;
+ if( ent->client->pers.statscounters.spreefeeds < 0 )
+ ent->client->pers.statscounters.spreefeeds = 0;
+ }
+ if( ent->client->pers.statscounters.spreekills > 1 )
+ {
+ ent->client->pers.statscounters.spreekills -= 2;
+ }
+ if( ent->client->pers.statscounters.spreebleeds ||
+ ent->client->pers.bleeder )
+ {
+ if( ent->client->pers.bleeder )
+ ent->client->pers.statscounters.spreebleeds -= 10;
+ else
+ ent->client->pers.statscounters.spreebleeds -= 4;
+ if( ent->client->pers.statscounters.spreebleeds < 0 )
+ ent->client->pers.statscounters.spreebleeds = 0;
+ if( ent->client->pers.bleeder )
+ {
+ if( !ent->client->pers.statscounters.spreebleeds )
+ {
+ ent->client->pers.bleeder = qfalse;
+ trap_SendServerCommand( -1,
+ va( "print \"%s^7 has been ^2forgiven ^7by their base\n\"", ent->client->pers.netname ) );
+ if( client->sess.sessionTeam != TEAM_SPECTATOR ||
+ client->sess.spectatorState != SPECTATOR_SCOREBOARD )
+ trap_SendServerCommand( ent-g_entities, "cp \"^2Your base has forgiven you\"" );
+ if( level.bleeders )
+ level.bleeders--;
+ }
+ else if( client->sess.sessionTeam != TEAM_SPECTATOR &&
+ ent->client->ps.stats[ STAT_HEALTH ] <= 0 )
+ {
+ int spreeLength;
+
+ spreeLength = ent->client->pers.statscounters.spreebleeds / 10;
+ trap_SendServerCommand( ent-g_entities,
+ va( "cp \"^3Bleeding made you an enemy of your own base!\n\n^7for %d:%02d\n\n^1Respawn delayed %d seconds\"",
+ spreeLength / 60, spreeLength % 60,
+ ( ent->client->respawnTime - level.time) / 1000 ) );
+ }
+ }
+ }
+
+ }
+}
+
/*
==================
ClientTimerActions
@@ -568,9 +652,9 @@ void ClientTimerActions( gentity_t *ent, int msec )
gclient_t *client;
usercmd_t *ucmd;
int aForward, aRight;
- qboolean walking = qfalse, stopped = qfalse,
- crouched = qfalse, jumping = qfalse,
- strafing = qfalse;
+ int staminaLarmour = STAMINA_LARMOUR_TAKE;
+ int staminaSprint = STAMINA_SPRINT_TAKE;
+ int timestep10000;
ucmd = &ent->client->pers.cmd;
@@ -582,43 +666,38 @@ void ClientTimerActions( gentity_t *ent, int msec )
client->time1000 += msec;
client->time10000 += msec;
- if( aForward == 0 && aRight == 0 )
- stopped = qtrue;
- else if( aForward <= 64 && aRight <= 64 )
- walking = qtrue;
-
- if( aRight > 0 )
- strafing = qtrue;
-
- if( ucmd->upmove > 0 )
- jumping = qtrue;
- else if( ent->client->ps.pm_flags & PMF_DUCKED )
- crouched = qtrue;
+ if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) &&
+ g_modHumanStamina.integer > 0 )
+ {
+ staminaLarmour = staminaLarmour * 100 / g_modHumanStamina.integer;
+ staminaSprint = staminaSprint * 100 / g_modHumanStamina.integer;
+ }
while ( client->time100 >= 100 )
{
client->time100 -= 100;
//if not trying to run then not trying to sprint
- if( walking || stopped )
+ if( aForward <= 64 )
client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST;
if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) )
client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST;
- if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) && !crouched )
+ if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) &&
+ !( client->ps.stats[ STAT_STATE ] & PMF_DUCKED ) )
{
//subtract stamina
if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) )
- client->ps.stats[ STAT_STAMINA ] -= STAMINA_LARMOUR_TAKE;
+ client->ps.stats[ STAT_STAMINA ] -= staminaLarmour;
else
- client->ps.stats[ STAT_STAMINA ] -= STAMINA_SPRINT_TAKE;
+ client->ps.stats[ STAT_STAMINA ] -= staminaSprint;
if( client->ps.stats[ STAT_STAMINA ] < -MAX_STAMINA )
client->ps.stats[ STAT_STAMINA ] = -MAX_STAMINA;
}
- if( walking || crouched )
+ if( ( aForward <= 64 && aForward > 5 ) || ( aRight <= 64 && aRight > 5 ) )
{
//restore stamina
client->ps.stats[ STAT_STAMINA ] += STAMINA_WALK_RESTORE;
@@ -626,7 +705,7 @@ void ClientTimerActions( gentity_t *ent, int msec )
if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA )
client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;
}
- else if( stopped )
+ else if( aForward <= 5 && aRight <= 5 )
{
//restore stamina faster
client->ps.stats[ STAT_STAMINA ] += STAMINA_STOP_RESTORE;
@@ -769,6 +848,13 @@ void ClientTimerActions( gentity_t *ent, int msec )
{
int remainingStartupTime = MEDKIT_STARTUP_TIME - ( level.time - client->lastMedKitTime );
+ if( level.vampireDeath )
+ {
+ ent->client->medKitHealthToRestore = 0;
+ trap_SendServerCommand( ent-g_entities,
+ "print \"Medkit is disabled during Vampire Sudden Death\n\"" );
+ }
+
if( remainingStartupTime < 0 )
{
if( ent->health < ent->client->ps.stats[ STAT_MAX_HEALTH ] &&
@@ -830,7 +916,7 @@ void ClientTimerActions( gentity_t *ent, int msec )
//replenish alien health
if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS &&
- level.surrenderTeam != PTE_ALIENS )
+ level.surrenderTeam != PTE_ALIENS && !level.vampireDeath )
{
int entityList[ MAX_GENTITIES ];
vec3_t range = { LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE };
@@ -866,7 +952,32 @@ void ClientTimerActions( gentity_t *ent, int msec )
!level.paused &&
( ent->lastDamageTime + ALIEN_REGEN_DAMAGE_TIME ) < level.time )
{
- ent->health += BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] ) * modifier;
+ float dist_mod = 1.0f;
+ float hadd;
+
+ if( g_modAlienRegenRange.integer )
+ {
+ float om;
+
+ om = G_BuildableRangeClosest( client->ps.origin, CREEP_BASESIZE * 2, BA_A_OVERMIND );
+ if( om > 0.0 )
+ {
+ dist_mod = ( ( CREEP_BASESIZE * 2 ) - om ) / ( CREEP_BASESIZE * 2 ) + 0.25;
+ if( dist_mod < 0.25 )
+ dist_mod = 0.25;
+ if( dist_mod > 1.0 )
+ dist_mod = 1.0;
+ }
+ else
+ {
+ dist_mod = 0.10f;
+ }
+ }
+
+ hadd = (float)(BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] ) * modifier * dist_mod);
+ if( hadd < 1.0 && hadd >= 0.5 )
+ hadd = 1.0;
+ ent->health += hadd;
// if completely healed, cancel retribution
if( ent->health >= client->ps.stats[ STAT_MAX_HEALTH ] )
@@ -884,10 +995,12 @@ void ClientTimerActions( gentity_t *ent, int msec )
if( ent->client->ps.stats[ STAT_HEALTH ] > 0 && ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
{
ent->client->pers.statscounters.timealive++;
+ ent->client->pers.karma += 1;
level.alienStatsCounters.timealive++;
if( G_BuildableRange( ent->client->ps.origin, 900, BA_A_OVERMIND ) )
{
ent->client->pers.statscounters.timeinbase++;
+ ent->client->pers.karma -= 1;
level.alienStatsCounters.timeinbase++;
}
if( BG_ClassHasAbility( ent->client->ps.stats[ STAT_PCLASS ], SCA_WALLCLIMBER ) )
@@ -904,10 +1017,12 @@ void ClientTimerActions( gentity_t *ent, int msec )
else if( ent->client->ps.stats[ STAT_HEALTH ] > 0 && ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
{
ent->client->pers.statscounters.timealive++;
+ ent->client->pers.karma += 1;
level.humanStatsCounters.timealive++;
if( G_BuildableRange( ent->client->ps.origin, 900, BA_H_REACTOR ) )
{
ent->client->pers.statscounters.timeinbase++;
+ ent->client->pers.karma -= 1;
level.humanStatsCounters.timeinbase++;
}
if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) )
@@ -943,7 +1058,7 @@ void ClientTimerActions( gentity_t *ent, int msec )
BG_DeactivateUpgrade( UP_JETPACK, client->ps.stats );
} else if( client->jetpackfuel < 10.0f && client->jetpackfuel > 0.0f) {
client->jetpackfuel = client->jetpackfuel - mod_jetpackConsume.value;
- trap_SendServerCommand( client - level.clients, "cp \"^3Fuel ^1Low!!!!!^7\nLand now.\"" );
+ trap_SendServerCommand( client - level.clients, "cp \"^3Fuel ^1Low!^7\nLand now.\"" );
} else {
client->jetpackfuel = client->jetpackfuel - mod_jetpackConsume.value;
}
@@ -961,9 +1076,23 @@ void ClientTimerActions( gentity_t *ent, int msec )
}
}
- while( client->time10000 >= 10000 )
+ timestep10000 = 10000;
+ if( g_modAlienRate.integer > 0 )
+ {
+ timestep10000 = timestep10000 * 100 / g_modAlienRate.integer;
+ if( timestep10000 < 1000 )
+ timestep10000 = 1000;
+ }
+ if( g_modBuildableHealth.integer > 0 )
+ {
+ timestep10000 = timestep10000 * 100 / g_modBuildableHealth.integer;
+ if( timestep10000 < 1000 )
+ timestep10000 = 1000;
+ }
+
+ while( client->time10000 >= timestep10000 )
{
- client->time10000 -= 10000;
+ client->time10000 -= timestep10000;
if( client->ps.weapon == WP_ALEVEL3_UPG )
{
@@ -1500,6 +1629,9 @@ void ClientThink_real( gentity_t *ent )
return;
}
+ //spree decay
+ ClientSpreeDecay( ent, msec );
+
if( client->pers.teamSelection != PTE_NONE && client->pers.joinedATeam )
G_UpdatePTRConnection( client );
@@ -1514,7 +1646,7 @@ void ClientThink_real( gentity_t *ent )
}
// check for inactivity timer, but never drop the local client of a non-dedicated server
- if( !ClientInactivityTimer( client ) )
+ if( !ClientInactivityTimer( ent ) )
return;
// calculate where ent is currently seeing all the other active clients
@@ -1567,6 +1699,15 @@ void ClientThink_real( gentity_t *ent )
client->ps.gravity = g_gravity.value;
+ if( client->pers.bubbleTime && client->pers.bubbleTime < level.time )
+ {
+ gentity_t *bubble;
+
+ client->pers.bubbleTime = level.time + 500;
+ bubble = G_TempEntity( client->ps.origin, EV_PLAYER_TELEPORT_OUT );
+ bubble->s.clientNum = ent->s.clientNum;
+ }
+
if( BG_InventoryContainsUpgrade( UP_MEDKIT, client->ps.stats ) &&
BG_UpgradeIsActive( UP_MEDKIT, client->ps.stats ) )
{
diff --git a/src/game/g_admin.c b/src/game/g_admin.c
index f9c5f84..de59a47 100644
--- a/src/game/g_admin.c
+++ b/src/game/g_admin.c
@@ -80,6 +80,16 @@ g_admin_cmd_t g_admin_cmds[ ] =
"[^3name|slot#|IP^7] (^5time^7) (^5reason^7)"
},
+ {"bring", G_admin_bring, "bring",
+ "This will bring you to a players location.",
+ "[^3name|slot#^7]"
+ },
+
+ {"bubble", G_admin_bubble, "bubble",
+ "draw attention to a player with bubbles",
+ "[^3name|slot#^7]"
+ },
+
{"buildlog", G_admin_buildlog, "buildlog",
"display a list of recent builds and deconstructs, optionally specifying"
" a team",
@@ -108,26 +118,31 @@ g_admin_cmd_t g_admin_cmds[ ] =
"[^3name|slot#^7]"
},
+ {"denyweapon", G_admin_denyweapon, "denyweapon",
+ "take away a player's ability to use a weapon or class",
+ "[^3name|slot#^7] [^3class|weapon^7]"
+ },
+
{"designate", G_admin_designate, "designate",
"give the player designated builder privileges",
"[^3name|slot#^7]"
},
-
+
{"devmap", G_admin_devmap, "devmap",
"load a map with cheats (and optionally force layout)",
"[^3mapname^7] (^5layout^7)"
},
- {"denyweapon", G_admin_denyweapon, "denyweapon",
- "take away a player's ability to use a weapon or class",
- "[^3name|slot#^7] [^3class|weapon^7]"
- },
-
{"drop", G_admin_drop, "drop",
"kick a client from the server without log",
"[^3name|slot#^7] [^3message^7]"
},
+ {"expire", G_admin_expire, "expire",
+ "Expires up to 5 level 1 admins with !seen times older than g_adminExpireTime",
+ "[^5confirm^7]"
+ },
+
{"flag", G_admin_flag, "flag",
"add an admin flag to a player, prefix flag with '-' to disallow the flag. "
"console can use this command on admin levels by prefacing a '*' to the admin level value.",
@@ -138,7 +153,7 @@ g_admin_cmd_t g_admin_cmds[ ] =
"list all flags understood by this server",
""
},
-
+
{"help", G_admin_help, "help",
"display commands available to you or help on a specific command",
"(^5command^7)"
@@ -148,12 +163,17 @@ g_admin_cmd_t g_admin_cmds[ ] =
"display the contents of server info files",
"(^5subject^7)"
},
-
+
+ {"immunity", G_admin_immunity, "immunity",
+ "give a player ban immunity",
+ "[^3+|-^7](^5slot#^7)"
+ },
+
{"invisible", G_admin_invisible, "invisible",
"hides a player so they cannot be seen in playerlists",
""
},
-
+
{"kick", G_admin_kick, "kick",
"kick a player with an optional reason",
"[^3name|slot#^7] (^5reason^7)"
@@ -224,9 +244,11 @@ g_admin_cmd_t g_admin_cmds[ ] =
""
},
- {"nobuild", G_admin_nobuild, "nobuild",
- "set nobuild markers to prevent players from building in an area",
- "(^5area^7) (^5height^7)"
+ {"outlaw", G_admin_outlaw, "outlaw",
+ "adjust a player's bleed counter, usually to make their base turn on them."
+ " bleed value can be '?' to query their current value, a number to add and activate bleed status,"
+ " +num or -num will silently adjust their current bleed value, 0 will pardon them",
+ "(^5name|slot^7) (^5bleed value)"
},
{"passvote", G_admin_passvote, "passvote",
@@ -240,6 +262,12 @@ g_admin_cmd_t g_admin_cmds[ ] =
"(^5name|slot|*^7)"
},
+ {"practice", G_admin_practice, "practice",
+ "set practice mode for player names with 'clan tag', "
+ "these players will be allowed to join any team regardless of balance. "
+ "'map count' is number of maps to maintain practice mode",
+ "[^3clan tag|off^7] [^3map count^7])"
+ },
{"putteam", G_admin_putteam, "putteam",
"move a player to a specified team",
@@ -253,7 +281,7 @@ g_admin_cmd_t g_admin_cmds[ ] =
{"register", G_admin_register, "register",
"Registers your name to protect it from being used by others or updates your admin name to your current name.",
- ""
+ "[^3level^7] [^3password^7]"
},
{"rename", G_admin_rename, "rename",
@@ -299,12 +327,13 @@ g_admin_cmd_t g_admin_cmds[ ] =
{"spec999", G_admin_spec999, "spec999",
"move 999 pingers to the spectator team",
- ""
- },
-
+ ""},
+
+ //kev: a bit of a hack, but there is no real point to
+ //creating a new admin flag for this, so i stole it from !help
{"specme", G_admin_putmespec, "specme",
- "moves you to the spectators",
- ""
+ "moves you to the spectators (can be done silently with the 's' argument)",
+ "(^5s^7)"
},
{"subnetban", G_admin_subnetban, "subnetban",
@@ -316,7 +345,7 @@ g_admin_cmd_t g_admin_cmds[ ] =
"\n ^1WARNING:^7 Use of this command may make your admin.dat incompatible with other game.qvms"
},
- {"suspendban", G_admin_suspendban, "ban",
+ {"suspendban", G_admin_suspendban, "suspendban",
"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",
@@ -325,7 +354,11 @@ g_admin_cmd_t g_admin_cmds[ ] =
{"time", G_admin_time, "time",
"show the current local server time",
- ""
+ ""},
+
+ {"tklog", G_admin_tklog, "tklog",
+ "list recent teamkill activity",
+ "(^5start id#|name|-skip#^7) (^5search skip#^7)"
},
{"unban", G_admin_unban, "ban",
@@ -337,13 +370,13 @@ g_admin_cmd_t g_admin_cmds[ ] =
"revoke designated builder privileges",
"[^3name|slot#^7]"
},
-
+
{"unflag", G_admin_flag, "flag",
"clears an admin flag from a player. "
"console can use this command on admin levels by prefacing a '*' to the admin level value.",
"[^3name|slot#|admin#|*adminlevel^7] (^5+^7|^5-^7)[^3flag^7]"
},
-
+
{"unlock", G_admin_unlock, "lock",
"unlock a locked team",
"[^3a|h^7]"
@@ -354,6 +387,11 @@ g_admin_cmd_t g_admin_cmds[ ] =
"[^3name|slot#^7]"
},
+ {"nobuild", G_admin_nobuild, "nobuild",
+ "Enable and control nobuild mode.",
+ "[^3on|off|save|add|del|list|mode|zone|+|-|go^7]"
+ },
+
{"unpause", G_admin_pause, "pause",
"allow a player to interact with the game."
" * will unpause all players, using no argument will unpause game clock",
@@ -376,6 +414,12 @@ g_admin_ban_t *g_admin_bans[ MAX_ADMIN_BANS ];
g_admin_command_t *g_admin_commands[ MAX_ADMIN_COMMANDS ];
g_admin_namelog_t *g_admin_namelog[ MAX_ADMIN_NAMELOGS ];
+static int admin_adminlog_index = 0;
+g_admin_adminlog_t *g_admin_adminlog[ MAX_ADMIN_ADMINLOGS ];
+
+static int admin_tklog_index = 0;
+g_admin_tklog_t *g_admin_tklog[ MAX_ADMIN_TKLOGS ];
+
// match a certain flag within these flags
// return state of whether flag was found or not,
// set *perm to indicate whether found flag was + or -
@@ -404,16 +448,12 @@ static qboolean admin_permission( char *flags, const char *flag, qboolean *perm
*perm = base_perm;
return qtrue;
}
-
return qfalse;
}
-static int admin_adminlog_index = 0;
-g_admin_adminlog_t *g_admin_adminlog[ MAX_ADMIN_ADMINLOGS ];
-
// This function should only be used directly when the client is connecting and thus has no GUID.
// Else, use G_admin_permission()
-qboolean G_admin_permission_guid( char *guid, const char* flag )
+qboolean G_admin_permission_guid( char *guid, const char *flag )
{
int i;
int l = 0;
@@ -436,8 +476,7 @@ qboolean G_admin_permission_guid( char *guid, const char* flag )
for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ )
{
if( g_admin_levels[ i ]->level == l )
- return admin_permission( g_admin_levels[ i ]->flags, flag, &perm ) &&
- perm;
+ return admin_permission( g_admin_levels[ i ]->flags, flag, &perm ) && perm;
}
return qfalse;
}
@@ -542,18 +581,13 @@ static qboolean admin_higher_guid( char *admin_guid, char *victim_guid )
{
int i;
int alevel = 0;
- int alevel2 = 0;
+ qboolean perm = qfalse;
for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ )
{
if( !Q_stricmp( admin_guid, g_admin_admins[ i ]->guid ) )
{
alevel = g_admin_admins[ i ]->level;
-
- // Novelty Levels should be equivelant to level 1
- if( alevel > 9 )
- alevel = 1;
-
break;
}
}
@@ -561,17 +595,9 @@ static qboolean admin_higher_guid( char *admin_guid, char *victim_guid )
{
if( !Q_stricmp( victim_guid, g_admin_admins[ i ]->guid ) )
{
- alevel2 = g_admin_admins[ i ]->level;
-
- // Novelty Levels should be equivelant to level 1
- if( alevel2 > 9 )
- alevel2 = 1;
-
- if( alevel < alevel2 )
- return qfalse;
-
- if( strstr( g_admin_admins[ i ]->flags, va( "%s", ADMF_IMMUTABLE ) ) )
+ if( alevel < g_admin_admins[ i ]->level )
return qfalse;
+ return ( !admin_permission( g_admin_admins[ i ]->flags, ADMF_IMMUTABLE, &perm ) || !perm );
}
}
return qtrue;
@@ -619,7 +645,7 @@ static void admin_writeconfig( void )
{
fileHandle_t f;
int len, i;
- int t, expiretime;
+ int t;
char levels[ MAX_STRING_CHARS ] = {""};
if( !g_admin.string[ 0 ] )
@@ -650,19 +676,9 @@ static void admin_writeconfig( void )
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 )
+ if( g_admin_admins[ i ]->level == 0 )
continue;
- //if set dont write admins that havent been seen in a while
- expiretime = G_admin_parse_time( g_adminExpireTime.string );
- if( expiretime > 0 ) {
- //only expire level 1 people
- if( t - expiretime > g_admin_admins[ i ]->seen && g_admin_admins[ i ]->level == 1 ) {
- G_Printf("Admin %s has been expired.\n", g_admin_admins[ i ]->name );
- continue;
- }
- }
-
trap_FS_Write( "[admin]\n", 8, f );
trap_FS_Write( "name = ", 10, f );
admin_writeconfig_string( g_admin_admins[ i ]->name, f );
@@ -672,8 +688,6 @@ static void admin_writeconfig( void )
admin_writeconfig_int( g_admin_admins[ i ]->level, f );
trap_FS_Write( "flags = ", 10, f );
admin_writeconfig_string( g_admin_admins[ i ]->flags, f );
- trap_FS_Write( "seen = ", 10, f );
- admin_writeconfig_int( g_admin_admins[ i ]->seen, f );
trap_FS_Write( "\n", 1, f );
}
for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ )
@@ -697,7 +711,8 @@ static void admin_writeconfig( void )
admin_writeconfig_string( g_admin_bans[ i ]->made, f );
trap_FS_Write( "expires = ", 10, f );
admin_writeconfig_int( g_admin_bans[ i ]->expires, f );
- if( g_admin_bans[ i ]->suspend > t ) {
+ if( g_admin_bans[ i ]->suspend > t )
+ {
trap_FS_Write( "suspend = ", 10, f );
admin_writeconfig_int( g_admin_bans[ i ]->suspend, f );
}
@@ -778,6 +793,253 @@ 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;
+
+ G_admin_karma_sync( );
+
+ if( !g_chat.string[ 0 ] )
+ {
+ G_Printf( "WARNING: g_chat is not set. "
+ " channel subscriptions will not be saved to a file.\n" );
+ return;
+ }
+
+ // check that there is something to save
+ found = qfalse;
+ for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] && !found; i++ )
+ {
+ if( g_admin_admins[ i ]->level == 0 )
+ continue;
+ for( j = 0; j < CHAT_MAXCHAN; j++ )
+ {
+ if( g_admin_admins[ i ]->chat[ j ][ 0 ] )
+ found = qtrue;
+ }
+ if(g_admin_admins[ i ]->seen )
+ found = qtrue;
+ }
+ if( !found )
+ {
+ G_Printf( "Cowardly refusing to create an empty chat 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 == 0 )
+ 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 );
+ trap_FS_Write( "karma = ", 10, f );
+ admin_writeconfig_int( g_admin_admins[ i ]->karma, 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( a == NULL )
+ {
+ // no guid match, ignored
+ }
+ else if( !Q_stricmp( t, "seen" ) )
+ {
+ admin_readconfig_int( &cnf, &a->seen );
+ }
+ else if( !Q_stricmp( t, "karma" ) )
+ {
+ admin_readconfig_int( &cnf, &a->karma );
+ }
+ 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 ) )
+ {
+ ent->client->pers.karma = g_admin_admins[ i ]->karma;
+ 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 )
@@ -828,7 +1090,7 @@ static void admin_default_levels( void )
sizeof( l->name ) );
Q_strncpyz( g_admin_levels[ 4 ]->flags,
"listplayers admintest help specme time putteam spec999 kick mute showbans "
- "ban namelog warn denybuild ADMINCHAT SEESFULLLISTPLAYERS",
+ "ban namelog warn denybuild invisible ADMINCHAT SEESFULLLISTPLAYERS",
sizeof( l->flags ) );
Q_strncpyz( g_admin_levels[ 5 ]->name, "^1Server Operator",
@@ -897,7 +1159,7 @@ void G_admin_set_adminname( gentity_t *ent )
}
}
-// Get an admin's registered name
+// get an admin's realname
const char *G_admin_get_adminname( gentity_t *ent )
{
int i;
@@ -914,8 +1176,6 @@ const char *G_admin_get_adminname( gentity_t *ent )
return ent->client->pers.netname;
}
-// Get an admin's name to print as the user of various commands,
-// obeying admin stealth settings when necessary
char* G_admin_adminPrintName( gentity_t *ent )
{
char *out;
@@ -1110,26 +1370,27 @@ static int admin_listadmins( gentity_t *ent, int start, char *search, int minlev
break;
}
}
- ADMBP( va( "%4i %4i %s^7 (*%s) %s^7\n",
+ ADMBP( va( "%4i %4i %s^7 (*%s) ^1%1s ^7%s^7\n",
i,
l,
lname,
guid_stub,
+ ( G_admin_permission( &g_entities[ i ], ADMF_BAN_IMMUNITY ) ) ? "I" : "",
vic->client->pers.netname ) );
drawn++;
}
for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]
&& drawn < MAX_ADMIN_LISTITEMS; i++ )
- if( g_admin_admins[ i ]->level >= minlevel )
+ if( ( minlevel > 0 && g_admin_admins[ i ]->level >= minlevel ) ||
+ ( minlevel < 0 && g_admin_admins[ i ]->level <= minlevel ) ||
+ ( minlevel == 0 && g_admin_admins[ i ]->level != 0 ) )
{
-
if( start )
{
start--;
continue;
}
-
if( search[ 0 ] )
{
G_SanitiseString( g_admin_admins[ i ]->name, name, sizeof( name ) );
@@ -1174,11 +1435,12 @@ static int admin_listadmins( gentity_t *ent, int start, char *search, int minlev
break;
}
}
- ADMBP( va( "%4i %4i %s^7 (*%s) %s^7\n",
+ ADMBP( va( "%4i %4i %s^7 (*%s) ^1%1s ^7%s^7\n",
( i + MAX_CLIENTS ),
g_admin_admins[ i ]->level,
lname,
guid_stub,
+ ( G_admin_permission_guid( g_admin_admins[ i ]->guid, ADMF_BAN_IMMUNITY ) ) ? "I" : "",
g_admin_admins[ i ]->name ) );
drawn++;
}
@@ -1223,6 +1485,7 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen )
int t;
char notice[51];
qboolean ignoreIP = qfalse;
+ qboolean banned = qfalse;
trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) );
@@ -1252,17 +1515,15 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen )
if(!IP[k]) continue;
userIP |= IP[k] << 8*(k-1);
}
- ignoreIP = G_admin_permission_guid( guid , ADMF_BAN_IMMUNITY );
+ ignoreIP = G_admin_permission_guid( guid, ADMF_BAN_IMMUNITY );
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 )
continue;
- //if suspend time is over continue
if( g_admin_bans[ i ]->suspend >= t )
continue;
-
if( !ignoreIP )
{
tempIP = userIP;
@@ -1295,47 +1556,38 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen )
if( intIP == tempIP || mask == 0 )
{
- char duration[ 32 ];
- G_admin_duration( ( g_admin_bans[ i ]->expires - t ),
- duration, sizeof( duration ) );
-
- // flood protected
- if( t - lastConnectTime >= 300 ||
- Q_stricmp( lastConnectIP, ip ) )
- {
- lastConnectTime = t;
- Q_strncpyz( lastConnectIP, ip, sizeof( lastConnectIP ) );
-
- G_WarningsPrintf(
- "ban",
- "Banned player %s^7 (%s^7) tried to connect (ban #%i on %s by %s^7 expires %s reason: %s^7 )\n",
- Info_ValueForKey( userinfo, "name" ),
- g_admin_bans[ i ]->name,
- i+1,
- ip,
- 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 %s",
- g_admin_bans[ i ]->banner,
- g_admin_bans[ i ]->reason,
- duration,
- notice
- );
G_LogPrintf("Banned player tried to connect from IP %s\n", ip);
- return qtrue;
+ banned = qtrue;
}
}
- if( *guid && !Q_stricmp( g_admin_bans[ i ]->guid, guid ) )
+ if( !banned && *guid && !Q_stricmp( g_admin_bans[ i ]->guid, guid ) )
+ {
+ G_LogPrintf("Banned player tried to connect with GUID %s\n", guid);
+ banned = qtrue;
+ }
+ if( banned )
{
char duration[ 32 ];
G_admin_duration( ( g_admin_bans[ i ]->expires - t ),
duration, sizeof( duration ) );
+
+ // flood protected
+ if( t - lastConnectTime >= 300 ||
+ Q_stricmp( lastConnectIP, ip ) )
+ {
+ lastConnectTime = t;
+ Q_strncpyz( lastConnectIP, ip, sizeof( lastConnectIP ) );
+
+ G_AdminsPrintf(
+ "Banned player %s^7 (%s^7) tried to connect (ban #%i on %s by %s^7 expires %s reason: %s^7 )\n",
+ Info_ValueForKey( userinfo, "name" ),
+ g_admin_bans[ i ]->name,
+ i+1,
+ ip,
+ g_admin_bans[ i ]->banner,
+ duration,
+ g_admin_bans[ i ]->reason );
+ }
Com_sprintf(
reason,
rlen,
@@ -1344,7 +1596,6 @@ qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen )
g_admin_bans[ i ]->reason,
duration
);
- G_Printf("Banned player tried to connect with GUID %s\n", guid);
return qtrue;
}
}
@@ -1469,10 +1720,7 @@ void G_admin_namelog_update( gclient_t *client, qboolean disconnect )
char n2[ MAX_NAME_LENGTH ];
int clientNum = ( client - level.clients );
- if ( client->sess.invisible == qfalse )
- {
- G_admin_seen_update( client->pers.guid );
- }
+ G_admin_seen_update( client, disconnect );
G_SanitiseString( client->pers.netname, n1, sizeof( n1 ) );
for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ )
@@ -1539,11 +1787,6 @@ void G_admin_namelog_update( gclient_t *client, qboolean disconnect )
G_AdminsPrintf( "^7%s^7's Putteam spectator has been restored\n", client->pers.netname );
g_admin_namelog[ i ]->specExpires = 0;
}
- if( g_admin_namelog[ i ]->voteCount > 0 )
- {
- client->pers.voteCount = g_admin_namelog[ i ]->voteCount;
- g_admin_namelog[ i ]->voteCount = 0;
- }
}
else
{
@@ -1573,12 +1816,9 @@ void G_admin_namelog_update( gclient_t *client, qboolean disconnect )
{
g_admin_namelog[ i ]->specExpires = client->pers.specExpires;
}
- if( client->pers.voteCount > 0 )
- {
- g_admin_namelog[ i ]->voteCount = client->pers.voteCount;
- }
}
+
return;
}
}
@@ -1698,10 +1938,6 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg )
{
admin_readconfig_string( &cnf, a->flags, sizeof( a->flags ) );
}
- else if( !Q_stricmp( t, "seen" ) )
- {
- admin_readconfig_int( &cnf, &a->seen );
- }
else
{
ADMP( va( "^3!readconfig: ^7[admin] parse error near %s on line %d\n",
@@ -1863,6 +2099,9 @@ qboolean G_admin_readconfig( gentity_t *ent, int skiparg )
for( i = 0; i < level.maxclients; i++ )
if( level.clients[ i ].pers.connected != CON_DISCONNECTED )
level.clients[ i ].pers.adminLevel = G_admin_level( &g_entities[ i ] );
+
+ G_admin_chat_readconfig( ent );
+
return qtrue;
}
@@ -1874,7 +2113,6 @@ qboolean G_admin_time( gentity_t *ent, int skiparg )
t = trap_RealTime( &qt );
ADMP( va( "^3!time: ^7local time is %02i:%02i:%02i\n",
qt.tm_hour, qt.tm_min, qt.tm_sec ) );
-
return qtrue;
}
@@ -2138,379 +2376,6 @@ qboolean G_admin_setlevel( gentity_t *ent, int skiparg )
return qtrue;
}
-static int SortFlags( const void *pa, const void *pb )
-{
- char *a = (char *)pa;
- char *b = (char *)pb;
-
- if( *a == '-' || *a == '+' )
- a++;
- if( *b == '-' || *b == '+' )
- b++;
- return strcmp(a, b);
-}
-
-#define MAX_USER_FLAGS 200
-const char *G_admin_user_flag( char *oldflags, char *flag, qboolean add, qboolean clear,
- char *newflags, int size )
-{
- char *token, *token_p;
- char *key;
- char head_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ];
- char tail_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ];
- char allflag[ MAX_ADMIN_FLAG_LEN ];
- char newflag[ MAX_ADMIN_FLAG_LEN ];
- int head_count = 0;
- int tail_count = 0;
- qboolean flagset = qfalse;
- int i;
-
- if( !flag[ 0 ] )
- {
- return "invalid admin flag";
- }
-
- allflag[ 0 ] = '\0';
- token_p = oldflags;
- while( *( token = COM_Parse( &token_p ) ) )
- {
- key = token;
- if( *key == '-' || *key == '+' )
- key++;
-
- if( !strcmp( key, flag ) )
- {
- if( flagset )
- continue;
- flagset = qtrue;
- if( clear )
- {
- // clearing ALLFLAGS will result in clearing any following flags
- if( !strcmp( key, ADMF_ALLFLAGS ) )
- break;
- else
- continue;
- }
- Com_sprintf( newflag, sizeof( newflag ), "%s%s",
- ( add ) ? "+" : "-", key );
- }
- else
- {
- Q_strncpyz( newflag, token, sizeof( newflag ) );
- }
-
- if( !strcmp( key, ADMF_ALLFLAGS ) )
- {
- if( !allflag[ 0 ] )
- Q_strncpyz( allflag, newflag, sizeof( allflag ) );
- continue;
- }
-
- if( !allflag[ 0 ] )
- {
- if( head_count < MAX_USER_FLAGS )
- {
- Q_strncpyz( head_flags[ head_count ], newflag,
- sizeof( head_flags[ head_count ] ) );
- head_count++;
- }
- }
- else
- {
- if( tail_count < MAX_USER_FLAGS )
- {
- Q_strncpyz( tail_flags[ tail_count ], newflag,
- sizeof( tail_flags[ tail_count ] ) );
- tail_count++;
- }
- }
- }
-
- if( !flagset && !clear )
- {
- if( !strcmp( flag, ADMF_ALLFLAGS ) )
- {
- Com_sprintf( allflag, sizeof( allflag ), "%s%s",
- ( add ) ? "" : "-", ADMF_ALLFLAGS );
- }
- else if( !allflag[ 0 ] )
- {
- if( head_count < MAX_USER_FLAGS )
- {
- Com_sprintf( head_flags[ head_count ], sizeof( head_flags[ head_count ] ),
- "%s%s", ( add ) ? "" : "-", flag );
- head_count++;
- }
- }
- else
- {
- if( tail_count < MAX_USER_FLAGS )
- {
- Com_sprintf( tail_flags[ tail_count ], sizeof( tail_flags[ tail_count ] ),
- "%s%s", ( add ) ? "+" : "-", flag );
- tail_count++;
- }
- }
- }
-
- qsort( head_flags, head_count, sizeof( head_flags[ 0 ] ), SortFlags );
- qsort( tail_flags, tail_count, sizeof( tail_flags[ 0 ] ), SortFlags );
-
- // rebuild flags
- newflags[ 0 ] = '\0';
- for( i = 0; i < head_count; i++ )
- {
- Q_strcat( newflags, size,
- va( "%s%s", ( i ) ? " ": "", head_flags[ i ] ) );
- }
- if( allflag[ 0 ] )
- {
- Q_strcat( newflags, size,
- va( "%s%s", ( newflags[ 0 ] ) ? " ": "", allflag ) );
-
- for( i = 0; i < tail_count; i++ )
- {
- Q_strcat( newflags, size,
- va( " %s", tail_flags[ i ] ) );
- }
- }
-
- return NULL;
-}
-
-typedef struct {
- char *flag;
- char *description;
-} AdminFlagListEntry_t;
-static AdminFlagListEntry_t adminFlagList[] =
-{
- { ADMF_ACTIVITY, "inactivity rules do not apply" },
- { ADMF_ADMINCHAT, "can see and use admin chat" },
- { ADMF_ALLFLAGS, "has all flags and can use any command" },
- { ADMF_BAN_IMMUNITY, "immune from IP bans" },
- { ADMF_CAN_PERM_BAN, "can permanently ban players" },
- { ADMF_DBUILDER, "permanent designated builder" },
- { ADMF_FORCETEAMCHANGE, "team balance rules do not apply" },
- { ADMF_INCOGNITO, "does not show as admin in !listplayers" },
- { ADMF_IMMUNITY, "cannot be vote kicked or muted" },
- { ADMF_IMMUTABLE, "admin commands cannot be used on them" },
- { ADMF_NOCENSORFLOOD, "no flood protection" },
- { ADMF_NO_VOTE_LIMIT, "vote limitations do not apply" },
- { ADMF_SEESFULLLISTPLAYERS, "sees all info in !listplayers" },
- { ADMF_SPEC_ALLCHAT, "can see team chat as spectator" },
- { ADMF_ADMINSTEALTH, "uses admin stealth" },
- { ADMF_TEAMCHANGEFREE, "keeps credits on team switch" },
- { ADMF_TEAMCHAT_CMD, "can run commands from team chat" },
- { ADMF_UNACCOUNTABLE, "does not need to specify reason for kick/ban" },
- { ADMF_NO_CHAT, "can not talk" },
- { ADMF_NO_VOTE, "can not call votes" }
-};
-static int adminNumFlags= sizeof( adminFlagList ) / sizeof( adminFlagList[ 0 ] );
-
-#define MAX_LISTCOMMANDS 128
-qboolean G_admin_flaglist( gentity_t *ent, int skiparg )
-{
- qboolean shown[ MAX_LISTCOMMANDS ];
- int i, j;
- int count = 0;
-
- ADMBP_begin();
-
- ADMBP( "^3Ability flags:\n" );
-
- for( i = 0; i < adminNumFlags; i++ )
- {
- ADMBP( va( " %s%-20s ^7%s\n",
- ( adminFlagList[ i ].flag[ 0 ] != '.' ) ? "^5" : "^1",
- adminFlagList[ i ].flag,
- adminFlagList[ i ].description ) );
- }
-
- ADMBP( "^3Command flags:\n" );
-
- memset( shown, 0, sizeof( shown ) );
- for( i = 0; i < adminNumCmds; i++ )
- {
- if( i < MAX_LISTCOMMANDS && shown[ i ] )
- continue;
- ADMBP( va( " ^5%-20s^7", g_admin_cmds[ i ].flag ) );
- for( j = i; j < adminNumCmds; j++ )
- {
- if( !strcmp( g_admin_cmds[ j ].flag, g_admin_cmds[ i ].flag ) )
- {
- ADMBP( va( " %s", g_admin_cmds[ j ].keyword ) );
- if( j < MAX_LISTCOMMANDS )
- shown[ j ] = qtrue;
- }
- }
- ADMBP( "\n" );
- count++;
- }
-
- ADMBP( va( "^3!flaglist: ^7listed %d abilities and %d command flags\n",
- adminNumFlags, count ) );
-
- ADMBP_end();
-
- return qtrue;
-}
-
-qboolean G_admin_flag( gentity_t *ent, int skiparg )
-{
- char command[ MAX_ADMIN_CMD_LEN ], *cmd;
- char name[ MAX_NAME_LENGTH ];
- char flagbuf[ MAX_ADMIN_FLAG_LEN ];
- char *flag;
- int id;
- char adminname[ MAX_NAME_LENGTH ] = {""};
- const char *result;
- qboolean add = qtrue;
- qboolean clear = qfalse;
- int admin_level = -1;
- int i, level;
-
- 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;
- }
-
- G_SayArgv( 1 + skiparg, name, sizeof( name ) );
- if( name[ 0 ] == '*' )
- {
- if( ent )
- {
- ADMP( va( "^3!%s: only console can change admin level flags\n", cmd ) );
- return qfalse;
- }
- id = atoi( name + 1 );
- for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ )
- {
- if( g_admin_levels[ i ]->level == id )
- {
- admin_level = i;
- break;
- }
- }
- if( admin_level < 0 )
- {
- ADMP( va( "^3!%s: admin level %d does not exist\n", cmd, id ) );
- return qfalse;
- }
- Com_sprintf( adminname, sizeof( adminname ), "admin level %d", id );
- }
- else
- {
- id = G_admin_find_admin_slot( ent, name, cmd, 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 than you\n", cmd ) );
- return qfalse;
- }
- }
-
- if( G_SayArgc() < 3 + skiparg )
- {
- flag = "";
- level = 0;
- if( admin_level < 0 )
- {
- for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ )
- {
- if( g_admin_admins[ id ]->level == g_admin_levels[ i ]->level )
- {
- flag = g_admin_levels[ i ]->flags;
- level = g_admin_admins[ id ]->level;
- break;
- }
- }
- ADMP( va( "^3%s:^7 flags for %s^7 are '^3%s^7'\n",
- cmd, adminname, g_admin_admins[ id ]->flags) );
- }
- else
- {
- flag = g_admin_levels[ admin_level ]->flags;
- level = g_admin_levels[ admin_level ]->level;
- }
- ADMP( va( "^3%s:^7 level %d flags are '%s'\n",
- cmd, level, flag ) );
-
- return qtrue;
- }
-
- G_SayArgv( 2 + skiparg, flagbuf, sizeof( flagbuf ) );
- flag = flagbuf;
- if( flag[ 0 ] == '-' || flag[ 0 ] == '+' )
- {
- add = ( flag[ 0 ] == '+' );
- 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( flag[ 0 ] != '.' && !G_admin_permission( ent, flag ) )
- {
- 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;
- }
-
- if( admin_level < 0 )
- {
- result = G_admin_user_flag( g_admin_admins[ id ]->flags, flag, add, clear,
- g_admin_admins[ id ]->flags, sizeof( g_admin_admins[ id ]->flags ) );
- }
- else
- {
- result = G_admin_user_flag( g_admin_levels[ admin_level ]->flags, flag, add, clear,
- g_admin_levels[ admin_level ]->flags,
- sizeof( g_admin_levels[ admin_level ]->flags ) );
- }
- if( result )
- {
- ADMP( va( "^3!flag: ^7an error occured setting flag '^3%s^7', %s\n",
- flag, result ) );
- return qfalse;
- }
-
- if( !Q_stricmp( cmd, "flag" ) )
- {
- G_AdminsPrintf( "^3!%s: ^7%s^7 was %s admin flag '%s' by %s\n",
- cmd, adminname,
- ( add ) ? "given" : "denied",
- flag,
- ( ent ) ? ent->client->pers.netname : "console" );
- }
- else
- {
- G_AdminsPrintf( "^3!%s: ^7admin flag '%s' for %s^7 cleared by %s\n",
- cmd, flag, 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;
-}
-
int G_admin_parse_time( const char *time )
{
int seconds = 0, num = 0;
@@ -2544,6 +2409,49 @@ int G_admin_parse_time( const char *time )
return seconds;
}
+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 ) &&
+ G_admin_permission_guid( g_admin_admins[ j ]->guid, ADMF_BAN_IMMUNITY ) )
+ {
+ immune = qtrue;
+ }
+ }
+
+ G_AdminsPrintf( "new ban #%d duplicates %sban #%d (%s^7)%s.\n",
+ ban + 1,
+ ( g_admin_bans[ i ]->suspend > t ) ? "SUSPENDED " : "",
+ i + 1,
+ g_admin_bans[ i ]->name,
+ ( immune ) ? ", player has immunity" : "" );
+ }
+ }
+}
+
static qboolean admin_create_ban( gentity_t *ent,
char *netname,
char *guid,
@@ -2597,6 +2505,82 @@ static qboolean admin_create_ban( gentity_t *ent,
return qfalse;
}
g_admin_bans[ i ] = b;
+
+ admin_check_duplicate_ban( i );
+
+ return qtrue;
+}
+
+static qboolean admin_create_ban_check_repeats( gentity_t *ent,
+ char *netname,
+ char *guid,
+ char *ip,
+ int seconds,
+ char *reason )
+{
+ qboolean repeatBan = qfalse;
+ qboolean isKick = qfalse;
+ qtime_t qt;
+ int t;
+ int i;
+
+ t = trap_RealTime( &qt );
+
+ if( seconds >= G_admin_parse_time( g_adminTempBan.string ) - 60 &&
+ seconds <= G_admin_parse_time( g_adminTempBan.string ) + 60 )
+ isKick = qtrue;
+
+ if( g_adminBanRepeatKicks.integer && isKick )
+ {
+ for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ] && !repeatBan; i++ )
+ {
+ if( g_admin_bans[ i ]->expires != 0 &&
+ g_admin_bans[ i ]->expires - t < 1 )
+ continue;
+
+ if( !Q_stricmp( ip, g_admin_bans[ i ]->ip ) ||
+ ( guid[0] != 'X' && !Q_stricmp( guid, g_admin_bans[ i ]->guid) ))
+ {
+ char duration[ 32 ];
+
+ g_admin_bans[ i ]->suspend = 0;
+ g_admin_bans[ i ]->expires += g_adminBanRepeatKicks.integer * 60 * 60;
+ repeatBan = qtrue;
+
+ G_admin_duration( ( g_admin_bans[ i ]->expires - t ), duration, sizeof( duration ) );
+ trap_SendServerCommand( -1,
+ va( "print \"^3autoban: ^7%s^7 has been auto-banned. duration: %s, reason: repeated kicks\n\"",
+ netname, duration ) );
+
+ return qtrue;
+ }
+ }
+ }
+
+ if( !admin_create_ban( ent, netname, guid, ip, seconds, reason ) )
+ return qfalse;
+
+ if( g_adminBanRepeatKicks.integer && seconds > 0 && isKick )
+ {
+ int newestBan = -1;
+ int length;
+
+ length = g_adminBanRepeatKicks.integer * 60 * 60;
+ if( admin_create_ban( ent, netname, guid, ip, length, reason ) )
+ {
+ for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ )
+ {
+ newestBan = i;
+ }
+ if( newestBan >= 0 &&
+ !Q_stricmp( ip, g_admin_bans[ newestBan ]->ip ) )
+ {
+ g_admin_bans[ newestBan ]->suspend = t + length;
+ Q_strncpyz( g_admin_bans[ newestBan ]->banner, "^3auto-banner", sizeof( g_admin_bans[ newestBan ]->banner ) );
+ }
+ }
+ }
+
return qtrue;
}
@@ -2634,7 +2618,8 @@ qboolean G_admin_kick( gentity_t *ent, int skiparg )
return qfalse;
}
vic = &g_entities[ pids[ 0 ] ];
- admin_create_ban( ent,
+ G_admin_autorevert( vic );
+ admin_create_ban_check_repeats( ent,
vic->client->pers.netname,
vic->client->pers.guid,
vic->client->pers.ip, G_admin_parse_time( g_adminTempBan.string ),
@@ -2642,10 +2627,20 @@ qboolean G_admin_kick( gentity_t *ent, int skiparg )
if( g_admin.string[ 0 ] )
admin_writeconfig();
+ vic->client->pers.karma -= 5000;
+
trap_SendServerCommand( pids[ 0 ],
va( "disconnect \"You have been kicked.\n%s^7\nreason:\n%s\n%s\"",
( ent ) ? va( "admin:\n%s", G_admin_adminPrintName( ent ) ) : "admin\nconsole",
( *reason ) ? reason : "kicked by admin", notice ) );
+
+ G_LogPrintf( "kick: %i %i [%s] (%s) %s^7 %s^7\n",
+ vic->client->ps.clientNum,
+ G_admin_parse_time( g_adminTempBan.string ),
+ vic->client->pers.ip,
+ vic->client->pers.guid,
+ vic->client->pers.netname,
+ ( *reason ) ? reason : "automatic temp ban created by kick" );
trap_DropClient( pids[ 0 ], va( "kicked%s^7, reason: %s",
( ent ) ? va( " by %s", G_admin_adminPrintName( ent ) ) : " by console",
@@ -2669,6 +2664,7 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg )
char s2[ MAX_NAME_LENGTH ];
char guid_stub[ 9 ];
char notice[51];
+ gentity_t *vic;
trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) );
@@ -2823,7 +2819,7 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg )
return qfalse;
}
- admin_create_ban( ent,
+ admin_create_ban_check_repeats( ent,
g_admin_namelog[ logmatch ]->name[ 0 ],
g_admin_namelog[ logmatch ]->guid,
g_admin_namelog[ logmatch ]->ip,
@@ -2836,6 +2832,13 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg )
else
admin_writeconfig();
+ G_LogPrintf( "ban: %i %i [%s] (%s) %s^7 %s^7\n",
+ g_admin_namelog[ logmatch ]->slot, seconds,
+ g_admin_namelog[ logmatch ]->ip,
+ g_admin_namelog[ logmatch ]->guid,
+ g_admin_namelog[ logmatch ]->name[ 0 ],
+ ( *reason ) ? reason : "banned by admin" );
+
if( g_admin_namelog[ logmatch ]->slot == -1 )
{
// client is already disconnected so stop here
@@ -2847,6 +2850,11 @@ qboolean G_admin_ban( gentity_t *ent, int skiparg )
( *reason ) ? reason : "banned by admin" ) );
return qtrue;
}
+ vic = &g_entities[ g_admin_namelog[ logmatch ]->slot ];
+ G_admin_autorevert( vic );
+
+ if( g_karma.integer )
+ vic->client->pers.karma -= 5000;
trap_SendServerCommand( g_admin_namelog[ logmatch ]->slot,
va( "disconnect \"You have been banned.\n"
@@ -3022,6 +3030,7 @@ qboolean G_admin_subnetban( gentity_t *ent, int skiparg )
ADMP( "^3!subnetban: ^7Only console may ban such a large network. Regular admins may only ban >=16.\n" );
return qfalse;
}
+
if( strcmp(exl, "!") )
{
ADMP( "^3!subnetban: ^1WARNING:^7 you are about to ban a large network, use !subnetban [ban] [mask] ! to force^7\n" );
@@ -3065,12 +3074,12 @@ qboolean G_admin_subnetban( gentity_t *ent, int skiparg )
{
Q_strncpyz(
cIPRlow,
- va("%u.%u.%u.%u", (IPRlow & (255 << 24)) >> 24, (IPRlow & (255 << 16)) >> 16, (IPRlow & (255 << 8)) >> 8, IPRlow & 255),
+ va("%i.%i.%i.%i", (IPRlow & (255 << 24)) >> 24, (IPRlow & (255 << 16)) >> 16, (IPRlow & (255 << 8)) >> 8, IPRlow & 255),
sizeof( cIPRlow )
);
Q_strncpyz(
cIPRhigh,
- va("%u.%u.%u.%u", (IPRhigh & (255 << 24)) >> 24, (IPRhigh & (255 << 16)) >> 16, (IPRhigh & (255 << 8)) >> 8, IPRhigh & 255),
+ va("%i.%i.%i.%i", (IPRhigh & (255 << 24)) >> 24, (IPRhigh & (255 << 16)) >> 16, (IPRhigh & (255 << 8)) >> 8, IPRhigh & 255),
sizeof( cIPRhigh )
);
}
@@ -3097,16 +3106,15 @@ qboolean G_admin_subnetban( gentity_t *ent, int skiparg )
return qtrue;
}
+
qboolean G_admin_suspendban( gentity_t *ent, int skiparg )
{
int bnum;
int length;
- int timenow = 0;
int expires = 0;
char *arg;
char bs[ 5 ];
char duration[ 32 ];
- qtime_t qt;
if( G_SayArgc() < 3 + skiparg )
{
@@ -3127,9 +3135,7 @@ qboolean G_admin_suspendban( gentity_t *ent, int skiparg )
}
arg = G_SayConcatArgs( 2 + skiparg );
- timenow = trap_RealTime( &qt );
length = G_admin_parse_time( arg );
-
if( length < 0 )
{
ADMP( "^3!suspendban: ^7invalid length\n" );
@@ -3140,16 +3146,13 @@ qboolean G_admin_suspendban( gentity_t *ent, int skiparg )
length = MAX_ADMIN_BANSUSPEND_DAYS * 24 * 60 * 60;
ADMP( va( "^3!suspendban: ^7maximum ban suspension is %d days\n",
MAX_ADMIN_BANSUSPEND_DAYS ) );
- } else if( g_admin_bans[ bnum - 1 ]->expires > 0 && length + timenow > g_admin_bans[ bnum - 1 ]->expires ) {
- length = g_admin_bans[ bnum - 1 ]->expires - timenow;
- G_admin_duration( length , duration, sizeof( duration ) );
- ADMP( va( "^3!suspendban: ^7Suspension Duration trimmed to Ban duration: %s\n",
- duration ) );
}
if ( length > 0 )
{
- expires = timenow + length;
+ qtime_t qt;
+
+ expires = trap_RealTime( &qt ) + length;
}
if( g_admin_bans[ bnum - 1 ]->suspend == expires )
{
@@ -3225,93 +3228,6 @@ qboolean G_admin_unban( gentity_t *ent, int skiparg )
return qtrue;
}
-qboolean G_admin_putteam( gentity_t *ent, int skiparg )
-{
- int pids[ MAX_CLIENTS ];
- char name[ MAX_NAME_LENGTH ], team[ 7 ], err[ MAX_STRING_CHARS ];
- gentity_t *vic;
- pTeam_t teamnum = PTE_NONE;
- char teamdesc[ 32 ] = {"spectators"};
- char secs[ 7 ];
- int seconds = 0;
- qboolean useDuration = qfalse;
-
- G_SayArgv( 1 + skiparg, name, sizeof( name ) );
- G_SayArgv( 2 + skiparg, team, sizeof( team ) );
- if( G_SayArgc() < 3 + skiparg )
- {
- ADMP( "^3!putteam: ^7usage: !putteam [name] [h|a|s] (duration)\n" );
- return qfalse;
- }
-
- if( G_ClientNumbersFromString( name, pids ) != 1 )
- {
- G_MatchOnePlayer( pids, err, sizeof( err ) );
- ADMP( va( "^3!putteam: ^7%s\n", err ) );
- return qfalse;
- }
- if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) )
- {
- ADMP( "^3!putteam: ^7sorry, but your intended victim has a higher "
- " admin level than you\n" );
- return qfalse;
- }
- vic = &g_entities[ pids[ 0 ] ];
-
- if ( vic->client->sess.invisible == qtrue )
- {
- ADMP( "^3!putteam: ^7invisible players cannot join a team\n" );
- return qfalse;
- }
-
- switch( team[ 0 ] )
- {
- case 'a':
- teamnum = PTE_ALIENS;
- Q_strncpyz( teamdesc, "aliens", sizeof( teamdesc ) );
- break;
- case 'h':
- teamnum = PTE_HUMANS;
- Q_strncpyz( teamdesc, "humans", sizeof( teamdesc ) );
- break;
- case 's':
- teamnum = PTE_NONE;
- break;
- default:
- ADMP( va( "^3!putteam: ^7unknown team %c\n", team[ 0 ] ) );
- return qfalse;
- }
- //duration code
- if( G_SayArgc() > 3 + skiparg ) {
- //can only lock players in spectator
- if ( teamnum != PTE_NONE )
- {
- ADMP( "^3!putteam: ^7You can only lock a player into the spectators team\n" );
- return qfalse;
- }
- G_SayArgv( 3 + skiparg, secs, sizeof( secs ) );
- seconds = G_admin_parse_time( secs );
- useDuration = qtrue;
- }
-
- if( vic->client->pers.teamSelection == teamnum && teamnum != PTE_NONE )
- {
- ADMP( va( "^3!putteam: ^7%s ^7is already on the %s team\n", vic->client->pers.netname, teamdesc ) );
- return qfalse;
- }
-
- if( useDuration == qtrue && seconds > 0 ) {
- vic->client->pers.specExpires = level.time + ( seconds * 1000 );
- }
- G_ChangeTeam( vic, teamnum );
-
- AP( va( "print \"^3!putteam: ^7%s^7 put %s^7 on to the %s team%s\n\"",
- ( ent ) ? G_admin_adminPrintName( ent ) : "console",
- vic->client->pers.netname, teamdesc,
- ( seconds ) ? va( " for %i seconds", seconds ) : "" ) );
- return qtrue;
-}
-
qboolean G_admin_seen(gentity_t *ent, int skiparg )
{
char name[ MAX_NAME_LENGTH ];
@@ -3362,11 +3278,8 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg )
if( i == id || (search[ 0 ] && strstr( name, search ) ) )
{
- if ( vic->client->sess.invisible == qfalse )
- {
- ADMBP( va( "^3%4d ^7%s^7 is currently playing\n", i, vic->client->pers.netname ) );
- count++;
- }
+ 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++ )
@@ -3384,11 +3297,8 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg )
if( !Q_stricmp( vic->client->pers.guid, g_admin_admins[ i ]->guid )
&& strstr( name, search ) )
{
- if ( vic->client->sess.invisible == qfalse )
- {
- ison = qtrue;
- break;
- }
+ ison = qtrue;
+ break;
}
}
@@ -3396,7 +3306,7 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg )
{
if( id == -1 )
continue;
- ADMBP( va( "^3%4d ^7%s^7 is currently playing\n",
+ ADMBP( va( "%4d %s^7 is currently playing\n",
i + MAX_CLIENTS, g_admin_admins[ i ]->name ) );
}
else
@@ -3422,10 +3332,42 @@ qboolean G_admin_seen(gentity_t *ent, int skiparg )
return qtrue;
}
-void G_admin_seen_update( char *guid )
+void G_admin_karma_sync( void )
{
+ gclient_t *p;
+ int i, j;
+
+ if( !g_karma.integer )
+ return;
+
+ for( i = 0; i < level.maxclients; i++ )
+ {
+ p = &level.clients[ i ];
+
+ if( p->pers.connected != CON_CONNECTED &&
+ p->pers.connected != CON_CONNECTING )
+ continue;
+ if( p->pers.adminLevel < 1 )
+ continue;
+
+ for( j = 0; j < MAX_ADMIN_ADMINS && g_admin_admins[ j ]; j++ )
+ {
+ if( !Q_stricmp( g_admin_admins[ j ]->guid, p->pers.guid ) )
+ {
+ if( p->pers.karma )
+ g_admin_admins[ j ]->karma = p->pers.karma;
+ break;
+ }
+ }
+ }
+}
+
+void G_admin_seen_update( gclient_t *client, qboolean disconnect )
+{
+ char *guid;
int i;
+ guid = client->pers.guid;
for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ )
{
if( !Q_stricmp( g_admin_admins[ i ]->guid, guid ) )
@@ -3433,11 +3375,198 @@ void G_admin_seen_update( char *guid )
qtime_t qt;
g_admin_admins[ i ]->seen = trap_RealTime( &qt );
+ if ( disconnect && client->pers.karma )
+ g_admin_admins[ i ]->karma = client->pers.karma;
return;
}
}
}
+typedef struct
+{
+ g_admin_admin_t *admin;
+ int id;
+}
+g_admin_sort_t;
+
+static int SortSeenTimes( const void *av, const void *bv)
+{
+ const g_admin_sort_t *a = av;
+ const g_admin_sort_t *b = bv;
+
+ if( !a->admin || !b->admin )
+ return 0;
+
+ if( a->admin->seen > b->admin->seen )
+ return 1;
+ if( a->admin->seen < b->admin->seen )
+ return -1;
+
+ return 0;
+}
+
+qboolean G_admin_expire( gentity_t *ent, int skiparg )
+{
+ g_admin_sort_t sort_list[ MAX_ADMIN_ADMINS ];
+ char arg[ MAX_ADMIN_CMD_LEN ];
+ qboolean confirm = qfalse;
+ qtime_t qt;
+ int t;
+ int count = 0;
+ int max = 5;
+ int i;
+
+ if( g_adminExpireTime.integer < 1 )
+ {
+ ADMP( "^3!expire: ^7expire is disabled\n" );
+ return qfalse;
+ }
+
+ if( G_SayArgc() > 1 + skiparg )
+ {
+ G_SayArgv( skiparg + 1, arg, sizeof( arg ) );
+ if( !Q_stricmp( arg, "confirm" ) )
+ confirm = qtrue;
+ }
+
+ t = trap_RealTime( &qt );
+
+ memset( sort_list, 0, sizeof( sort_list ) );
+ for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ] ; i++ )
+ {
+ sort_list[ i ].admin = g_admin_admins[ i ];
+ sort_list[ i ].id = MAX_CLIENTS + i;
+ count++;
+ }
+
+ qsort( sort_list, count, sizeof( sort_list[ 0 ] ), SortSeenTimes );
+
+ ADMBP_begin();
+ for( i = 0; i < count && max > 0 ; i++ )
+ {
+ if( !sort_list[ i ].admin )
+ continue;
+ if( sort_list[ i ].admin->level != 1 )
+ continue;
+ if( t - sort_list[ i ].admin->seen > g_adminExpireTime.integer * 86400 )
+ {
+ char sduration[ 32 ];
+
+ max--;
+ G_admin_duration( t - sort_list[ i ].admin->seen, sduration, sizeof( sduration ) );
+
+ if( confirm )
+ {
+ trap_SendConsoleCommand( EXEC_APPEND, va( "!setlevel %d 0;", sort_list[ i ].id ) );
+ }
+ else
+ {
+ ADMBP( va( " ^7%d %s^7 last seen %s%s\n",
+ sort_list[ i ].id,
+ sort_list[ i ].admin->name,
+ (sort_list[ i ].admin->seen ) ? sduration : "",
+ (sort_list[ i ].admin->seen ) ? " ago" : "time is unknown" ) );
+ }
+ }
+ }
+
+ ADMBP( va( "^3!expire: ^7%s %d level 1 admin(s) older than %d days\n",
+ ( confirm ) ? "expired" : "listed",
+ 5 - max, g_adminExpireTime.integer ) );
+ if ( !confirm )
+ ADMBP( "^3!expire: ^7to make this permanent use '^2!expire confirm^7'\n" );
+
+ ADMBP_end();
+ return qtrue;
+}
+
+qboolean G_admin_putteam( gentity_t *ent, int skiparg )
+{
+ int pids[ MAX_CLIENTS ];
+ char name[ MAX_NAME_LENGTH ], team[ 7 ], err[ MAX_STRING_CHARS ];
+ gentity_t *vic;
+ pTeam_t teamnum = PTE_NONE;
+ char teamdesc[ 32 ] = {"spectators"};
+ char secs[ 7 ];
+ int seconds = 0;
+ qboolean useDuration = qfalse;
+
+ G_SayArgv( 1 + skiparg, name, sizeof( name ) );
+ G_SayArgv( 2 + skiparg, team, sizeof( team ) );
+ if( G_SayArgc() < 3 + skiparg )
+ {
+ ADMP( "^3!putteam: ^7usage: !putteam [name] [h|a|s] (duration)\n" );
+ return qfalse;
+ }
+
+ if( G_ClientNumbersFromString( name, pids ) != 1 )
+ {
+ G_MatchOnePlayer( pids, err, sizeof( err ) );
+ ADMP( va( "^3!putteam: ^7%s\n", err ) );
+ return qfalse;
+ }
+ if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) )
+ {
+ ADMP( "^3!putteam: ^7sorry, but your intended victim has a higher "
+ " admin level than you\n" );
+ return qfalse;
+ }
+ vic = &g_entities[ pids[ 0 ] ];
+
+ if ( vic->client->sess.invisible == qtrue )
+ {
+ ADMP( "^3!putteam: ^7invisible players cannot join a team\n" );
+ return qfalse;
+ }
+
+ switch( team[ 0 ] )
+ {
+ case 'a':
+ teamnum = PTE_ALIENS;
+ Q_strncpyz( teamdesc, "aliens", sizeof( teamdesc ) );
+ break;
+ case 'h':
+ teamnum = PTE_HUMANS;
+ Q_strncpyz( teamdesc, "humans", sizeof( teamdesc ) );
+ break;
+ case 's':
+ teamnum = PTE_NONE;
+ break;
+ default:
+ ADMP( va( "^3!putteam: ^7unknown team %c\n", team[ 0 ] ) );
+ return qfalse;
+ }
+ //duration code
+ if( G_SayArgc() > 3 + skiparg ) {
+ //can only lock players in spectator
+ if ( teamnum != PTE_NONE )
+ {
+ ADMP( "^3!putteam: ^7You can only lock a player into the spectators team\n" );
+ return qfalse;
+ }
+ G_SayArgv( 3 + skiparg, secs, sizeof( secs ) );
+ seconds = G_admin_parse_time( secs );
+ useDuration = qtrue;
+ }
+
+ if( vic->client->pers.teamSelection == teamnum && teamnum != PTE_NONE )
+ {
+ ADMP( va( "^3!putteam: ^7%s ^7is already on the %s team\n", vic->client->pers.netname, teamdesc ) );
+ return qfalse;
+ }
+
+ if( useDuration == qtrue && seconds > 0 ) {
+ vic->client->pers.specExpires = level.time + ( seconds * 1000 );
+ }
+ G_ChangeTeam( vic, teamnum );
+
+ AP( va( "print \"^3!putteam: ^7%s^7 put %s^7 on to the %s team%s\n\"",
+ ( ent ) ? G_admin_adminPrintName( ent ) : "console",
+ vic->client->pers.netname, teamdesc,
+ ( seconds ) ? va( " for %i seconds", seconds ) : "" ) );
+ return qtrue;
+}
+
void G_admin_adminlog_cleanup( void )
{
int i;
@@ -3469,7 +3598,6 @@ void G_admin_adminlog_log( gentity_t *ent, char *command, char *args, int skipar
!Q_stricmp( command, "listplayers" ) ||
!Q_stricmp( command, "namelog" ) ||
!Q_stricmp( command, "showbans" ) ||
- !Q_stricmp( command, "seen" ) ||
!Q_stricmp( command, "time" ) )
return;
@@ -3722,6 +3850,261 @@ qboolean G_admin_adminlog( gentity_t *ent, int skiparg )
return qtrue;
}
+void G_admin_tklog_cleanup( void )
+{
+ int i;
+
+ for( i = 0; i < MAX_ADMIN_TKLOGS && g_admin_tklog[ i ]; i++ )
+ {
+ G_Free( g_admin_tklog[ i ] );
+ g_admin_tklog[ i ] = NULL;
+ }
+
+ admin_tklog_index = 0;
+}
+
+void G_admin_tklog_log( gentity_t *attacker, gentity_t *victim, int meansOfDeath )
+{
+ g_admin_tklog_t *tklog;
+ int previous;
+ int count = 1;
+
+ if( !attacker )
+ return;
+
+ previous = admin_tklog_index - 1;
+ if( previous < 0 )
+ previous = MAX_ADMIN_TKLOGS - 1;
+
+ if( g_admin_tklog[ previous ] )
+ count = g_admin_tklog[ previous ]->id + 1;
+
+ if( g_admin_tklog[ admin_tklog_index ] )
+ tklog = g_admin_tklog[ admin_tklog_index ];
+ else
+ tklog = G_Alloc( sizeof( g_admin_tklog_t ) );
+
+ memset( tklog, 0, sizeof( g_admin_tklog_t ) );
+ tklog->id = count;
+ tklog->time = level.time - level.startTime;
+ Q_strncpyz( tklog->name, attacker->client->pers.netname, sizeof( tklog->name ) );
+
+ if( victim )
+ {
+ Q_strncpyz( tklog->victim, victim->client->pers.netname, sizeof( tklog->victim ) );
+ tklog->damage = victim->client->tkcredits[ attacker->s.number ];
+ tklog->value = victim->client->ps.stats[ STAT_MAX_HEALTH ];
+ }
+ else
+ {
+ Q_strncpyz( tklog->victim, "^3BLEEDING", sizeof( tklog->victim ) );
+ tklog->damage = attacker->client->pers.statscounters.spreebleeds;
+ tklog->value = g_bleedingSpree.integer * 100;
+ }
+
+ tklog->team = attacker->client->ps.stats[ STAT_PTEAM ];
+ if( meansOfDeath == MOD_GRENADE )
+ tklog->weapon = WP_GRENADE;
+ else if( tklog->team == PTE_HUMANS )
+ tklog->weapon = attacker->s.weapon;
+ else
+ tklog->weapon = attacker->client->ps.stats[ STAT_PCLASS ];
+
+ g_admin_tklog[ admin_tklog_index ] = tklog;
+ admin_tklog_index++;
+ if( admin_tklog_index >= MAX_ADMIN_TKLOGS )
+ admin_tklog_index = 0;
+}
+
+qboolean G_admin_tklog( gentity_t *ent, int skiparg )
+{
+ g_admin_tklog_t *results[ 10 ];
+ int result_index = 0;
+ char *search_name = NULL;
+ int index;
+ int skip = 0;
+ int skipped = 0;
+ int checked = 0;
+ char n1[ MAX_NAME_LENGTH ];
+ char fmt_name[ 16 ];
+ char argbuf[ 32 ];
+ char *weaponName;
+ int name_length = 10;
+ int max_id = 0;
+ int i;
+ qboolean match;
+
+ memset( results, 0, sizeof( results ) );
+
+ index = admin_tklog_index;
+ for( i = 0; i < 10; i++ )
+ {
+ int prev;
+
+ prev = index - 1;
+ if( prev < 0 )
+ prev = MAX_ADMIN_TKLOGS - 1;
+ if( !g_admin_tklog[ prev ] )
+ break;
+ if( g_admin_tklog[ prev ]->id > max_id )
+ max_id = g_admin_tklog[ prev ]->id;
+ index = prev;
+ }
+
+ if( G_SayArgc() > 1 + skiparg )
+ {
+ G_SayArgv( 1 + skiparg, argbuf, sizeof( argbuf ) );
+ if( ( *argbuf >= '0' && *argbuf <= '9' ) || *argbuf == '-' )
+ {
+ int id;
+
+ id = atoi( argbuf );
+ if( id < 0 )
+ id += ( max_id - 9 );
+ else if( id <= max_id - MAX_ADMIN_TKLOGS )
+ id = max_id - MAX_ADMIN_TKLOGS + 1;
+
+ if( id + 9 >= max_id )
+ id = max_id - 9;
+ if( id < 1 )
+ id = 1;
+ for( i = 0; i < MAX_ADMIN_TKLOGS; i++ )
+ {
+ if( g_admin_tklog[ i ]->id == id )
+ {
+ index = i;
+ break;
+ }
+ }
+ }
+ else
+ {
+ search_name = argbuf;
+ }
+
+ if( G_SayArgc() > 2 + skiparg && ( search_name ) )
+ {
+ char skipbuf[ 4 ];
+ G_SayArgv( 2 + skiparg, skipbuf, sizeof( skipbuf ) );
+ skip = atoi( skipbuf );
+ }
+ }
+
+ if( search_name )
+ {
+ g_admin_tklog_t *result_swap[ 10 ];
+
+ memset( &result_swap, 0, sizeof( result_swap ) );
+
+ index = admin_tklog_index - 1;
+ if( index < 0 )
+ index = MAX_ADMIN_TKLOGS - 1;
+
+ while( g_admin_tklog[ index ] &&
+ checked < MAX_ADMIN_TKLOGS &&
+ result_index < 10 )
+ {
+ match = qfalse;
+
+ G_SanitiseString( g_admin_tklog[ index ]->name, n1, sizeof( n1 ) );
+ if( strstr( n1, search_name ) )
+ match = qtrue;
+
+ if( match && skip > 0 )
+ {
+ match = qfalse;
+ skip--;
+ skipped++;
+ }
+ if( match )
+ {
+ result_swap[ result_index ] = g_admin_tklog[ index ];
+ result_index++;
+ }
+
+ checked++;
+ index--;
+ if( index < 0 )
+ index = MAX_ADMIN_TKLOGS - 1;
+ }
+ // search runs backwards, turn it around
+ for( i = 0; i < result_index; i++ )
+ results[ i ] = result_swap[ result_index - i - 1 ];
+ }
+ else
+ {
+ while( g_admin_tklog[ index ] && result_index < 10 )
+ {
+ results[ result_index ] = g_admin_tklog[ index ];
+ result_index++;
+ index++;
+ if( index >= MAX_ADMIN_TKLOGS )
+ index = 0;
+ }
+ }
+
+ for( i = 0; results[ i ] && i < 10; i++ )
+ {
+ int l;
+
+ G_DecolorString( results[ i ]->name, n1 );
+ l = strlen( n1 );
+ if( l > name_length )
+ name_length = l;
+ }
+ ADMBP_begin( );
+ for( i = 0; results[ i ] && i < 10; i++ )
+ {
+ int t;
+
+ t = results[ i ]->time / 1000;
+
+ G_DecolorString( results[ i ]->name, n1 );
+ Com_sprintf( fmt_name, sizeof( fmt_name ), "%%%ds",
+ ( name_length + strlen( results[ i ]->name ) - strlen( n1 ) ) );
+ Com_sprintf( n1, sizeof( n1 ), fmt_name, results[ i ]->name );
+
+ if( results[ i ]->team == PTE_HUMANS )
+ weaponName = BG_FindNameForWeapon( results[ i ]->weapon );
+ else
+ weaponName = BG_FindNameForClassNum( results[ i ]->weapon );
+
+ ADMBP( va( "^7%3d %3d:%02d %s^7 %3d / %3d %10s %s^7\n",
+ results[ i ]->id,
+ t / 60, t % 60,
+ n1,
+ results[ i ]->damage,
+ results[ i ]->value,
+ weaponName,
+ results[ i ]->victim ) );
+ }
+ if( search_name )
+ {
+ ADMBP( va( "^3!tklog:^7 Showing %d matches for '%s^7'.",
+ result_index,
+ argbuf ) );
+ if( checked < MAX_ADMIN_TKLOGS && g_admin_tklog[ checked ] )
+ ADMBP( va( " run '!tklog %s^7 %d' to see more",
+ argbuf,
+ skipped + result_index ) );
+ ADMBP( "\n" );
+ }
+ else if ( results[ 0 ] )
+ {
+ ADMBP( va( "^3!tklog:^7 Showing %d - %d of %d.\n",
+ results[ 0 ]->id,
+ results[ result_index - 1 ]->id,
+ max_id ) );
+ }
+ else
+ {
+ ADMBP( "^3!tklog:^7 log is empty.\n" );
+ }
+ ADMBP_end( );
+
+ return qtrue;
+}
+
qboolean G_admin_map( gentity_t *ent, int skiparg )
{
char map[ MAX_QPATH ];
@@ -3947,6 +4330,9 @@ qboolean G_admin_maplog( gentity_t *ent, int skiparg )
case 'M':
result = "^6admin changed map";
break;
+ case 'l':
+ result = "^2layout vote";
+ break;
case 'D':
result = "^6admin loaded devmap";
break;
@@ -3986,6 +4372,10 @@ qboolean G_admin_maplog( gentity_t *ent, int skiparg )
ptr = end;
count++;
}
+
+ if( g_nextMap.string[ 0 ] )
+ ADMBP( va( "^5NextMap override: %s\n", g_nextMap.string ) );
+
ADMBP_end( );
return qtrue;
@@ -4013,7 +4403,7 @@ qboolean G_admin_demo( gentity_t *ent, int skiparg )
{
if( !ent )
{
- ADMP( "!demo: console can not use demo.\n" );
+ ADMP( "!demo: console can not use demo.\n");
return qfalse;
}
@@ -4415,29 +4805,34 @@ qboolean G_admin_listadmins( gentity_t *ent, int skiparg )
int start = 0;
qboolean numeric = qtrue;
int drawn = 0;
- int minlevel = 1;
+ int minlevel = 0;
+ char *direction = "(all levels)";
if( G_SayArgc() == 3 + skiparg )
{
G_SayArgv( 2 + skiparg, s, sizeof( s ) );
- if( s[ 0 ] >= '0' && s[ 0] <= '9' )
+ if( ( s[ 0 ] >= '0' && s[ 0] <= '9' ) || s[ 0 ] == '-' )
{
minlevel = atoi( s );
- if( minlevel < 1 )
- minlevel = 1;
+ if( minlevel > 0 )
+ direction = "or greater";
+ else if( minlevel < 0)
+ direction = "or lesser";
}
}
for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ )
{
- if( g_admin_admins[ i ]->level >= minlevel )
+ if( ( minlevel == 0 && g_admin_admins[ i ]->level != 0 ) ||
+ ( minlevel > 0 && g_admin_admins[ i ]->level >= minlevel ) ||
+ ( minlevel < 0 && g_admin_admins[ i ]->level <= minlevel ) )
found++;
}
if( !found )
{
- if( minlevel > 1 )
+ if( minlevel != 0 )
{
- ADMP( va( "^3!listadmins: ^7no admins level %i or greater found\n", minlevel ) );
+ ADMP( va( "^3!listadmins: ^7no admins level %i %s found\n", minlevel, direction ) );
}
else
{
@@ -4476,27 +4871,28 @@ qboolean G_admin_listadmins( gentity_t *ent, int skiparg )
{
if( drawn <= 20 )
{
- ADMP( va( "^3!listadmins:^7 found %d admins level %i or greater matching '%s^7'\n",
- drawn, minlevel, search ) );
+ ADMP( va( "^3!listadmins:^7 found %d admins level %i %s matching '%s^7'\n",
+ drawn, minlevel, direction, search ) );
}
else
{
- ADMP( va( "^3!listadmins:^7 found >20 admins level %i or greater matching '%s^7. Try a more narrow search.'\n",
- minlevel, search ) );
+ ADMP( va( "^3!listadmins:^7 found >20 admins level %i %s matching '%s^7. Try a more narrow search.'\n",
+ minlevel, direction, search ) );
}
}
else
{
ADMBP_begin();
- ADMBP( va( "^3!listadmins:^7 showing admins level %i or greater %d - %d of %d. ",
+ ADMBP( va( "^3!listadmins:^7 showing admins level %i %s %d - %d of %d. ",
minlevel,
+ direction,
( found ) ? ( start + 1 ) : 0,
( ( start + MAX_ADMIN_LISTITEMS ) > found ) ?
found : ( start + MAX_ADMIN_LISTITEMS ),
found ) );
if( ( start + MAX_ADMIN_LISTITEMS ) < found )
{
- if( minlevel > 1)
+ if( minlevel != 0 )
{
ADMBP( va( "run '!listadmins %d %d' to see more",
( start + MAX_ADMIN_LISTITEMS + 1 ), minlevel ) );
@@ -4564,27 +4960,29 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg )
char lname[ MAX_NAME_LENGTH ];
char lname2[ MAX_NAME_LENGTH ];
char guid_stub[ 9 ];
- char muted[ 2 ], denied[ 2 ], dbuilder[ 2 ], misc[ 2 ];
+ char muted[ 2 ], denied[ 2 ], dbuilder[ 2 ], immune[ 2 ];
int l;
char lname_fmt[ 5 ];
+ char karma[ 8 ];
+ //get amount of invisible players
for( i = 0; i < level.maxclients; i++ ) {
p = &level.clients[ i ];
if ( p->sess.invisible == qtrue )
invisiblePlayers++;
}
-
+
ADMBP_begin();
- ADMBP( va( "^3!listplayers^7: %i players connected:\n",
+ ADMBP( va( "^3!listplayers^7: %d players connected:\n",
level.numConnectedClients - invisiblePlayers ) );
for( i = 0; i < level.maxclients; i++ )
{
p = &level.clients[ i ];
-
+
// Ignore invisible players
if ( p->sess.invisible == qtrue )
continue;
-
+
Q_strncpyz( t, "S", sizeof( t ) );
Q_strncpyz( c, S_COLOR_YELLOW, sizeof( c ) );
if( p->pers.teamSelection == PTE_HUMANS )
@@ -4613,6 +5011,10 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg )
guid_stub[ j ] = '\0';
muted[ 0 ] = '\0';
+ if( G_admin_permission( &g_entities[ i ], ADMF_NO_VOTE ) )
+ {
+ Q_strncpyz( muted, "V", sizeof( muted ) );
+ }
if( p->pers.muted )
{
Q_strncpyz( muted, "M", sizeof( muted ) );
@@ -4641,15 +5043,16 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg )
Q_strncpyz( dbuilder, "D", sizeof( dbuilder ) );
}
}
-
- misc[ 0 ] = '\0';
+ immune[ 0 ] = '\0';
if( G_admin_permission( &g_entities[ i ], ADMF_BAN_IMMUNITY ) )
{
- // use Misc slot for Immunity player status
- Q_strncpyz( misc, "I", sizeof( misc ) );
- } else if( p->pers.paused ) {
- // use Misc slot for paused player status
- Q_strncpyz( misc, "P", sizeof( misc ) );
+ Q_strncpyz( immune, "I", sizeof( immune ) );
+ }
+
+ if( p->pers.paused )
+ {
+ // use immunity slot for paused player status
+ Q_strncpyz( immune, "L", sizeof( immune ) );
}
l = 0;
@@ -4693,19 +5096,25 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg )
}
+ if( g_karma.integer )
+ Com_sprintf( karma, sizeof( karma ), " %4i", p->pers.karma / 1000 );
+ else
+ karma[ 0 ] = '\0';
+
if( G_admin_permission(ent, ADMF_SEESFULLLISTPLAYERS ) ) {
- ADMBP( va( "%2i %s%s^7 %-2i %s^7 (*%s) ^1%1s%1s%1s%1s^7 %s^7 %s%s^7%s\n",
+ ADMBP( va( "%2i %s%s^7%s %-2i %s^7 (*%s) ^1%1s%1s%1s%1s^7 %s^7 %s%s^7%s\n",
i,
c,
t,
+ karma,
l,
( *lname ) ? lname2 : "",
guid_stub,
+ immune,
muted,
dbuilder,
denied,
- misc,
p->pers.netname,
( *n ) ? "(a.k.a. " : "",
n,
@@ -4714,10 +5123,11 @@ qboolean G_admin_listplayers( gentity_t *ent, int skiparg )
}
else
{
- ADMBP( va( "%2i %s%s^7 ^1%1s%1s%1s^7 %s^7\n",
+ ADMBP( va( "%2i %s%s^7%s ^1%1s%1s%1s^7 %s^7\n",
i,
c,
t,
+ karma,
muted,
dbuilder,
denied,
@@ -4870,52 +5280,6 @@ qboolean G_admin_listrotation( gentity_t *ent, int skiparg )
statusColor = 7;
status = "vote";
}
- } else if( !Q_stricmp( mapRotations.rotations[ i ].maps[ j ].name, "*RANDOM*" ) )
- {
- char slotMap[ 64 ];
- int lineLen = 0;
- int k;
-
- trap_Cvar_VariableStringBuffer( "mapname", slotMap, sizeof( slotMap ) );
- mapnames[ 0 ] = '\0';
- for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numConditions; k++ )
- {
- char *thisMap;
- int mc = 7;
-
- if( mapRotations.rotations[ i ].maps[ j ].conditions[ k ].lhs != MCV_SELECTEDRANDOM )
- continue;
-
- thisMap = mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest;
- lineLen += strlen( thisMap ) + 1;
-
- if( currentMap == j && !Q_stricmp( thisMap, slotMap ) )
- mc = 3;
- Q_strcat( mapnames, sizeof( mapnames ), va( "^7%s%s^%i%s",
- ( k ) ? ", " : "",
- ( lineLen > 50 ) ? "\n " : "",
- mc, thisMap ) );
- if( lineLen > 50 )
- lineLen = strlen( thisMap ) + 2;
- else
- lineLen++;
- }
-
- if( currentMap == j )
- {
- statusColor = 3;
- status = "current slot";
- }
- else if( !k )
- {
- statusColor = 1;
- status = "empty Random";
- }
- else
- {
- statusColor = 7;
- status = "Random";
- }
}
else if ( currentMap == j )
{
@@ -4980,16 +5344,13 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )
char name_match[ MAX_NAME_LENGTH ] = {""};
int show_count = 0;
qboolean subnetfilter = qfalse;
+ int line_color;
+ int dummy;
t = trap_RealTime( NULL );
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;
- }
found++;
}
@@ -5067,7 +5428,6 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )
else
{
int mask = -1;
- int dummy;
int scanflen = 0;
scanflen = sscanf( g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &dummy, &dummy, &dummy, &dummy, &mask );
if( scanflen == 5 && mask < 32 )
@@ -5114,7 +5474,6 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )
else
{
int mask = -1;
- int dummy;
int scanflen = 0;
scanflen = sscanf( g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &dummy, &dummy, &dummy, &dummy, &mask );
if( scanflen != 5 || mask >= 32 )
@@ -5141,42 +5500,54 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )
made++;
}
- if( g_admin_bans[ i ]->expires != 0
- && ( g_admin_bans[ i ]->expires - t ) < 1 )
+ if( g_admin_bans[ i ]->expires == 0 ||
+ g_admin_bans[ i ]->expires > t )
{
- Com_sprintf( duration, sizeof( duration ), "^1*EXPIRED*^7" );
- } else {
secs = ( g_admin_bans[ i ]->expires - t );
G_admin_duration( secs, duration, sizeof( duration ) );
+ if( sscanf( g_admin_bans[ i ]->ip, "%d.%d.%d.%d/%d", &dummy, &dummy, &dummy, &dummy, &dummy ) != 4 )
+ line_color = 1;
+ else if( g_admin_bans[ i ]->suspend > t )
+ line_color = 5;
+ else
+ line_color = 7;
+ }
+ else
+ {
+ Q_strncpyz( duration, "expired", sizeof( duration ) );
+ line_color = 5;
}
suspended[ 0 ] = '\0';
if( g_admin_bans[ i ]->suspend > t )
{
G_admin_duration( g_admin_bans[ i ]->suspend - t, sduration, sizeof( sduration ) );
- Com_sprintf( suspended, sizeof( suspended ), "^3*SUSPENDED*^7 for %s^7",
+ Com_sprintf( suspended, sizeof( suspended ), "^5 | - SUSPENDED for %s\n",
sduration );
}
G_DecolorString( g_admin_bans[ i ]->name, n1 );
- Com_sprintf( name_fmt, sizeof( name_fmt ), "%%%is",
+ Com_sprintf( name_fmt, sizeof( name_fmt ), "%%-%is",
( max_name + strlen( g_admin_bans[ i ]->name ) - strlen( n1 ) ) );
Com_sprintf( n1, sizeof( n1 ), name_fmt, g_admin_bans[ i ]->name );
G_DecolorString( g_admin_bans[ i ]->banner, n2 );
- Com_sprintf( banner_fmt, sizeof( banner_fmt ), "%%%is",
+ Com_sprintf( banner_fmt, sizeof( banner_fmt ), "%%-%is",
( max_banner + strlen( g_admin_bans[ i ]->banner ) - strlen( n2 ) ) );
- Com_sprintf( n2, sizeof( n2 ), banner_fmt, g_admin_bans[ i ]->banner );
+ Com_sprintf( n2, sizeof( n2 ), banner_fmt, g_admin_bans[ i ]->banner );
bannerslevel = g_admin_bans[ i ]->bannerlevel;
- ADMBP( va( "%4i %s^7 %-15s %-8s %-10s\n | %-15s^7 Level:%2i\n | %s\n \\__ %s\n",
+ ADMBP( va( "^%i%4i ^7%s^%i %-15s %-8s ^7%s^7 %2i^%i %-10s\n%s^5 \\__ %s\n",
+ line_color,
( i + 1 ),
n1,
+ line_color,
g_admin_bans[ i ]->ip,
date,
- duration,
n2,
bannerslevel,
+ line_color,
+ duration,
suspended,
g_admin_bans[ i ]->reason ) );
@@ -5209,8 +5580,9 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )
if( ( start + MAX_ADMIN_SHOWBANS ) < found )
{
- ADMBP( va( "run !showbans %d %s to see more",
+ ADMBP( va( "run !showbans %d%s%s to see more",
( start + MAX_ADMIN_SHOWBANS + 1 ),
+ (filter[0]) ? " " : "",
(filter[0]) ? filter : "" ) );
}
ADMBP( "\n" );
@@ -5221,8 +5593,7 @@ qboolean G_admin_showbans( gentity_t *ent, int skiparg )
qboolean G_admin_help( gentity_t *ent, int skiparg )
{
int i;
- int count = 0;
- int commandsPerLine = 6;
+ char additional[ MAX_STRING_CHARS ] = "";
if( G_SayArgc() < 2 + skiparg )
{
@@ -5259,102 +5630,39 @@ qboolean G_admin_help( gentity_t *ent, int skiparg )
j = 0;
}
}
-
+
+ if( ent && g_markDeconstruct.integer == 2 )
+ strcat( additional, " /mark" );
+ if( ent )
+ strcat( additional, " /builder /say_area" );
+ if( g_publicSayadmins.integer || G_admin_permission( ent, ADMF_ADMINCHAT ) )
+ strcat( additional, " /a /say_admins" );
+ if( g_privateMessages.integer )
+ strcat( additional, " /m" );
+ if( ent && g_actionPrefix.string[0] )
+ strcat( additional, " /me /mt /me_team" );
+ if( ent && g_myStats.integer )
+ strcat( additional, " /mystats" );
+ if( ent && g_teamStatus.integer )
+ strcat( additional, " /teamstatus" );
+ if( ent && ent->client )
+ {
+ if( ent->client->pers.designatedBuilder )
+ {
+ strcat( additional, " /protect /resign" );
+ }
+ }
+ if( ent && g_allowShare.integer )
+ strcat( additional, " /share /donate" );
+
if( count )
ADMBP( "\n" );
ADMBP( va( "^3!help: ^7%i available commands\n", count ) );
ADMBP( "run !help [^3command^7] for help with a specific command.\n" );
- ADMBP( "The following non-standard /commands may also be available to you: \n" );
- count = 1;
-
- if( ent && g_AllStats.integer ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "allstats" ) );
- count++;
- }
- if ( ent ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "builder" ) );
- count++;
- }
- if( ent && g_allowVote.integer && G_admin_permission( ent, ADMF_NO_VOTE ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "callvote" ) );
- count++;
- }
- if( ent && g_allowVote.integer && G_admin_permission( ent, ADMF_NO_VOTE ) && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "callteamvote" ) );
- count++;
- }
- if( ent && g_allowShare.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "donate" ) );
- count++;
- }
- if( g_privateMessages.integer ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "m" ) );
- count++;
- }
- if( ent && g_markDeconstruct.integer == 2 && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "mark" ) );
- count++;
- }
- if( ent && g_actionPrefix.string[0] ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "me" ) );
- count++;
- }
- if( ent && g_actionPrefix.string[0] ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "me_team" ) );
- count++;
- }
- if( ent && g_actionPrefix.string[0] && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "mt" ) );
- count++;
- }
- if( ent && g_myStats.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "mystats" ) );
- count++;
- }
- if( ent->client->pers.designatedBuilder && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "protect" ) );
- count++;
- }
- if( ent->client->pers.designatedBuilder && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "resign" ) );
- count++;
- }
- if( g_publicSayadmins.integer || G_admin_permission( ent, ADMF_ADMINCHAT ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "say_admins" ) );
- count++;
- }
- if( ent && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "say_area" ) );
- count++;
- }
- if( ent && g_allowShare.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "share" ) );
- count++;
- }
- if( ent && g_teamStatus.integer && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ) {
- if( count > commandsPerLine && ( count % commandsPerLine ) == 1 ) ADMBP( "\n" );
- ADMBP( va( "^5/%-12s", "teamstatus" ) );
- count++;
- }
- ADMBP( "\n" );
+ ADMBP( va( "\nThe following non-standard /commands may also be available to you: \n^3%s\n",
+ additional ) );
ADMBP_end();
-
+
return qtrue;
}
else
@@ -5490,6 +5798,10 @@ qboolean G_admin_cancelvote( gentity_t *ent, int skiparg )
ADMP( "^3!cancelvote^7: no vote in progress\n" );
return qfalse;
}
+ if( !Q_strncmp( level.voteDisplayString, "Extend", 6 ) &&
+ level.extend_vote_count > 0 )
+ level.extend_vote_count--;
+
level.voteNo = level.numConnectedClients;
level.voteYes = 0;
CheckVote( );
@@ -5679,11 +5991,81 @@ qboolean G_admin_pause( gentity_t *ent, int skiparg )
vic->client->pers.netname,
( ent ) ? ent->client->pers.netname : "console" ) );
}
- ClientUserinfoChanged( pids[ i ], qfalse );
}
return qtrue;
}
+qboolean G_admin_practice( gentity_t *ent, int skiparg )
+{
+ char clantag[ MAX_NAME_LENGTH ];
+ char mapsarg[ 8 ];
+ int maps;
+
+ if( G_SayArgc() < 2 + skiparg )
+ {
+ if( g_practiceCount.integer )
+ {
+ AP( va( "print \"^3practice:^7 practice mode is in effect for the next %d maps\n\"",
+ g_practiceCount.integer ) );
+ ADMP( va( "^3!practice: ^7practice mode set to %s^7 for next %d maps\n",
+ g_practiceText.string, g_practiceCount.integer ) );
+ }
+ else
+ {
+ ADMP( "^3!practice: ^7practice mode is off\n" );
+ }
+ return qfalse;
+ }
+
+ G_SayArgv( 1 + skiparg, clantag, sizeof( clantag ) );
+
+ if( G_SayArgc() < 3 + skiparg )
+ {
+ if( !Q_stricmp( clantag, "off" ) )
+ {
+ if( g_practiceCount.integer )
+ {
+ trap_Cvar_Set( "g_practiceCount", "0" );
+ AP( va ("print \"^3!practice: ^7 practice mode turned off by %s^7\n\"",
+ ( ent ) ? ent->client->pers.netname : "console" ) );
+ ADMP( "^3!practice: ^7practice mode set to off\n" );
+ return qtrue;
+ }
+ else
+ {
+ ADMP( "^3!practice: ^7practice mode already off\n" );
+ }
+ }
+ else
+ {
+ ADMP( "^3!practice: ^7usage: practice [clan tag] [map count]\n" );
+ }
+ return qfalse;
+ }
+ G_SayArgv( 2 + skiparg, mapsarg, sizeof( mapsarg ) );
+ maps = atoi( mapsarg );
+
+ if( !clantag[ 0 ] )
+ {
+ ADMP( "^3!practice: ^7no clan tag specified\n" );
+ return qfalse;
+ }
+ if( maps < 1 || maps > 8 )
+ {
+ ADMP( "^3!practice: ^7map count must be between 1 and 8\n" );
+ return qfalse;
+ }
+
+ trap_Cvar_Set( "g_practiceText", clantag );
+ trap_Cvar_Set( "g_practiceCount", va( "%d", maps ) );
+
+ AP( va( "print \"^3practice:^7 %s^7 has activated practice mode for the next %d maps\n\"",
+ ( ent ) ? ent->client->pers.netname : "console",
+ maps ) );
+
+ return qtrue;
+}
+
qboolean G_admin_spec999( gentity_t *ent, int skiparg )
{
int i;
@@ -5711,13 +6093,47 @@ qboolean G_admin_spec999( gentity_t *ent, int skiparg )
qboolean G_admin_register(gentity_t *ent, int skiparg ){
int level = 0;
+ int max;
+ char buffer [ 64 ];
if( !ent ) return qtrue;
level = G_admin_level(ent);
- if( level == 0 )
- level = 1;
+ if( G_SayArgc() > 1 + skiparg )
+ {
+ G_SayArgv( 1 + skiparg, buffer, sizeof( buffer ) );
+ max = atoi( buffer );
+
+ if( G_SayArgc() > 2 + skiparg )
+ {
+ G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) );
+ if( g_adminRegisterAdminPass.string[ 0 ] && Q_stricmp( g_adminRegisterAdminPass.string, "none" ) &&
+ Q_stricmp( g_adminRegisterAdminPass.string, buffer) )
+ {
+ ADMP( "Invalid password.\n" );
+ return qfalse;
+ }
+ if (max > g_adminRegisterAdminLevel.integer)
+ max = g_adminRegisterAdminLevel.integer;
+ if( level <= g_adminRegisterAdminLevel.integer )
+ level = max;
+ }
+ else
+ {
+ if( max > g_adminRegisterLevel.integer )
+ max = g_adminRegisterLevel.integer;
+ if( max >= 0 && level >= 0 && level <= g_adminRegisterLevel.integer )
+ level = max;
+ }
+ }
+ else
+ {
+ max = 1;
+ }
+
+ if( level >= 0 && level < max )
+ level = max;
if( !Q_stricmp( ent->client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ) )
{
@@ -5886,140 +6302,6 @@ qboolean G_admin_restart( gentity_t *ent, int skiparg )
return qtrue;
}
-qboolean G_admin_nobuild( gentity_t *ent, int skiparg )
-{
- char buffer[ MAX_STRING_CHARS ];
- int i, tmp;
-
- if( G_SayArgc() < 2 + skiparg )
- {
- ADMP( "^3!nobuild: ^7usage: !nobuild (^5enable / disable / log / remove / save^7)\n" );
- return qfalse;
- }
-
- G_SayArgv( 1 + skiparg, buffer, sizeof( buffer ) );
-
- if( !Q_stricmp( buffer, "enable" ) || !Q_stricmp( buffer, "Enable" ) )
- {
- if( G_SayArgc() < 4 + skiparg )
- {
- ADMP( "^3!nobuild: ^7usage: !nobuild enable (^5area^7) (^5height^7)\n" );
- return qfalse;
- }
-
- if( !level.noBuilding )
- {
-
- level.noBuilding = qtrue;
-
- // Grab and set the area
- G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) );
- tmp = atoi( buffer );
- level.nbArea = tmp;
-
- // Grab and set the height
- G_SayArgv( 3 + skiparg, buffer, sizeof( buffer ) );
- tmp = atoi( buffer );
- level.nbHeight = tmp;
-
- ADMP( "^3!nobuild: ^7nobuild is now enabled, please place a buildable to spawn a marker\n" );
- return qtrue;
- }
- else
- {
- ADMP( "^3!nobuild: ^7nobuild is already enabled. type !nobuild disable to disable nobuild mode.\n" );
- return qfalse;
- }
- }
-
- if( !Q_stricmp( buffer, "disable" ) || !Q_stricmp( buffer, "Disable" ) )
- {
- if( level.noBuilding )
- {
- level.noBuilding = qfalse;
- level.nbArea = 0.0f;
- level.nbHeight = 0.0f;
- ADMP( "^3!nobuild: ^7nobuild is now disabled\n" );
- return qtrue;
- }
- else
- {
- ADMP( "^3!nobuild: ^7nobuild is disabled. type !nobuild enable (^5area^7) (^5height^7) to enable nobuild mode.\n" );
- return qfalse;
- }
- }
-
- if( !Q_stricmp( buffer, "log" ) || !Q_stricmp( buffer, "Log" ) )
- {
- ADMBP_begin();
-
- tmp = 0;
- for( i = 0; i < MAX_GENTITIES; i++ )
- {
- if( level.nbMarkers[ i ].Marker == NULL )
- continue;
-
- // This will display a start at 1, and not 0. This is to stop confusion by server ops
- ADMBP( va( "^7#%i at origin (^5%f %f %f^7)^7\n", i + 1, level.nbMarkers[ i ].Origin[0], level.nbMarkers[ i ].Origin[1], level.nbMarkers[ i ].Origin[2] ) );
- tmp++;
- }
- ADMBP( va( "^3!nobuild:^7 displaying %i marker(s)\n", tmp ) );
-
- ADMBP_end();
- return qtrue;
- }
-
- if( !Q_stricmp( buffer, "remove" ) || !Q_stricmp( buffer, "Remove" ) )
- {
- if( G_SayArgc() < 3 + skiparg )
- {
- ADMP( "^3!nobuild: ^7usage: !nobuild remove (^5marker #^7)\n" );
- return qfalse;
- }
-
- G_SayArgv( 2 + skiparg, buffer, sizeof( buffer ) );
- tmp = atoi( buffer ) - 1;
- // ^ that was to work with the display number...
-
- if( level.nbMarkers[ tmp ].Marker == NULL )
- {
- ADMP( "^3!nobuild: ^7that is not a valid marker number\n" );
- return qfalse;
- }
- // Donno why im doing this, but lets clear this...
- level.nbMarkers[ tmp ].Marker->noBuild.isNB = qfalse;
- level.nbMarkers[ tmp ].Marker->noBuild.Area = 0.0f;
- level.nbMarkers[ tmp ].Marker->noBuild.Height = 0.0f;
-
- // Free the entitiy and null it out...
- G_FreeEntity( level.nbMarkers[ tmp ].Marker );
- level.nbMarkers[ tmp ].Marker = NULL;
-
- // That is to work with the display number...
- ADMP( va( "^3!nobuild:^7 marker %i has been removed\n", tmp + 1 ) );
- return qtrue;
- }
-
- if( !Q_stricmp( buffer, "save" ) || !Q_stricmp( buffer, "Save" ) )
- {
- int i, tmp;
-
- G_NobuildSave( );
-
- tmp = 0;
- for( i = 0; i < MAX_GENTITIES; i++ )
- {
- if( level.nbMarkers[ i ].Marker == NULL )
- continue;
-
- tmp++;
- }
- ADMP( va( "^3!nobuild:^7 %i nobuild markers have been saved\n", tmp ) );
- return qtrue;
- }
- return qfalse;
-}
-
qboolean G_admin_nextmap( gentity_t *ent, int skiparg )
{
AP( va( "print \"^3!nextmap: ^7%s^7 decided to load the next map\n\"",
@@ -6244,6 +6526,441 @@ qboolean G_admin_designate( gentity_t *ent, int skiparg )
return qtrue;
}
+static int SortFlags( const void *pa, const void *pb )
+{
+ char *a = (char *)pa;
+ char *b = (char *)pb;
+
+ if( *a == '-' || *a == '+' )
+ a++;
+ if( *b == '-' || *b == '+' )
+ b++;
+ return strcmp(a, b);
+}
+
+#define MAX_USER_FLAGS 200
+const char *G_admin_user_flag( char *oldflags, char *flag, qboolean add, qboolean clear,
+ char *newflags, int size )
+{
+ char *token, *token_p;
+ char *key;
+ char head_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ];
+ char tail_flags[ MAX_USER_FLAGS ][ MAX_ADMIN_FLAG_LEN ];
+ char allflag[ MAX_ADMIN_FLAG_LEN ];
+ char newflag[ MAX_ADMIN_FLAG_LEN ];
+ int head_count = 0;
+ int tail_count = 0;
+ qboolean flagset = qfalse;
+ int i;
+
+ if( !flag[ 0 ] )
+ {
+ return "invalid admin flag";
+ }
+
+ allflag[ 0 ] = '\0';
+ token_p = oldflags;
+ while( *( token = COM_Parse( &token_p ) ) )
+ {
+ key = token;
+ if( *key == '-' || *key == '+' )
+ key++;
+
+ if( !strcmp( key, flag ) )
+ {
+ if( flagset )
+ continue;
+ flagset = qtrue;
+ if( clear )
+ {
+ // clearing ALLFlAGS will result in clearing any following flags
+ if( !strcmp( key, ADMF_ALLFLAGS ) )
+ break;
+ else
+ continue;
+ }
+ Com_sprintf( newflag, sizeof( newflag ), "%s%s",
+ ( add ) ? "+" : "-", key );
+ }
+ else
+ {
+ Q_strncpyz( newflag, token, sizeof( newflag ) );
+ }
+
+ if( !strcmp( key, ADMF_ALLFLAGS ) )
+ {
+ if( !allflag[ 0 ] )
+ Q_strncpyz( allflag, newflag, sizeof( allflag ) );
+ continue;
+ }
+
+ if( !allflag[ 0 ] )
+ {
+ if( head_count < MAX_USER_FLAGS )
+ {
+ Q_strncpyz( head_flags[ head_count ], newflag,
+ sizeof( head_flags[ head_count ] ) );
+ head_count++;
+ }
+ }
+ else
+ {
+ if( tail_count < MAX_USER_FLAGS )
+ {
+ Q_strncpyz( tail_flags[ tail_count ], newflag,
+ sizeof( tail_flags[ tail_count ] ) );
+ tail_count++;
+ }
+ }
+ }
+
+ if( !flagset && !clear )
+ {
+ if( !strcmp( flag, ADMF_ALLFLAGS ) )
+ {
+ Com_sprintf( allflag, sizeof( allflag ), "%s%s",
+ ( add ) ? "" : "-", ADMF_ALLFLAGS );
+ }
+ else if( !allflag[ 0 ] )
+ {
+ if( head_count < MAX_USER_FLAGS )
+ {
+ Com_sprintf( head_flags[ head_count ], sizeof( head_flags[ head_count ] ),
+ "%s%s", ( add ) ? "" : "-", flag );
+ head_count++;
+ }
+ }
+ else
+ {
+ if( tail_count < MAX_USER_FLAGS )
+ {
+ Com_sprintf( tail_flags[ tail_count ], sizeof( tail_flags[ tail_count ] ),
+ "%s%s", ( add ) ? "+" : "-", flag );
+ tail_count++;
+ }
+ }
+ }
+
+ qsort( head_flags, head_count, sizeof( head_flags[ 0 ] ), SortFlags );
+ qsort( tail_flags, tail_count, sizeof( tail_flags[ 0 ] ), SortFlags );
+
+ // rebuild flags
+ newflags[ 0 ] = '\0';
+ for( i = 0; i < head_count; i++ )
+ {
+ Q_strcat( newflags, size,
+ va( "%s%s", ( i ) ? " ": "", head_flags[ i ] ) );
+ }
+ if( allflag[ 0 ] )
+ {
+ Q_strcat( newflags, size,
+ va( "%s%s", ( newflags[ 0 ] ) ? " ": "", allflag ) );
+
+ for( i = 0; i < tail_count; i++ )
+ {
+ Q_strcat( newflags, size,
+ va( " %s", tail_flags[ i ] ) );
+ }
+ }
+
+ return NULL;
+}
+
+
+typedef struct {
+ char *flag;
+ char *description;
+} AdminFlagListEntry_t;
+static AdminFlagListEntry_t adminFlagList[] =
+{
+ { ADMF_ACTIVITY, "inactivity rules do not apply" },
+ { ADMF_ADMINCHAT, "can see and use admin chat" },
+ { ADMF_ALLFLAGS, "has all flags and can use any command" },
+ { ADMF_BAN_IMMUNITY, "immune from IP bans" },
+ { ADMF_CAN_PERM_BAN, "can permanently ban players" },
+ { ADMF_DBUILDER, "permanent designated builder" },
+ { ADMF_FORCETEAMCHANGE, "team balance rules do not apply" },
+ { ADMF_INCOGNITO, "does not show as admin in !listplayers" },
+ { ADMF_IMMUNITY, "cannot be vote kicked or muted" },
+ { ADMF_IMMUTABLE, "admin commands cannot be used on them" },
+ { ADMF_NOCENSORFLOOD, "no flood protection" },
+ { ADMF_NO_VOTE_LIMIT, "vote limitations do not apply" },
+ { ADMF_SEESFULLLISTPLAYERS, "sees all info in !listplayers" },
+ { ADMF_SPEC_ALLCHAT, "can see team chat as spectator" },
+ { ADMF_ADMINSTEALTH, "uses admin stealth" },
+ { ADMF_TEAMCHANGEFREE, "keeps credits on team switch" },
+ { ADMF_TEAMCHAT_CMD, "can run commands from team chat" },
+ { ADMF_UNACCOUNTABLE, "does not need to specify reason for kick/ban" },
+ { ADMF_NO_CHAT, "can not talk" },
+ { ADMF_NO_VOTE, "can not call votes" }
+};
+static int adminNumFlags= sizeof( adminFlagList ) / sizeof( adminFlagList[ 0 ] );
+
+#define MAX_LISTCOMMANDS 128
+qboolean G_admin_flaglist( gentity_t *ent, int skiparg )
+{
+ qboolean shown[ MAX_LISTCOMMANDS ];
+ int i, j;
+ int count = 0;
+
+ ADMBP_begin();
+
+ ADMBP( "^3Ability flags:\n" );
+
+ for( i = 0; i < adminNumFlags; i++ )
+ {
+ ADMBP( va( " %s%-20s ^7%s\n",
+ ( adminFlagList[ i ].flag[ 0 ] != '.' ) ? "^5" : "^1",
+ adminFlagList[ i ].flag,
+ adminFlagList[ i ].description ) );
+ }
+
+ ADMBP( "^3Command flags:\n" );
+
+ memset( shown, 0, sizeof( shown ) );
+ for( i = 0; i < adminNumCmds; i++ )
+ {
+ if( i < MAX_LISTCOMMANDS && shown[ i ] )
+ continue;
+ ADMBP( va( " ^5%-20s^7", g_admin_cmds[ i ].flag ) );
+ for( j = i; j < adminNumCmds; j++ )
+ {
+ if( !strcmp( g_admin_cmds[ j ].flag, g_admin_cmds[ i ].flag ) )
+ {
+ ADMBP( va( " %s", g_admin_cmds[ j ].keyword ) );
+ if( j < MAX_LISTCOMMANDS )
+ shown[ j ] = qtrue;
+ }
+ }
+ ADMBP( "\n" );
+ count++;
+ }
+
+ ADMBP( va( "^3!flaglist: ^7listed %d abilities and %d command flags\n",
+ adminNumFlags, count ) );
+
+ ADMBP_end();
+
+ return qtrue;
+}
+
+qboolean G_admin_flag( gentity_t *ent, int skiparg )
+{
+ char command[ MAX_ADMIN_CMD_LEN ], *cmd;
+ char name[ MAX_NAME_LENGTH ];
+ char flagbuf[ MAX_ADMIN_FLAG_LEN ];
+ char *flag;
+ int id;
+ char adminname[ MAX_NAME_LENGTH ] = {""};
+ const char *result;
+ qboolean add = qtrue;
+ qboolean clear = qfalse;
+ int admin_level = -1;
+ int i, level;
+
+ 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;
+ }
+
+ G_SayArgv( 1 + skiparg, name, sizeof( name ) );
+ if( name[ 0 ] == '*' )
+ {
+ if( ent )
+ {
+ ADMP( va( "^3!%s: only console can change admin level flags\n", cmd ) );
+ return qfalse;
+ }
+ id = atoi( name + 1 );
+ for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ )
+ {
+ if( g_admin_levels[ i ]->level == id )
+ {
+ admin_level = i;
+ break;
+ }
+ }
+ if( admin_level < 0 )
+ {
+ ADMP( va( "^3!%s: admin level %d does not exist\n", cmd, id ) );
+ return qfalse;
+ }
+ Com_sprintf( adminname, sizeof( adminname ), "admin level %d", id );
+ }
+ else
+ {
+ id = G_admin_find_admin_slot( ent, name, cmd, 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 than you\n", cmd ) );
+ return qfalse;
+ }
+ }
+
+ if( G_SayArgc() < 3 + skiparg )
+ {
+ flag = "";
+ level = 0;
+ if( admin_level < 0 )
+ {
+ for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ )
+ {
+ if( g_admin_admins[ id ]->level == g_admin_levels[ i ]->level )
+ {
+ flag = g_admin_levels[ i ]->flags;
+ level = g_admin_admins[ id ]->level;
+ break;
+ }
+ }
+ ADMP( va( "^3%s:^7 flags for %s^7 are '^3%s^7'\n",
+ cmd, adminname, g_admin_admins[ id ]->flags) );
+ }
+ else
+ {
+ flag = g_admin_levels[ admin_level ]->flags;
+ level = g_admin_levels[ admin_level ]->level;
+ }
+ ADMP( va( "^3%s:^7 level %d flags are '%s'\n",
+ cmd, level, flag ) );
+
+ return qtrue;
+ }
+
+ G_SayArgv( 2 + skiparg, flagbuf, sizeof( flagbuf ) );
+ flag = flagbuf;
+ if( flag[ 0 ] == '-' || flag[ 0 ] == '+' )
+ {
+ add = ( flag[ 0 ] == '+' );
+ 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( flag[ 0 ] != '.' && !G_admin_permission( ent, flag ) )
+ {
+ 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;
+ }
+
+ if( admin_level < 0 )
+ {
+ result = G_admin_user_flag( g_admin_admins[ id ]->flags, flag, add, clear,
+ g_admin_admins[ id ]->flags, sizeof( g_admin_admins[ id ]->flags ) );
+ }
+ else
+ {
+ result = G_admin_user_flag( g_admin_levels[ admin_level ]->flags, flag, add, clear,
+ g_admin_levels[ admin_level ]->flags,
+ sizeof( g_admin_levels[ admin_level ]->flags ) );
+ }
+ if( result )
+ {
+ ADMP( va( "^3!flag: ^7an error occured setting flag '^3%s^7', %s\n",
+ flag, result ) );
+ return qfalse;
+ }
+
+ if( !Q_stricmp( cmd, "flag" ) )
+ {
+ G_AdminsPrintf( "^3!%s: ^7%s^7 was %s admin flag '%s' by %s\n",
+ cmd, adminname,
+ ( add ) ? "given" : "denied",
+ flag,
+ ( ent ) ? ent->client->pers.netname : "console" );
+ }
+ else
+ {
+ G_AdminsPrintf( "^3!%s: ^7admin flag '%s' for %s^7 cleared by %s\n",
+ cmd, flag, 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_admin_slot( ent, cmd, "immunity", adminname, sizeof( adminname ) );
+ if( id < 0 )
+ return qfalse;
+
+ if( *action != '+' && *action != '-' )
+ {
+ ADMP( va( "^3immunity:^7 ban immunity for %s^7 is %s, prepend + or - to the slot number to change.\n",
+ adminname,
+ ( G_admin_permission_guid( g_admin_admins[ id ]->guid, ADMF_BAN_IMMUNITY ) ) ? "on" : "off" ) );
+ return qfalse;
+ }
+
+ result = G_admin_user_flag( g_admin_admins[ id ]->flags,
+ ADMF_BAN_IMMUNITY, qtrue, ( *action != '+' ),
+ g_admin_admins[ id ]->flags, sizeof( g_admin_admins[ id ]->flags ) );
+ if( result )
+ {
+ ADMP( va( "^3!immunity: ^7an error occured setting flag, %s\n", result ) );
+ return qfalse;
+ }
+
+ if( *action == '+' )
+ {
+ AP( va(
+ "print \"^3!immunity: ^7%s^7 was given ban immunity by %s\n\"",
+ adminname, ( ent ) ? ent->client->pers.netname : "console" ) );
+ }
+ else
+ {
+ AP( va(
+ "print \"^3!immunity: ^7ban immunity for %s^7 removed by %s\n\"",
+ adminname, ( ent ) ? ent->client->pers.netname : "console" ) );
+ }
+
+ if( !g_admin.string[ 0 ] )
+ ADMP( "^3!immunity: ^7WARNING g_admin not set, not saving admin record "
+ "to a file\n" );
+ else
+ admin_writeconfig();
+
+ return qtrue;
+}
+
//!Warn by Gate (Daniel Evans)
qboolean G_admin_warn( gentity_t *ent, int skiparg )
{//mostly copy and paste with the proper lines altered from !mute and !kick
@@ -6284,9 +7001,47 @@ qboolean G_admin_warn( gentity_t *ent, int skiparg )
( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );//console announcement
return qtrue;
}
+
+qboolean G_admin_bring( gentity_t *ent, int skiparg )
+{
+ int pids[ MAX_CLIENTS ];
+ char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ];
+ gentity_t *vic;
+
+ if( !ent )
+ {
+ ADMP( "^3!bring: ^7console cannot use this command\n" );
+ return qfalse;
+ }
+ if( ent->client->pers.teamSelection != PTE_NONE )
+ {
+ ADMP( "^3!bring: ^7you can only use this command from spectator\n" );
+ return qfalse;
+ }
+ if( G_SayArgc() < 2 + skiparg )
+ {
+ ADMP( "^3!bring: ^7usage: !bring [name|slot#]\n" );
+ return qfalse;
+ }
+
+ G_SayArgv( 1 + skiparg, name, sizeof( name ) );
+ if( G_ClientNumbersFromString( name, pids ) != 1 )
+ {
+ G_MatchOnePlayer( pids, err, sizeof( err ) );
+ ADMP( va( "^3!bring: ^7%s\n", err ) );
+ return qfalse;
+ }
+
+ vic = &g_entities[ pids[ 0 ] ];
+ VectorCopy( vic->client->ps.origin, ent->client->ps.origin );
+
+ return qtrue;
+}
qboolean G_admin_putmespec( gentity_t *ent, int skiparg )
{
+ int cs_offset;
+
if( !ent )
{
ADMP( "!specme: sorry, but console isn't allowed on the spectators team\n");
@@ -6302,6 +7057,15 @@ qboolean G_admin_putmespec( gentity_t *ent, int skiparg )
if(ent->client->pers.teamSelection == PTE_NONE)
return qfalse;
+ if( ent->client->pers.teamSelection == PTE_ALIENS )
+ cs_offset = 1;
+ else
+ cs_offset = 0;
+ if( level.teamVoteTime[ cs_offset ] )
+ {
+ trap_SendServerCommand( ent-g_entities, "print \"Can not leave team during a team vote\n\"" );
+ return qfalse;
+ }
//guard against build timer exploit
if( ent->client->pers.teamSelection != PTE_NONE && ent->client->sess.sessionTeam != TEAM_SPECTATOR &&
( ent->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0 ||
@@ -6315,10 +7079,119 @@ qboolean G_admin_putmespec( gentity_t *ent, int skiparg )
}
G_ChangeTeam( ent, PTE_NONE );
+
+ // check for silent '!specme s' - requires !kick permission
+ if( G_SayArgc() > 1 + skiparg )
+ {
+ char arg[ 2 ];
+
+ G_SayArgv( 1 + skiparg, arg, sizeof( arg ) );
+ if( ( arg[ 0 ] == 's' || arg[ 0 ] == 'S' ) && G_admin_permission( ent, "kick" ) )
+ {
+ ADMP("^3!specme: ^7 You have silently joined the spectators\n");
+ return qtrue;
+ }
+ }
+
AP( va("print \"^3!specme: ^7%s^7 decided to join the spectators\n\"", ent->client->pers.netname ) );
return qtrue;
}
+qboolean G_admin_outlaw( gentity_t *ent, int skiparg )
+{
+ int pids[ MAX_CLIENTS ];
+ char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ];
+ char valuebuf[ 8 ];
+ gentity_t *vic;
+ int points;
+ qboolean activate = qtrue;
+
+ if( !g_bleedingSpree.integer )
+ {
+ ADMP( "^3!outlaw: ^7bleeding sprees are disabled\n" );
+ return qfalse;
+ }
+
+ if( G_SayArgc() < 2 + skiparg )
+ {
+ ADMP( "^3!outlaw: ^7usage: !outlaw [name|slot#] (value)\n" );
+ return qfalse;
+ }
+ G_SayArgv( 1 + skiparg, name, sizeof( name ) );
+ if( G_ClientNumbersFromString( name, pids ) != 1 )
+ {
+ G_MatchOnePlayer( pids, err, sizeof( err ) );
+ ADMP( va( "^3!mute: ^7%s\n", err ) );
+ return qfalse;
+ }
+ vic = &g_entities[ pids[ 0 ] ];
+
+ if( G_SayArgc() > 2 + skiparg )
+ {
+ G_SayArgv( 2 + skiparg, valuebuf, sizeof( valuebuf ) );
+ if( valuebuf[ 0 ] == '?' )
+ {
+ ADMP( va( "^3!outlaw: ^7%s^7's bleeder value is %d\n",
+ vic->client->pers.netname,
+ vic->client->pers.statscounters.spreebleeds ) );
+ return qtrue;
+ }
+ if( valuebuf[ 0 ] == '+' || valuebuf[ 0 ] == '-' )
+ {
+ activate = qfalse;
+ }
+ points = atoi( valuebuf );
+ }
+ else
+ {
+ points = ( g_bleedingSpree.integer + 1 ) * 100;
+ }
+
+ if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) )
+ {
+ ADMP( "^3!outlaw: ^7sorry, but your intended victim has a higher admin"
+ " level than you\n" );
+ return qfalse;
+ }
+ if( points )
+ {
+ vic->client->pers.statscounters.spreebleeds += points;
+ if( vic->client->pers.statscounters.spreebleeds < 1 )
+ vic->client->pers.statscounters.spreebleeds = 1;
+ if( activate && !vic->client->pers.bleeder )
+ {
+ vic->client->pers.bleeder = qtrue;
+ level.bleeders++;
+
+ AP( va( "print \"^3!outlaw: ^7%s^7 has been designated an outlaw by ^7%s\n\"",
+ vic->client->pers.netname,
+ ( ent ) ? ent->client->pers.netname : "console" ) );
+ }
+ else
+ {
+ AP( va( "print \"^3!outlaw: ^7%s^7 bleeder value has been adjusted by ^7%s\n\"",
+ vic->client->pers.netname,
+ ( ent ) ? ent->client->pers.netname : "console" ) );
+ ADMP( va( "^3!outlaw: ^7%s^7's bleeder value is now %d\n",
+ vic->client->pers.netname,
+ vic->client->pers.statscounters.spreebleeds ) );
+ }
+ }
+ else
+ {
+ vic->client->pers.statscounters.spreebleeds = 0;
+ if( vic->client->pers.bleeder )
+ vic->client->pers.bleeder = qfalse;
+ if( level.bleeders )
+ level.bleeders--;
+
+ AP( va( "print \"^3!outlaw: ^7%s^7 has been pardoned by ^7%s\n\"",
+ vic->client->pers.netname,
+ ( ent ) ? ent->client->pers.netname : "console" ) );
+ }
+ return qtrue;
+}
+
qboolean G_admin_slap( gentity_t *ent, int skiparg )
{
int pids[ MAX_CLIENTS ];
@@ -6452,6 +7325,45 @@ qboolean G_admin_drop( gentity_t *ent, int skiparg )
return qtrue;
}
+qboolean G_admin_bubble( gentity_t *ent, int skiparg )
+{
+ int pids[ MAX_CLIENTS ];
+ char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ];
+ gentity_t *vic;
+
+ if( G_SayArgc() < 2 + skiparg )
+ {
+ ADMP( "^3!bubble: ^7usage: !bubble [name|slot#]\n" );
+ return qfalse;
+ }
+ G_SayArgv( 1 + skiparg, name, sizeof( name ) );
+ if( G_ClientNumbersFromString( name, pids ) != 1 )
+ {
+ G_MatchOnePlayer( pids, err, sizeof( err ) );
+ ADMP( va( "^3!bubble: ^7%s\n", err ) );
+ return qfalse;
+ }
+ if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) )
+ {
+ ADMP( "^3!bubble: ^7sorry, but your intended victim has a higher admin"
+ " level than you\n" );
+ return qfalse;
+ }
+
+ vic = &g_entities[ pids[ 0 ] ];
+ if( vic->client->pers.bubbleTime )
+ vic->client->pers.bubbleTime = 0;
+ else
+ vic->client->pers.bubbleTime = level.time + 500;
+
+ AP( va( "print \"^3!bubble: ^7bubbles %s for %s^7 by %s\n\"",
+ ( vic->client->pers.bubbleTime ) ? "enabled" : "disabled",
+ vic->client->pers.netname,
+ ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
+
+ return qtrue;
+}
+
qboolean G_admin_buildlog( gentity_t *ent, int skiparg )
{
#define LOG_DISPLAY_LENGTH 10
@@ -6663,6 +7575,34 @@ qboolean G_admin_buildlog( gentity_t *ent, int skiparg )
return qtrue;
}
+int G_admin_autorevert( gentity_t *ent )
+{
+ int count = 0;
+ int max;
+ buildHistory_t *ptr;
+
+ if( !g_autoRevert.integer )
+ return 0;
+ if( !g_buildLogMaxLength.integer )
+ return 0;
+
+ max = g_autoRevert.integer;
+ ptr = level.buildHistory;
+ while( ptr && count < max )
+ {
+ if( ptr->ent == ent && ptr->time > level.time - (60000 * 5) &&
+ ( ptr->fate == BF_TEAMKILLED || ptr->fate == BF_DECONNED ) )
+ {
+ trap_SendConsoleCommand( EXEC_APPEND, va("!revert #%d\n", ptr->ID) );
+ count++;
+ }
+
+ ptr = ptr->next;
+ }
+
+ return count;
+}
+
qboolean G_admin_revert( gentity_t *ent, int skiparg )
{
int i = 0, j = 0, repeat = 1, ID = 0, len, matchlen=0;
@@ -6805,33 +7745,6 @@ qboolean G_admin_revert( gentity_t *ent, int skiparg )
return qfalse;
}
}
- // Prevent teleport glitch when reverting an occupied hovel
- if( targ->s.modelindex == BA_A_HOVEL &&
- targ->active )
- {
- gentity_t *builder = targ->builder;
- vec3_t newOrigin;
- vec3_t newAngles;
-
- VectorCopy( targ->s.angles, newAngles );
- newAngles[ ROLL ] = 0;
-
- VectorCopy( targ->s.origin, newOrigin );
- VectorMA( newOrigin, 1.0f, targ->s.origin2, newOrigin );
-
- //prevent lerping
- builder->client->ps.eFlags ^= EF_TELEPORT_BIT;
- builder->client->ps.eFlags &= ~EF_NODRAW;
- G_UnlaggedClear( builder );
-
- G_SetOrigin( builder, newOrigin );
- VectorCopy( newOrigin, builder->client->ps.origin );
- G_SetClientViewAngle( builder, newAngles );
-
- //client leaves hovel
- builder->client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING;
- }
-
// if we haven't returned yet then we're good to go, free it
G_FreeEntity( targ );
// put the marked buildables back and mark them again
@@ -7225,6 +8138,76 @@ qboolean G_admin_L1(gentity_t *ent, int skiparg ){
return qtrue;
}
+qboolean G_admin_nobuild(gentity_t *ent, int skiparg )
+{
+ char func[ 32 ];
+
+ if( ent && ent->client->pers.teamSelection != PTE_NONE )
+ {
+ ADMP( "^3!bring: ^7you can only use this command from spectator\n" );
+ return qfalse;
+ }
+
+ if( G_SayArgc() < 2 + skiparg )
+ {
+ ADMP( "^3!nobuild: ^7usage: !nobuild [on|off|save|add|del|list|mode|zone|+|-|go]\n" );
+ return qfalse;
+ }
+ G_SayArgv( 1 + skiparg, func, sizeof( func ) );
+
+ if( !Q_stricmp( func, "on" ) )
+ {
+ nobuild_set( qtrue, ent );
+ }
+ else if( !Q_stricmp( func, "off" ) )
+ {
+ nobuild_set( qfalse, ent );
+ }
+ else if( !Q_stricmp( func, "save" ) )
+ {
+ nobuild_save( );
+ }
+ else if( !Q_stricmp( func, "list" ) )
+ {
+ nobuild_list( ent );
+ }
+ else if( !Q_stricmp( func, "add" ) )
+ {
+ nobuild_add( ent );
+ }
+ else if( !Q_stricmp( func, "del" ) )
+ {
+ nobuild_del( ent );
+ }
+ else if( !Q_stricmp( func, "zone" ) )
+ {
+ nobuild_command( ent, qtrue, qfalse, 0.0f );
+ }
+ else if( !Q_stricmp( func, "mode" ) )
+ {
+ nobuild_command( ent, qfalse, qtrue, 0.0f );
+ }
+ else if( !Q_stricmp( func, "+" ) )
+ {
+ nobuild_command( ent, qfalse, qfalse, 8.0f );
+ }
+ else if( !Q_stricmp( func, "-" ) )
+ {
+ nobuild_command( ent, qfalse, qfalse, -8.0f );
+ }
+ else if( !Q_stricmp( func, "go" ) )
+ {
+ nobuild_go( ent );
+ }
+ else
+ {
+ ADMP( "^3!nobuild: ^7usage: !nobuild [on|off|save|add|del|list|mode|zone|+|-|go]\n" );
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
qboolean G_admin_invisible( gentity_t *ent, int skiparg )
{
if( !ent )
diff --git a/src/game/g_admin.h b/src/game/g_admin.h
index 33103ff..6e6e57a 100644
--- a/src/game/g_admin.h
+++ b/src/game/g_admin.h
@@ -32,19 +32,24 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define ADMBP_end() G_admin_buffer_end(ent)
#define MAX_ADMIN_LEVELS 32
-#define MAX_ADMIN_ADMINS 1024
+#define MAX_ADMIN_ADMINS 2048
#define MAX_ADMIN_BANS 1024
#define MAX_ADMIN_NAMELOGS 128
#define MAX_ADMIN_NAMELOG_NAMES 5
#define MAX_ADMIN_ADMINLOGS 128
#define MAX_ADMIN_ADMINLOG_ARGS 50
+#define MAX_ADMIN_TKLOGS 64
#define MAX_ADMIN_FLAG_LEN 20
#define MAX_ADMIN_FLAGS 1024
#define MAX_ADMIN_COMMANDS 64
#define MAX_ADMIN_CMD_LEN 20
#define MAX_ADMIN_BAN_REASON 50
+
#define MAX_ADMIN_BANSUSPEND_DAYS 14
+#define CHAT_MAXCHAN 10
+#define CHAT_MAXPASS 12
+
/*
* IMMUNITY - cannot be vote kicked, vote muted
* NOCENSORFLOOD - cannot be censored or flood protected
@@ -63,14 +68,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* SEESFULLLISTPLAYERS - sees all information in !listplayers
* DBUILDER - permanent designated builder
* STEALTH - uses admin stealth
- * SPECIAL - allows some special permissions (unlimited votes etc)
- * SPECIALNAME - allows black text in name
- * .NOCHAT - mutes a player on connect
- * .NOVOTE - disallows voting by a player
+ * BANIMMUNITY - immune from IP based bans
* ALLFLAGS - all flags (including command flags) apply to this player
*/
-
#define ADMF_IMMUNITY "IMMUNITY"
#define ADMF_NOCENSORFLOOD "NOCENSORFLOOD"
#define ADMF_TEAMCHANGEFREE "TEAMCHANGEFREE"
@@ -92,14 +93,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define ADMF_BAN_IMMUNITY "BANIMMUNITY"
-#define ADMF_SPECIAL "SPECIAL"
-#define ADMF_SPECIALNAME "SPECIALNAME"
-
#define ADMF_NO_CHAT ".NOCHAT"
#define ADMF_NO_VOTE ".NOVOTE"
#define MAX_ADMIN_LISTITEMS 20
#define MAX_ADMIN_SHOWBANS 10
+
#define MAX_ADMIN_MAPLOG_LENGTH 5
// important note: QVM does not seem to allow a single char to be a
@@ -129,6 +128,8 @@ typedef struct g_admin_admin
int level;
char flags[ MAX_ADMIN_FLAGS ];
int seen;
+ char chat[ CHAT_MAXCHAN ][ CHAT_MAXPASS ];
+ int karma;
}
g_admin_admin_t;
@@ -168,7 +169,6 @@ typedef struct g_admin_namelog
int denyHumanWeapons;
int denyAlienClasses;
int specExpires;
- int voteCount;
}
g_admin_namelog_t;
@@ -184,6 +184,19 @@ typedef struct g_admin_adminlog
}
g_admin_adminlog_t;
+typedef struct g_admin_tklog
+{
+ char name[ MAX_NAME_LENGTH ];
+ char victim[ MAX_NAME_LENGTH ];
+ int id;
+ int time;
+ int damage;
+ int value;
+ int team;
+ int weapon;
+}
+g_admin_tklog_t;
+
qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen );
qboolean G_admin_cmd_check( gentity_t *ent, qboolean say );
qboolean G_admin_readconfig( gentity_t *ent, int skiparg );
@@ -195,24 +208,31 @@ int G_admin_level( gentity_t *ent );
void G_admin_set_adminname( gentity_t *ent );
char* G_admin_adminPrintName( gentity_t *ent );
-qboolean G_admin_seen(gentity_t *ent, int skiparg );
-void G_admin_seen_update( char *guid );
+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 );
// ! command functions
qboolean G_admin_time( gentity_t *ent, int skiparg );
qboolean G_admin_setlevel( gentity_t *ent, int skiparg );
-qboolean G_admin_flaglist( gentity_t *ent, int skiparg );
-qboolean G_admin_flag( gentity_t *ent, int skiparg );
qboolean G_admin_kick( gentity_t *ent, int skiparg );
qboolean G_admin_adjustban( gentity_t *ent, int skiparg );
qboolean G_admin_subnetban( gentity_t *ent, int skiparg );
qboolean G_admin_suspendban( gentity_t *ent, int skiparg );
qboolean G_admin_ban( gentity_t *ent, int skiparg );
qboolean G_admin_unban( gentity_t *ent, int skiparg );
+qboolean G_admin_seen(gentity_t *ent, int skiparg );
+void G_admin_karma_sync( void );
+void G_admin_seen_update( gclient_t *client, qboolean disconnect );
+qboolean G_admin_expire( gentity_t *ent, int skiparg );
qboolean G_admin_putteam( gentity_t *ent, int skiparg );
qboolean G_admin_adminlog( gentity_t *ent, int skiparg );
void G_admin_adminlog_cleanup( void );
void G_admin_adminlog_log( gentity_t *ent, char *command, char *args, int skiparg, qboolean success );
+qboolean G_admin_tklog( gentity_t *ent, int skiparg );
+void G_admin_tklog_cleanup( void );
+void G_admin_tklog_log( gentity_t *attacker, gentity_t *victim, int meansOfDeath );
qboolean G_admin_listadmins( gentity_t *ent, int skiparg );
qboolean G_admin_listlayouts( gentity_t *ent, int skiparg );
qboolean G_admin_listplayers( gentity_t *ent, int skiparg );
@@ -237,25 +257,33 @@ qboolean G_admin_spec999( gentity_t *ent, int skiparg );
qboolean G_admin_register( gentity_t *ent, int skiparg );
qboolean G_admin_rename( gentity_t *ent, int skiparg );
qboolean G_admin_restart( gentity_t *ent, int skiparg );
-qboolean G_admin_nobuild( gentity_t *ent, int skiparg );
qboolean G_admin_nextmap( gentity_t *ent, int skiparg );
qboolean G_admin_namelog( gentity_t *ent, int skiparg );
qboolean G_admin_lock( gentity_t *ent, int skiparg );
qboolean G_admin_unlock( gentity_t *ent, int skiparg );
qboolean G_admin_info( gentity_t *ent, int skiparg );
+qboolean G_admin_nobuild(gentity_t *ent, int skiparg );
qboolean G_admin_buildlog( gentity_t *ent, int skiparg );
qboolean G_admin_revert( gentity_t *ent, int skiparg );
+int G_admin_autorevert( gentity_t *ent );
qboolean G_admin_pause( gentity_t *ent, int skiparg );
+qboolean G_admin_practice( gentity_t *ent, int skiparg );
qboolean G_admin_L0( gentity_t *ent, int skiparg );
qboolean G_admin_L1( gentity_t *ent, int skiparg );
+qboolean G_admin_bring( gentity_t *ent, int skiparg );
qboolean G_admin_putmespec( gentity_t *ent, int skiparg );
+qboolean G_admin_outlaw( gentity_t *ent, int skiparg );
qboolean G_admin_warn( gentity_t *ent, int skiparg );
qboolean G_admin_designate( gentity_t *ent, int skiparg );
+qboolean G_admin_flaglist( 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_cp( gentity_t *ent, int skiparg );
+qboolean G_admin_invisible( gentity_t *ent, int skiparg );
qboolean G_admin_slap( gentity_t *ent, int skiparg );
qboolean G_admin_drop( gentity_t *ent, int skiparg );
-qboolean G_admin_invisible( gentity_t *ent, int skiparg );
+qboolean G_admin_bubble( gentity_t *ent, int skiparg );
void G_admin_print( gentity_t *ent, char *m );
void G_admin_buffer_print( gentity_t *ent, char *m );
diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c
index 3290ccc..5a2698b 100644
--- a/src/game/g_buildable.c
+++ b/src/game/g_buildable.c
@@ -513,6 +513,54 @@ static void freeBuildable( gentity_t *self )
G_FreeEntity( self );
}
+/*
+================
+G_RecoverBuildPoints
+
+Schedule build points for delayed recovery when a buildable dies
+ credit to Mercenaries Guild dev team (mgdev) for the whole idea
+================
+*/
+void G_RecoverBuildPoints( gentity_t *self )
+{
+ int team;
+ int value;
+
+ if( g_buildPointsRecoverRate.integer < 1 )
+ return;
+
+ if( self->killedBy != ENTITYNUM_NONE )
+ return;
+
+ team = BG_FindTeamForBuildable( self->s.modelindex );
+ value = BG_FindBuildPointsForBuildable( self->s.modelindex );
+ if( team == BIT_ALIENS )
+ {
+ if( !level.alienRecoverBuildPoints )
+ level.alienRecoverTime = level.time + 60000 / g_buildPointsRecoverRate.integer;
+ level.alienRecoverBuildPoints += value;
+ }
+ else if( team == BIT_HUMANS )
+ {
+ if( !level.humanRecoverBuildPoints )
+ level.humanRecoverTime = level.time + 60000 / g_buildPointsRecoverRate.integer;
+ level.humanRecoverBuildPoints += value;
+ }
+
+ self->killedBy = ENTITYNUM_NONE;
+}
+
+static void G_RecoverSetKiller( gentity_t *self, gentity_t *attacker )
+{
+ if( g_buildPointsRecoverRate.integer < 1 )
+ return;
+
+ // note attacker if teamkill
+ if( attacker && attacker->client &&
+ attacker->client->ps.stats[ STAT_PTEAM ] == BG_FindTeamForBuildable( self->s.modelindex ) )
+ self->killedBy = attacker - g_entities;
+}
+
//==================================================================================
@@ -531,6 +579,7 @@ void A_CreepRecede( gentity_t *self )
if( !( self->s.eFlags & EF_DEAD ) )
{
self->s.eFlags |= EF_DEAD;
+ G_RecoverBuildPoints( self );
G_AddEvent( self, EV_BUILD_DESTROY, 0 );
if( self->spawned )
@@ -573,6 +622,7 @@ void ASpawn_Melt( gentity_t *self )
if( !( self->s.eFlags & EF_DEAD ) )
{
self->s.eFlags |= EF_DEAD;
+ G_RecoverBuildPoints( self );
G_AddEvent( self, EV_BUILD_DESTROY, 0 );
if( self->spawned )
@@ -631,6 +681,7 @@ void ASpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
buildHistory_t *new;
new = G_Alloc( sizeof( buildHistory_t ) );
new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID;
+ new->time = level.time;
new->ent = ( attacker && attacker->client ) ? attacker : NULL;
if( new->ent )
new->name[ 0 ] = 0;
@@ -658,6 +709,8 @@ void ASpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
self->s.eFlags &= ~EF_FIRING; //prevent any firing effects
+ G_RecoverSetKiller( self, attacker );
+
if( attacker && attacker->client )
{
if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
@@ -796,6 +849,18 @@ void AOvermind_Think( gentity_t *self )
vec3_t mins, maxs;
int i, num;
gentity_t *enemy;
+ float om_range = OVERMIND_ATTACK_RANGE;
+ float om_damage = self->splashDamage;
+ float om_splash = self->splashRadius;
+
+ if( g_modMainStrength.integer > 0 )
+ {
+ om_range = om_range * g_modMainStrength.value / 100;
+ range[0] = range[1] = range[2] = om_range;
+
+ om_damage = om_damage * g_modMainStrength.value / 100;
+ om_splash = om_splash * g_modMainStrength.value / 100;
+ }
VectorAdd( self->s.origin, range, maxs );
VectorSubtract( self->s.origin, range, mins );
@@ -814,8 +879,8 @@ void AOvermind_Think( gentity_t *self )
if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
{
self->timestamp = level.time;
- G_SelectiveRadiusDamage( self->s.pos.trBase, self, self->splashDamage,
- self->splashRadius, self, MOD_OVERMIND, PTE_ALIENS );
+ G_SelectiveRadiusDamage( self->s.pos.trBase, self, om_damage,
+ om_splash, self, MOD_OVERMIND, PTE_ALIENS );
G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );
}
}
@@ -940,6 +1005,7 @@ void ABarricade_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker,
buildHistory_t *new;
new = G_Alloc( sizeof( buildHistory_t ) );
new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID;
+ new->time = level.time;
new->ent = ( attacker && attacker->client ) ? attacker : NULL;
if( new->ent )
new->name[ 0 ] = 0;
@@ -966,6 +1032,8 @@ void ABarricade_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker,
else
self->nextthink = level.time; //blast immediately
+ G_RecoverSetKiller( self, attacker );
+
if( attacker && attacker->client )
{
if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
@@ -1031,13 +1099,18 @@ void AAcidTube_Damage( gentity_t *self )
{
if( self->spawned )
{
+ int repeatRate = ACIDTUBE_REPEAT;
+
if( !( self->s.eFlags & EF_FIRING ) )
{
self->s.eFlags |= EF_FIRING;
G_AddEvent( self, EV_ALIEN_ACIDTUBE, DirToByte( self->s.origin2 ) );
}
- if( ( self->timestamp + ACIDTUBE_REPEAT ) > level.time )
+ if( g_modBuildableSpeed.integer > 0 )
+ repeatRate = repeatRate * 100 / g_modBuildableSpeed.integer;
+
+ if( ( self->timestamp + repeatRate ) > level.time )
self->think = AAcidTube_Damage;
else
{
@@ -1096,7 +1169,8 @@ void AAcidTube_Think( gentity_t *self )
if( !G_Visible( self, enemy ) )
continue;
- if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ if( enemy->client &&
+ ( enemy->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS || enemy->client->pers.bleeder ) )
{
if( level.paused || enemy->client->pers.paused )
continue;
@@ -1389,6 +1463,7 @@ void AHovel_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
buildHistory_t *new;
new = G_Alloc( sizeof( buildHistory_t ) );
new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID;
+ new->time = level.time;
new->ent = ( attacker && attacker->client ) ? attacker : NULL;
if( new->ent )
new->name[ 0 ] = 0;
@@ -1447,6 +1522,8 @@ void AHovel_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
self->r.contents = 0; //stop collisions...
trap_LinkEntity( self ); //...requires a relink
+ G_RecoverSetKiller( self, attacker );
+
if( attacker && attacker->client )
{
if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
@@ -1502,6 +1579,18 @@ void ABooster_Touch( gentity_t *self, gentity_t *other, trace_t *trace )
if( client && client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
return;
+ if( client->pers.bleeder )
+ {
+ if( !(client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED ) )
+ {
+ client->ps.stats[ STAT_STATE ] |= SS_POISONCLOUDED;
+ client->lastPoisonCloudedTime = level.time;
+ trap_SendServerCommand( client->ps.clientNum, "poisoncloud" );
+ trap_SendServerCommand( client->ps.clientNum, "print \"Your booster has poisoned you\n\"" );
+ }
+ return;
+ }
+
//only allow boostage once every 30 seconds
if( client->lastBoostedTime + BOOSTER_INTERVAL > level.time )
return;
@@ -1772,6 +1861,15 @@ void HReactor_Think( gentity_t *self )
vec3_t mins, maxs;
int i, num;
gentity_t *enemy, *tent;
+ float rc_range = REACTOR_ATTACK_RANGE;
+ float rc_damage = REACTOR_ATTACK_DAMAGE;
+
+ if( g_modMainStrength.integer > 0 )
+ {
+ rc_range = rc_range * g_modMainStrength.value / 100;
+ rc_damage = rc_damage * g_modMainStrength.value / 100;
+ range[0] = range[1] = range[2] = rc_range;
+ }
VectorAdd( self->s.origin, range, maxs );
VectorSubtract( self->s.origin, range, mins );
@@ -1792,8 +1890,8 @@ void HReactor_Think( gentity_t *self )
if( level.paused || enemy->client->pers.paused )
continue;
self->timestamp = level.time;
- G_SelectiveRadiusDamage( self->s.pos.trBase, self, REACTOR_ATTACK_DAMAGE,
- REACTOR_ATTACK_RANGE, self, MOD_REACTOR, PTE_HUMANS );
+ G_SelectiveRadiusDamage( self->s.pos.trBase, self, rc_damage,
+ rc_range, self, MOD_REACTOR, PTE_HUMANS );
tent = G_TempEntity( enemy->s.pos.trBase, EV_TESLATRAIL );
@@ -1943,6 +2041,7 @@ void HMedistat_Think( gentity_t *self )
{
if( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] &&
player->client->ps.pm_type != PM_DEAD &&
+ !player->client->pers.bleeder &&
self->enemy == player )
occupied = qtrue;
}
@@ -1960,7 +2059,8 @@ void HMedistat_Think( gentity_t *self )
if( player->flags & FL_NOTARGET )
continue; // notarget cancels even beneficial effects?
- if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS &&
+ !player->client->pers.bleeder )
{
if( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] &&
player->client->ps.pm_type != PM_DEAD )
@@ -1980,6 +2080,34 @@ void HMedistat_Think( gentity_t *self )
}
}
+ // bleeding spree retribution
+ if( level.bleeders && !self->enemy )
+ {
+ //look for something to hurt
+ for( i = 0; i < num; i++ )
+ {
+ player = &g_entities[ entityList[ i ] ];
+
+ if( player->client && player->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS && player->client->pers.bleeder )
+ {
+ if( player->health > 0 &&
+ player->client->ps.pm_type != PM_DEAD )
+ {
+ self->enemy = player;
+
+ //start the heal anim
+ if( !self->active )
+ {
+ G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse );
+ self->active = qtrue;
+ }
+ }
+ if( BG_InventoryContainsUpgrade( UP_MEDKIT, player->client->ps.stats ) )
+ BG_RemoveUpgradeFromInventory( UP_MEDKIT, player->client->ps.stats );
+ }
+ }
+ }
+
//nothing left to heal so go back to idling
if( !self->enemy && self->active )
{
@@ -1990,6 +2118,12 @@ void HMedistat_Think( gentity_t *self )
}
else if( self->enemy ) //heal!
{
+ if( self->enemy->client->pers.bleeder )
+ {
+ G_Damage( self->enemy, NULL, NULL, NULL, NULL, 10, 0, MOD_SLIME );
+ return;
+ }
+
if( self->enemy->client && self->enemy->client->ps.stats[ STAT_STATE ] & SS_POISONED )
self->enemy->client->ps.stats[ STAT_STATE ] &= ~SS_POISONED;
@@ -2147,7 +2281,8 @@ qboolean HMGTurret_CheckTarget( gentity_t *self, gentity_t *target, qboolean ign
if( !traceEnt->client )
return qfalse;
- if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS )
+ if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS &&
+ !traceEnt->client->pers.bleeder )
return qfalse;
return qtrue;
@@ -2211,6 +2346,24 @@ void HMGTurret_FindEnemy( gentity_t *self )
}
}
+ // bleeder retribution
+ if( level.bleeders )
+ {
+ for( i = 0; i < num; i++ )
+ {
+ target = &g_entities[ entityList[ i ] ];
+
+ if( target->client && target->client->pers.bleeder )
+ {
+ if( !HMGTurret_CheckTarget( self, target, qfalse ) )
+ continue;
+
+ self->enemy = target;
+ return;
+ }
+ }
+ }
+
//couldn't find a target
self->enemy = NULL;
}
@@ -2239,7 +2392,7 @@ void HMGTurret_Think( gentity_t *self )
//if not powered don't do anything and check again for power next think
if( !( self->powered = G_FindPower( self ) ) )
{
- if( self->spawned )
+ if( g_turretAim.integer && self->spawned )
{
// unpowered turret barrel falls to bottom of range
float droop;
@@ -2254,7 +2407,7 @@ void HMGTurret_Think( gentity_t *self )
return;
}
}
-
+
self->nextthink = level.time + POWER_REFRESH_TIME;
return;
}
@@ -2275,9 +2428,24 @@ void HMGTurret_Think( gentity_t *self )
//if a new target cannot be found don't do anything
if( !self->enemy )
+ {
+ if( g_turretAim.integer &&
+ self->rotatorAngle >= 360.0f &&
+ level.time > self->last_move_time + TURRET_REST_TIME )
+ {
+ float diff;
+
+ diff = AngleSubtract( self->s.angles2[ YAW ], self->rotatorAngle - 360.0f );
+ if( diff < -TURRET_REST_TOLERANCE )
+ self->s.angles2[ YAW ] += TURRET_REST_SPEED;
+ else if ( diff > TURRET_REST_TOLERANCE )
+ self->s.angles2[ YAW ] -= TURRET_REST_SPEED;
+ }
return;
+ }
self->enemy->targeted = self;
+ self->last_move_time = level.time;
//if we are pointing at our target and we can fire shoot it
if( HMGTurret_TrackEnemy( self ) && ( self->count < level.time ) )
@@ -2346,7 +2514,8 @@ void HTeslaGen_Think( gentity_t *self )
if( enemy->flags & FL_NOTARGET )
continue;
- if( enemy->client && enemy->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS &&
+ if( enemy->client &&
+ ( enemy->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS || enemy->client->pers.bleeder ) &&
enemy->health > 0 &&
!level.paused && !enemy->client->pers.paused &&
Distance( enemy->s.pos.trBase, self->s.pos.trBase ) <= TESLAGEN_RANGE )
@@ -2404,6 +2573,9 @@ void HSpawn_Disappear( gentity_t *self )
self->r.contents = 0; //stop collisions...
trap_LinkEntity( self ); //...requires a relink
+
+ self->s.eFlags |= EF_DEAD;
+ G_RecoverBuildPoints( self );
}
@@ -2436,6 +2608,9 @@ void HSpawn_Blast( gentity_t *self )
self->r.contents = 0; //stop collisions...
trap_LinkEntity( self ); //...requires a relink
+
+ self->s.eFlags |= EF_DEAD;
+ G_RecoverBuildPoints( self );
}
@@ -2451,6 +2626,7 @@ void HSpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
buildHistory_t *new;
new = G_Alloc( sizeof( buildHistory_t ) );
new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID;
+ new->time = level.time;
new->ent = ( attacker && attacker->client ) ? attacker : NULL;
if( new->ent )
new->name[ 0 ] = 0;
@@ -2485,6 +2661,8 @@ void HSpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
self->nextthink = level.time; //blast immediately
}
+ G_RecoverSetKiller( self, attacker );
+
if( attacker && attacker->client )
{
if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
@@ -2769,6 +2947,50 @@ qboolean G_BuildableRange( vec3_t origin, float r, buildable_t buildable )
return qfalse;
}
+/*
+===============
+G_BuildableRangeClosest
+
+Find Closest distance to a type of buildable, -1
+===============
+*/
+float G_BuildableRangeClosest( vec3_t origin, float r, buildable_t buildable )
+{
+ int entityList[ MAX_GENTITIES ];
+ vec3_t range;
+ vec3_t mins, maxs;
+ int i, num;
+ gentity_t *ent;
+ float found = -1.0;
+
+ VectorSet( range, r, r, r );
+ VectorAdd( origin, range, maxs );
+ VectorSubtract( origin, range, mins );
+
+ num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
+ for( i = 0; i < num; i++ )
+ {
+ ent = &g_entities[ entityList[ i ] ];
+
+ if( ent->s.eType != ET_BUILDABLE )
+ continue;
+
+ if( ent->biteam == BIT_HUMANS && !ent->powered )
+ continue;
+
+ if( ent->s.modelindex == buildable && ent->spawned )
+ {
+ float dist;
+
+ dist = Distance( origin, ent->s.origin );
+ if( found < 0.0 || dist < found )
+ found = dist;
+ }
+ }
+
+ return found;
+}
+
static qboolean G_BoundsIntersect(const vec3_t mins, const vec3_t maxs,
const vec3_t mins2, const vec3_t maxs2)
{
@@ -2851,7 +3073,6 @@ static int G_CompareBuildablesForRemoval( const void *a, const void *b )
buildableA = *(gentity_t **)a;
buildableB = *(gentity_t **)b;
-
// Prefer the one that collides with the thing we're building
aMatches = G_BuildablesIntersect( cmpBuildable, cmpOrigin,
buildableA->s.modelindex, buildableA->s.origin );
@@ -2909,6 +3130,7 @@ void G_FreeMarkedBuildables( void )
new = G_Alloc( sizeof( buildHistory_t ) );
new->ID = -1;
+ new->time = 0;
new->ent = NULL;
Q_strncpyz( new->name, "<markdecon>", 12 );
new->buildable = ent->s.modelindex;
@@ -3035,12 +3257,7 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable,
// Don't allow destruction of hovel with granger inside
if( ent->s.modelindex == BA_A_HOVEL && ent->active )
- {
- if( buildable == BA_A_HOVEL )
- return IBE_HOVEL;
- else
- continue;
- }
+ continue;
// Explicitly disallow replacement of the core buildable with anything
// other than the core buildable
@@ -3163,18 +3380,17 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
{
vec3_t angles;
vec3_t entity_origin, normal;
- vec3_t mins, maxs, nbmins, nbmaxs;
- vec3_t nbVect;
+ vec3_t mins, maxs;
trace_t tr1, tr2, tr3;
int i;
itemBuildError_t reason = IBE_NONE;
+ itemBuildError_t old_reason = IBE_NONE;
gentity_t *tempent;
float minNormal;
qboolean invert;
int contents;
playerState_t *ps = &ent->client->ps;
int buildPoints;
- gentity_t *tmp;
itemBuildError_t tempReason;
// Stop all buildables from interacting with traces
@@ -3200,28 +3416,13 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
if( tr1.entityNum != ENTITYNUM_WORLD )
reason = IBE_NORMAL;
+ // check nobuild zones
+ if( nobuild_check( entity_origin ) >= 0 )
+ reason = IBE_PERMISSION;
+
contents = trap_PointContents( entity_origin, -1 );
buildPoints = BG_FindBuildPointsForBuildable( buildable );
-
- //check if we are near a nobuild marker, if so, can't build here...
- for( i = 0; i < MAX_GENTITIES; i++ )
- {
- tmp = &g_entities[ i ];
-
- if( !tmp->noBuild.isNB )
- continue;
-
- nbVect[0] = tmp->noBuild.Area;
- nbVect[1] = tmp->noBuild.Area;
- nbVect[2] = tmp->noBuild.Height;
-
- VectorSubtract( origin, nbVect, nbmins );
- VectorAdd( origin, nbVect, nbmaxs );
-
- if( trap_EntityContact( nbmins, nbmaxs, tmp ) )
- reason = IBE_PERMISSION;
-
- }
+
if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
{
//alien criteria
@@ -3276,6 +3477,7 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
switch( buildable )
{
case BA_A_OVERMIND:
+ old_reason = reason;
reason = IBE_OVERMIND;
break;
@@ -3359,6 +3561,7 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
if( tempent->s.modelindex == BA_H_REACTOR && !tempent->deconstruct )
{
+ old_reason = reason;
reason = IBE_REACTOR;
break;
}
@@ -3369,6 +3572,38 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance
if( ( tempReason = G_SufficientBPAvailable( buildable, origin ) ) != IBE_NONE )
reason = tempReason;
+ if( g_modBuildableCount.integer > 1 && old_reason == IBE_NONE )
+ {
+ if( reason == IBE_REACTOR )
+ {
+ int rcs = 0;
+
+ for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ )
+ {
+ if( tempent->s.eType == ET_BUILDABLE &&
+ tempent->s.modelindex == BA_H_REACTOR &&
+ !tempent->deconstruct )
+ rcs++;
+ }
+ if( rcs < g_modBuildableCount.integer )
+ reason = IBE_NONE;
+ }
+ else if( reason == IBE_OVERMIND )
+ {
+ int oms = 0;
+
+ for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ )
+ {
+ if( tempent->s.eType == ET_BUILDABLE &&
+ tempent->s.modelindex == BA_A_OVERMIND &&
+ !tempent->deconstruct )
+ oms++;
+ }
+ if( oms < g_modBuildableCount.integer )
+ reason = IBE_NONE;
+ }
+ }
+
// Relink buildables
G_SetBuildableLinkState( qtrue );
@@ -3498,8 +3733,10 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori
built->buildTime = built->s.time = level.time;
built->spawnBlockTime = 0;
+ built->killedBy = ENTITYNUM_NONE;
+
// build instantly in cheat mode
- if( builder->client && g_cheats.integer )
+ if( builder->client && g_cheats.integer || g_instantBuild.integer )
{
built->health = BG_FindHealthForBuildable( buildable );
built->buildTime = built->s.time =
@@ -3704,6 +3941,7 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori
{
new = level.buildHistory;
new->ID = ( ++level.lastBuildID > 1000 ) ? ( level.lastBuildID = 1 ) : level.lastBuildID;
+ new->time = level.time;
new->ent = builder;
new->name[ 0 ] = 0;
new->buildable = buildable;
@@ -3722,39 +3960,6 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori
return built;
}
-static void G_SpawnMarker( vec3_t origin )
-{
- gentity_t *nb;
- int i;
-
- // Make the marker...
- nb = G_Spawn( );
- nb->s.modelindex = 0; //Coder humor is win
- VectorCopy( origin, nb->s.pos.trBase );
- VectorCopy( origin, nb->r.currentOrigin );
- nb->noBuild.isNB = qtrue;
- nb->noBuild.Area = level.nbArea;
- nb->noBuild.Height = level.nbHeight;
- trap_LinkEntity( nb );
-
- // Log markers made...
- for( i = 0; i < MAX_GENTITIES; i++ )
- {
- if( level.nbMarkers[ i ].Marker != NULL )
- continue;
-
- level.nbMarkers[ i ].Marker = nb;
- VectorCopy( origin, level.nbMarkers[ i ].Origin );
- SnapVector( level.nbMarkers[ i ].Origin );
- break;
- }
-
- // End nobuild mode...
- level.noBuilding = qfalse;
- level.nbArea = 0.0f;
- level.nbHeight = 0.0f;
-}
-
/*
=================
G_BuildIfValid
@@ -3770,15 +3975,6 @@ qboolean G_BuildIfValid( gentity_t *ent, buildable_t buildable )
switch( G_CanBuild( ent, buildable, dist, origin ) )
{
case IBE_NONE:
- if( level.noBuilding )
- {
- vec3_t mins;
- BG_FindBBoxForBuildable( buildable, mins, NULL );
- origin[2] += mins[2];
-
- G_SpawnMarker( origin );
- return qtrue;
- }
G_Build( ent, buildable, origin, ent->s.apos.trBase );
return qtrue;
@@ -3844,40 +4040,16 @@ qboolean G_BuildIfValid( gentity_t *ent, buildable_t buildable )
return qfalse;
case IBE_SPWNWARN:
- if( level.noBuilding )
- {
- vec3_t mins;
- BG_FindBBoxForBuildable( buildable, mins, NULL );
- origin[2] += mins[2];
- G_SpawnMarker( origin );
- return qtrue;
- }
G_TriggerMenu( ent->client->ps.clientNum, MN_A_SPWNWARN );
G_Build( ent, buildable, origin, ent->s.apos.trBase );
return qtrue;
case IBE_TNODEWARN:
- if( level.noBuilding )
- {
- vec3_t mins;
- BG_FindBBoxForBuildable( buildable, mins, NULL );
- origin[2] += mins[2];
- G_SpawnMarker( origin );
- return qtrue;
- }
G_TriggerMenu( ent->client->ps.clientNum, MN_H_TNODEWARN );
G_Build( ent, buildable, origin, ent->s.apos.trBase );
return qtrue;
case IBE_RPTWARN:
- if( level.noBuilding )
- {
- vec3_t mins;
- BG_FindBBoxForBuildable( buildable, mins, NULL );
- origin[2] += mins[2];
- G_SpawnMarker( origin );
- return qtrue;
- }
G_TriggerMenu( ent->client->ps.clientNum, MN_H_RPTWARN );
G_Build( ent, buildable, origin, ent->s.apos.trBase );
return qtrue;
@@ -4583,128 +4755,624 @@ void G_BaseSelfDestruct( pTeam_t team )
return "<buildlog entry expired>";
}
+
/*
-============
-G_NobuildLoad
+ * =======
+ * nobuild
+ * =======
+ */
-load the nobuild markers that were previously saved (if there are any).
-============
-*/
-void G_NobuildLoad( void )
+#define MAX_NOBUILD_ZONES 16
+#define NOBUILD_THINK_TIME 200
+
+typedef struct
+{
+ qboolean in_use;
+ vec3_t origin;
+ vec3_t size;
+}
+nobuild_t;
+
+typedef struct
+{
+ vec3_t dir;
+ float yaw;
+}
+nobuild_corner_t;
+
+static nobuild_corner_t nobuildCorner[] = {
+ { { -1, -1, -1 }, 0 },
+ { { -1, 1, -1 }, 270 },
+ { { 1, -1, -1 }, 90 },
+ { { 1, 1, -1 }, 180 },
+
+ { { -1, -1, 1 }, 270 },
+ { { -1, 1, 1 }, 180 },
+ { { 1, -1, 1 }, 0 },
+ { { 1, 1, 1 }, 90 }
+};
+#define MAX_NOBUILD_CORNERS ( sizeof( nobuildCorner ) / sizeof( nobuild_corner_t ) )
+
+typedef enum
+{
+ NB_ORIGIN_X,
+ NB_ORIGIN_Y,
+ NB_ORIGIN_Z,
+ NB_SIZE_X,
+ NB_SIZE_Y,
+ NB_SIZE_Z
+}
+nobuild_sizer_t;
+#define NB_COUNT ( NB_SIZE_Z + 1 )
+
+static nobuild_t noBuildZones[ MAX_NOBUILD_ZONES ];
+
+static qboolean noBuildActive = qfalse;
+static int noBuildEditZone = 0;
+static nobuild_sizer_t noBuildEditType = NB_ORIGIN_X;
+
+void nobuild_init( void )
{
- fileHandle_t f;
- int len;
- char *nobuild;
char map[ MAX_QPATH ];
- vec3_t origin = { 0.0f, 0.0f, 0.0f };
char line[ MAX_STRING_CHARS ];
- int i = 0;
- int i2;
- gentity_t *nb;
- float area;
- float height;
+ char *data, *pool;
+ fileHandle_t f;
+ int len;
+ int lcount = 0;
+ int zonecount = 0;
+ int i;
+ int id;
+ vec3_t origin, size;
+
+ memset( noBuildZones, 0, sizeof( noBuildZones ) );
trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) );
- len = trap_FS_FOpenFile( va( "nobuild/%s.dat", map ),
- &f, FS_READ );
+ len = trap_FS_FOpenFile( va( "layouts/%s/nobuild.nbd", map ), &f, FS_READ );
if( len < 0 )
{
- // This isn't needed since nobuild is pretty much optional...
- //G_Printf( "ERROR: nobuild for %s could not be opened\n", map );
return;
}
- nobuild = G_Alloc( len + 1 );
- trap_FS_Read( nobuild, len, f );
- *( nobuild + len ) = '\0';
+ data = pool = G_Alloc( len + 1 );
+ trap_FS_Read( data, len, f );
+ *( data + len ) = '\0';
trap_FS_FCloseFile( f );
- while( *nobuild )
+
+ i = 0;
+ while( *data )
{
if( i >= sizeof( line ) - 1 )
{
- return;
+ G_Printf( S_COLOR_RED "ERROR: line overflow in %s before \"%s\"\n",
+ va( "layouts/%s/nobuild.nbd", map ), line );
}
-
- line[ i++ ] = *nobuild;
+ line[ i++ ] = *data;
line[ i ] = '\0';
- if( *nobuild == '\n' )
+ if( *data == '\n' )
{
- i = 0;
- sscanf( line, "%f %f %f %f %f\n",
- &origin[ 0 ], &origin[ 1 ], &origin[ 2 ], &area, &height );
-
- // Make the marker...
- nb = G_Spawn( );
- nb->s.modelindex = 0;
- VectorCopy( origin, nb->s.pos.trBase );
- VectorCopy( origin, nb->r.currentOrigin );
- nb->noBuild.isNB = qtrue;
- nb->noBuild.Area = area;
- nb->noBuild.Height = height;
- trap_LinkEntity( nb );
-
- // Log markers made...
- for( i = 0; i < MAX_GENTITIES; i++ )
- {
- if( level.nbMarkers[ i ].Marker != NULL )
- continue;
-
- level.nbMarkers[ i ].Marker = nb;
- VectorCopy( origin, level.nbMarkers[ i ].Origin );
- SnapVector( level.nbMarkers[ i ].Origin );
- break;
- }
-
+ i = 0;
+ lcount++;
+ id = -1;
+ if( sscanf( line, "%d %f %f %f %f %f %f\n",
+ &id,
+ &origin[ 0 ], &origin[ 1 ], &origin[ 2 ],
+ &size[ 0 ], &size[ 1 ], &size[ 2 ]) == 7 )
+ {
+ if( zonecount >= MAX_NOBUILD_ZONES )
+ {
+ G_Printf( S_COLOR_YELLOW "WARNING: nobuild zone count exceeded on line %d\n", lcount );
+ break;
+ }
+ noBuildZones[ zonecount ].in_use = qtrue;
+ VectorCopy( origin, noBuildZones[ zonecount ].origin );
+ VectorCopy( size, noBuildZones[ zonecount ].size );
+ zonecount++;
+ }
+ else
+ {
+ G_Printf( S_COLOR_YELLOW "WARNING: nobuild data parse error on line %d\n", lcount );
+ }
}
- nobuild++;
+ data++;
}
+
+ G_Free( pool );
+
+ if( zonecount )
+ G_Printf( "nobuild: loaded %d zones\n", zonecount );
}
-/*
-============
-G_NobuildSave
-Save all currently placed nobuild markers into the "nobuild" folder
-============
-*/
-void G_NobuildSave( void )
+static void nobuild_sizer_think( gentity_t *self )
+{
+ int zone, size;
+ vec3_t origin;
+
+ if( !noBuildActive )
+ {
+ G_FreeEntity( self );
+ return;
+ }
+
+ self->nextthink = level.time + NOBUILD_THINK_TIME;
+
+ zone = noBuildEditZone;
+ if( zone < 0 || zone >= MAX_NOBUILD_ZONES || !noBuildZones[ zone ].in_use )
+ {
+ if( self->r.linked )
+ trap_UnlinkEntity( self );
+ return;
+ }
+
+ size = self->damage;
+ VectorCopy( noBuildZones[ zone ].origin, origin );
+ switch ( noBuildEditType )
+ {
+ case NB_ORIGIN_X:
+ case NB_SIZE_X:
+ origin[ 0 ] += size;
+ break;
+ case NB_ORIGIN_Y:
+ case NB_SIZE_Y:
+ origin[ 1 ] += size;
+ break;
+ case NB_ORIGIN_Z:
+ case NB_SIZE_Z:
+ origin[ 2 ] += size;
+ break;
+ }
+
+ VectorCopy( origin, self->s.origin );
+ VectorCopy( origin, self->s.pos.trBase );
+ VectorCopy( origin, self->r.currentOrigin );
+
+ if( self->watertype != noBuildEditType )
+ {
+ self->watertype = noBuildEditType;
+ switch( noBuildEditType )
+ {
+ case NB_ORIGIN_X:
+ case NB_ORIGIN_Y:
+ case NB_ORIGIN_Z:
+ self->s.modelindex = G_ModelIndex( "models/players/human_base/jetpack.md3" );
+ break;
+ case NB_SIZE_X:
+ case NB_SIZE_Y:
+ case NB_SIZE_Z:
+ self->s.modelindex = G_ModelIndex( "models/players/human_base/battpack.md3" );
+ break;
+ }
+ }
+
+ if( !self->r.linked )
+ trap_LinkEntity( self );
+}
+
+static gentity_t *nobuild_sizer( float distance )
+{
+ gentity_t *self;
+
+ self = G_Spawn( );
+ self->classname = "nobuild_sizer";
+ self->s.eType = ET_GENERAL;
+
+ self->s.time = level.time;
+ self->s.pos.trType = TR_STATIONARY;
+ self->s.pos.trTime = level.time;
+
+ self->r.svFlags = SVF_BROADCAST;
+
+ self->damage = distance;
+ self->watertype = -1;
+ self->think = nobuild_sizer_think;
+
+ // run a think to set positions
+ nobuild_sizer_think( self );
+
+ return self;
+}
+
+static void nobuild_corner_think( gentity_t *self )
+{
+ int zone, corner;
+ vec3_t origin;
+
+ zone = self->count;
+ corner = self->damage;
+
+ if( !noBuildActive || !noBuildZones[ zone ].in_use )
+ {
+ G_FreeEntity( self );
+ return;
+ }
+
+ self->nextthink = level.time + NOBUILD_THINK_TIME;
+
+ VectorCopy( noBuildZones[ zone ].origin, origin );
+ origin[ 0 ] += noBuildZones[ zone ].size[ 0 ] * nobuildCorner[ corner ].dir[ 0 ];
+ origin[ 1 ] += noBuildZones[ zone ].size[ 1 ] * nobuildCorner[ corner ].dir[ 1 ];
+ origin[ 2 ] += noBuildZones[ zone ].size[ 2 ] * nobuildCorner[ corner ].dir[ 2 ];
+
+ VectorCopy( origin, self->s.origin );
+ VectorCopy( origin, self->s.pos.trBase );
+ VectorCopy( origin, self->r.currentOrigin );
+}
+
+static gentity_t *nobuild_corner( int zone, int corner )
+{
+ gentity_t *self;
+
+ if( zone < 0 || zone >= MAX_NOBUILD_ZONES )
+ return NULL;
+ if( !noBuildZones[ zone ].in_use )
+ return NULL;
+ if( corner < 0 || corner >= MAX_NOBUILD_CORNERS )
+ return NULL;
+
+ self = G_Spawn( );
+ self->classname = "nobuild_corner";
+ self->s.eType = ET_GENERAL;
+ self->s.modelindex = 9999;
+
+ self->s.time = level.time;
+ self->s.pos.trType = TR_STATIONARY;
+ self->s.pos.trTime = level.time;
+
+ self->count = zone;
+ self->damage = corner;
+ self->think = nobuild_corner_think;
+
+ // run a think to set positions
+ nobuild_corner_think( self );
+
+ self->s.apos.trBase[ YAW ] = nobuildCorner[ corner ].yaw;
+ if( nobuildCorner[ corner ].dir[ 2 ] > 0.0f )
+ self->s.apos.trBase[ PITCH ] = 180.0f;
+
+ trap_LinkEntity( self );
+
+ return self;
+}
+
+static void nobuild_think( gentity_t *self )
+{
+ int zone;
+
+ zone = self->count;
+
+ if( !noBuildActive || !noBuildZones[ zone ].in_use )
+ {
+ G_FreeEntity( self );
+ return;
+ }
+
+ self->nextthink = level.time + NOBUILD_THINK_TIME;
+ VectorCopy( noBuildZones[ zone ].origin, self->s.origin );
+ VectorCopy( noBuildZones[ zone ].origin, self->s.pos.trBase );
+ VectorCopy( noBuildZones[ zone ].origin, self->r.currentOrigin );
+}
+
+static gentity_t *nobuild_spawn( int zone )
+{
+ gentity_t *self;
+ int i;
+
+ if( zone < 0 || zone >= MAX_NOBUILD_ZONES )
+ return NULL;
+ if( !noBuildZones[ zone ].in_use )
+ return NULL;
+
+ self = G_Spawn( );
+ self->classname = "nobuild_zone";
+ self->s.eType = ET_GENERAL;
+ self->s.modelindex = G_ModelIndex( "models/buildables/repeater/repeater.md3" );
+
+ self->s.time = level.time;
+ self->s.pos.trType = TR_STATIONARY;
+ self->s.pos.trTime = level.time;
+
+ self->think = nobuild_think;
+ self->nextthink = level.time + NOBUILD_THINK_TIME;
+
+ VectorCopy( noBuildZones[ zone ].origin, self->s.origin );
+ VectorCopy( noBuildZones[ zone ].origin, self->s.pos.trBase );
+ VectorCopy( noBuildZones[ zone ].origin, self->r.currentOrigin );
+
+ self->s.apos.trBase[ PITCH ] = 180.0f;
+
+ self->count = zone;
+
+ trap_LinkEntity( self );
+
+ for( i = 0; i < MAX_NOBUILD_CORNERS; i++ )
+ nobuild_corner( zone, i );
+
+ return self;
+}
+
+static void nobuild_on( gentity_t *ent )
+{
+ int i;
+
+ if( noBuildActive )
+ return;
+
+ noBuildActive = qtrue;
+ noBuildEditZone = MAX_NOBUILD_ZONES - 1;
+ noBuildEditType = NB_ORIGIN_X;
+
+ nobuild_command( ent, qtrue, qfalse, 0.0f );
+
+ for( i = 0; i < MAX_NOBUILD_ZONES; i++ )
+ {
+ nobuild_spawn( i );
+ }
+
+ nobuild_sizer( 32.0f );
+ nobuild_sizer( -32.0f );
+
+ trap_SendServerCommand( -1,
+ va( "print \"^3!nobuild ^7edit mode enabled by %s\n\"",
+ ( ent ) ? ent->client->pers.netname : "console " ) );
+}
+
+static void nobuild_off( gentity_t *ent )
+{
+ if( noBuildActive )
+ {
+ noBuildActive = qfalse;
+
+ trap_SendServerCommand( -1,
+ va( "print \"^3!nobuild ^7edit mode disabled by %s\n\"",
+ ( ent ) ? ent->client->pers.netname : "console " ) );
+ }
+}
+
+void nobuild_add( gentity_t *ent )
+{
+ int i;
+
+ if( !noBuildActive )
+ {
+ ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" );
+ return;
+ }
+
+ if( !ent )
+ return;
+
+ for( i = 0; i < MAX_NOBUILD_ZONES; i++ )
+ {
+ if( noBuildZones[ i ].in_use )
+ continue;
+
+ noBuildZones[ i ].in_use = qtrue;
+ VectorCopy( ent->s.origin, noBuildZones[ i ].origin );
+ VectorSet( noBuildZones[ i ].size, 64.0f, 64.0f, 64.0f );
+
+ noBuildEditZone = i;
+ nobuild_spawn( i );
+
+ ADMP( va( "added nobuild zone #%d\n", i ) );
+ return;
+ }
+
+ ADMP( "maximum nobuild zones reached, can not add another zone.\n" );
+}
+
+void nobuild_del( gentity_t *ent )
+{
+ int zone;
+
+ if( !noBuildActive )
+ {
+ ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" );
+ return;
+ }
+
+ if( noBuildEditZone < 0 || noBuildEditZone >= MAX_NOBUILD_ZONES ||
+ !noBuildZones[ noBuildEditZone ].in_use )
+ {
+ ADMP( "a nobuild zone is not selected\n" );
+ return;
+ }
+
+ zone = noBuildEditZone;
+ nobuild_command( ent, qtrue, qfalse, 0.0f );
+ noBuildZones[ zone ].in_use = qfalse;
+ if( noBuildEditZone == zone )
+ noBuildEditZone = -1;
+
+ ADMP( va( "removed nobuild zone #%d\n", zone ) );
+}
+
+#define SIZE_MIN( a, b ) ( a = ( a + b < 32.0 ? a = 32.0 : a + b ) )
+
+void nobuild_command( gentity_t *ent, qboolean next_zone, qboolean next_type, float size )
+{
+ int n;
+
+ if( !noBuildActive )
+ {
+ ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" );
+ return;
+ }
+
+ if( next_zone )
+ {
+ n = noBuildEditZone + 1;
+ while( n != noBuildEditZone )
+ {
+ if( n >= MAX_NOBUILD_ZONES )
+ n = 0;
+ if( noBuildZones[ n ].in_use )
+ {
+ noBuildEditZone = n;
+ ADMP( va ( "nobuild zone %d selected\n", n ) );
+ return;
+ }
+ n++;
+ }
+ }
+ else if( next_type )
+ {
+ noBuildEditType++;
+ if ( noBuildEditType >= NB_COUNT )
+ noBuildEditType = 0;
+ }
+ else if( size )
+ {
+ switch( noBuildEditType )
+ {
+ case NB_ORIGIN_X:
+ noBuildZones[ noBuildEditZone ].origin[ 0 ] += size;
+ break;
+ case NB_ORIGIN_Y:
+ noBuildZones[ noBuildEditZone ].origin[ 1 ] += size;
+ break;
+ case NB_ORIGIN_Z:
+ noBuildZones[ noBuildEditZone ].origin[ 2 ] += size;
+ break;
+ case NB_SIZE_X:
+ SIZE_MIN( noBuildZones[ noBuildEditZone ].size[ 0 ], size );
+ break;
+ case NB_SIZE_Y:
+ SIZE_MIN( noBuildZones[ noBuildEditZone ].size[ 1 ], size );
+ break;
+ case NB_SIZE_Z:
+ SIZE_MIN( noBuildZones[ noBuildEditZone ].size[ 2 ], size );
+ break;
+ }
+ }
+}
+
+void nobuild_go( gentity_t *ent )
+{
+ if( !ent )
+ return;
+
+ if( !noBuildActive )
+ {
+ ADMP( "!nobuild is not on, use '!nobuild on' to enable it\n" );
+ return;
+ }
+ if( noBuildEditZone < 0 || noBuildEditZone >= MAX_NOBUILD_ZONES ||
+ !noBuildZones[ noBuildEditZone ].in_use )
+ {
+ ADMP( "a nobuild zone is not selected\n" );
+ return;
+ }
+
+ VectorCopy( noBuildZones[ noBuildEditZone ].origin, ent->client->ps.origin );
+}
+
+void nobuild_set( qboolean enable, gentity_t *ent )
+{
+ if( !ent && enable )
+ {
+ ADMP( "only a connected client can enable nobuild mode\n" );
+ return;
+ }
+ if( enable )
+ {
+ nobuild_on( ent );
+ }
+ else
+ {
+ nobuild_off( ent );
+ }
+}
+
+void nobuild_save( void )
{
char map[ MAX_QPATH ];
char fileName[ MAX_OSPATH ];
fileHandle_t f;
int len;
+ int count = 0;
int i;
- gentity_t *ent;
char *s;
trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) );
if( !map[ 0 ] )
{
- G_Printf( "NobuildSave( ): no map is loaded\n" );
+ G_Printf( "LayoutSave( ): no map is loaded\n" );
return;
}
- Com_sprintf( fileName, sizeof( fileName ), "nobuild/%s.dat", map );
-
+ Com_sprintf( fileName, sizeof( fileName ), "layouts/%s/nobuild.nbd", map );
len = trap_FS_FOpenFile( fileName, &f, FS_WRITE );
- if( len < 0 )
+ if( len < 0 )
{
- G_Printf( "nobuildsave: could not open %s\n", fileName );
+ G_Printf( "layoutsave: could not open %s\n", fileName );
return;
}
- G_Printf("nobuildsave: saving nobuild to %s\n", fileName );
+ G_Printf("layoutsave: saving layout to %s\n", fileName );
- for( i = 0; i < MAX_GENTITIES; i++ )
+ for( i = 0; i < MAX_NOBUILD_ZONES; i++ )
{
- ent = &level.gentities[ i ];
- if( ent->noBuild.isNB != qtrue )
+ if( !noBuildZones[ i ].in_use )
continue;
- s = va( "%f %f %f %f %f\n",
- ent->r.currentOrigin[ 0 ],
- ent->r.currentOrigin[ 1 ],
- ent->r.currentOrigin[ 2 ],
- ent->noBuild.Area,
- ent->noBuild.Height );
+ s = va( "%d %f %f %f %f %f %f\n",
+ count,
+ noBuildZones[ i ].origin[ 0 ],
+ noBuildZones[ i ].origin[ 1 ],
+ noBuildZones[ i ].origin[ 2 ],
+ noBuildZones[ i ].size[ 0 ],
+ noBuildZones[ i ].size[ 1 ],
+ noBuildZones[ i ].size[ 2 ] );
trap_FS_Write( s, strlen( s ), f );
+ count++;
}
+
trap_FS_FCloseFile( f );
+ G_Printf( "saved %d nobuild zones\n", count );
}
+
+void nobuild_list( gentity_t *ent )
+{
+ int count = 0;
+ int i;
+
+ ADMBP_begin();
+ for( i = 0; i < MAX_NOBUILD_ZONES; i++ )
+ {
+ if( !noBuildZones[ i ].in_use )
+ continue;
+
+ ADMBP( va( "%2d @ x: %6.2f, y: %6.2f, z: %6.2f - %4.2f x %4.2f x %4.2f\n",
+ i,
+ noBuildZones[ i ].origin[ 0 ],
+ noBuildZones[ i ].origin[ 1 ],
+ noBuildZones[ i ].origin[ 2 ],
+ noBuildZones[ i ].size[ 0 ],
+ noBuildZones[ i ].size[ 1 ],
+ noBuildZones[ i ].size[ 2 ] ) );
+ count++;
+ }
+
+ ADMBP( va( "nobuild contains %d zones\n", count ) );
+
+ ADMBP_end();
+}
+
+int nobuild_check( vec3_t origin )
+{
+ int i;
+
+ for( i = 0; i < MAX_NOBUILD_ZONES; i ++ )
+ {
+ if( !noBuildZones[ i ].in_use )
+ continue;
+
+ if( origin[ 0 ] > noBuildZones[ i ].origin[ 0 ] - noBuildZones[ i ].size[ 0 ] &&
+ origin[ 0 ] < noBuildZones[ i ].origin[ 0 ] + noBuildZones[ i ].size[ 0 ] &&
+ origin[ 1 ] > noBuildZones[ i ].origin[ 1 ] - noBuildZones[ i ].size[ 1 ] &&
+ origin[ 1 ] < noBuildZones[ i ].origin[ 1 ] + noBuildZones[ i ].size[ 1 ] &&
+ origin[ 2 ] > noBuildZones[ i ].origin[ 2 ] - noBuildZones[ i ].size[ 2 ] &&
+ origin[ 2 ] < noBuildZones[ i ].origin[ 2 ] + noBuildZones[ i ].size[ 2 ] )
+ return i;
+ }
+
+ return -1;
+}
+
diff --git a/src/game/g_client.c b/src/game/g_client.c
index 3d274ee..abdfa8e 100644
--- a/src/game/g_client.c
+++ b/src/game/g_client.c
@@ -910,7 +910,7 @@ team_t TeamCount( int ignoreClientNum, int team )
ClientCleanName
============
*/
-static void ClientCleanName( const char *in, char *out, int outSize, qboolean special )
+static void ClientCleanName( const char *in, char *out, int outSize )
{
int len, colorlessLen;
char ch;
@@ -957,8 +957,8 @@ static void ClientCleanName( const char *in, char *out, int outSize, qboolean sp
break;
}
- // don't allow black in a name, unless if special
- if( ColorIndex( *in ) == 0 && !special )
+ // don't allow black in a name, period
+ if( ColorIndex( *in ) == 0 )
*out++ = COLOR_WHITE;
else
*out++ = *in;
@@ -1154,11 +1154,7 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )
// set name
Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) );
s = Info_ValueForKey( userinfo, "name" );
-
- if ( !G_admin_permission( ent, ADMF_SPECIALNAME ) )
- ClientCleanName( s, newname, sizeof( newname ), qfalse );
- else
- ClientCleanName( s, newname, sizeof( newname ), qtrue );
+ ClientCleanName( s, newname, sizeof( newname ) );
if( strcmp( oldname, newname ) )
{
@@ -1166,10 +1162,7 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )
showRenameMsg = qfalse;
// in case we need to revert and there's no oldname
- if ( !G_admin_permission( ent, ADMF_SPECIALNAME ) )
- ClientCleanName( va( "%s", client->pers.netname ), oldname, sizeof( oldname ), qfalse );
- else
- ClientCleanName( va( "%s", client->pers.netname ), oldname, sizeof( oldname ), qtrue );
+ ClientCleanName( va( "%s", client->pers.netname ), oldname, sizeof( oldname ) );
if( g_newbieNumbering.integer )
{
@@ -1198,8 +1191,7 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )
revertName = qtrue;
}
else if( g_maxNameChanges.integer > 0
- && client->pers.nameChanges >= g_maxNameChanges.integer
- && !G_admin_permission( ent, ADMF_SPECIAL ) )
+ && client->pers.nameChanges >= g_maxNameChanges.integer )
{
trap_SendServerCommand( ent - g_entities, va(
"print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"",
@@ -1248,7 +1240,8 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )
//dont show if players invisible
if( client->sess.invisible != qtrue )
trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE
- " renamed to %s^7\n\"", oldname, client->pers.netname ) );
+ " renamed to %s^7\n\"", oldname, client->pers.netname ) );
+
if( g_decolourLogfiles.integer)
{
char decoloured[ MAX_STRING_CHARS ] = "";
@@ -1367,17 +1360,18 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )
if ( client->sess.invisible != qtrue )
{
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\\ig\\%16s",
- client->pers.netname, team, model, model, c1, c2,
- client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask,
- teamLeader, BG_ClientListString( &client->sess.ignoreList ) );
+ "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, BG_ClientListString( &client->sess.ignoreList ) );
trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo );
} else {
trap_SetConfigstring( CS_PLAYERS + clientNum, "" );
}
+
/*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/
}
@@ -1426,6 +1420,7 @@ char *ClientConnect( int clientNum, qboolean firstTime )
return va( "%s", reason );
}
+
// IP filtering
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500
// recommanding PB based IP / GUID banning, the builtin system is pretty limited
@@ -1443,13 +1438,38 @@ char *ClientConnect( int clientNum, qboolean firstTime )
if( G_FilterPacket( value ) )
return "You are banned from this server.";
- if( ip[ 0 ] == 0 ||strlen( ip ) < 7 )
+ if( strlen( ip ) < 7 )
{
G_AdminsPrintf( "Connect from client with invalid IP: '%s' NAME: '%s^7'\n",
- ip, Info_ValueForKey( userinfo, "name" ) );
+ ip, Info_ValueForKey( userinfo, "name" ) );
return "Invalid client data";
}
+ // limit max clients per IP
+ if( g_maxGhosts.integer > 1 )
+ {
+ gclient_t *other;
+ int count = 0;
+
+ for( i = 0 ; i < level.maxclients; i++ )
+ {
+ other = &level.clients[ i ];
+ if( other &&
+ ( other->pers.connected == CON_CONNECTED || other->pers.connected == CON_CONNECTING ) &&
+ strcmp( ip, other->pers.ip ) == 0 )
+ {
+ count++;
+ }
+ }
+
+ if( count + 1 > g_maxGhosts.integer )
+ {
+ G_AdminsPrintf( "Connect from client exceeds %d maximum connections per IP: '%s' NAME: '%s^7'\n",
+ g_maxGhosts.integer, ip, Info_ValueForKey( userinfo, "name" ) );
+ return "Maximum simultaneous clients exceeded";
+ }
+ }
+
// check for a password
value = Info_ValueForKey( userinfo, "password" );
@@ -1464,7 +1484,7 @@ char *ClientConnect( int clientNum, qboolean firstTime )
memset( client, 0, sizeof(*client) );
// add guid to session so we don't have to keep parsing userinfo everywhere
- if( !guid[ 0 ] )
+ if( !guid[0] )
{
Q_strncpyz( client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
sizeof( client->pers.guid ) );
@@ -1473,23 +1493,9 @@ char *ClientConnect( int clientNum, qboolean firstTime )
{
Q_strncpyz( client->pers.guid, guid, sizeof( client->pers.guid ) );
}
-
Q_strncpyz( client->pers.ip, ip, sizeof( client->pers.ip ) );
client->pers.adminLevel = G_admin_level( ent );
- // do autoghost now so that there won't be any name conflicts later on
- if ( g_autoGhost.integer && client->pers.guid[ 0 ] != 'X' )
- {
- for ( i = 0; i < MAX_CLIENTS; i++ )
- {
- if ( i != ent - g_entities && g_entities[i].client && g_entities[i].client->pers.connected != CON_DISCONNECTED && !Q_stricmp( g_entities[i].client->pers.guid, client->pers.guid ) )
- {
- trap_SendServerCommand( i, "disconnect \"You may not be connected to this server multiple times\"" );
- trap_DropClient( i, "disconnected" );
- }
- }
- }
-
client->pers.connected = CON_CONNECTING;
// read or initialize the session data
@@ -1545,7 +1551,6 @@ char *ClientConnect( int clientNum, qboolean firstTime )
CalculateRanks( );
G_admin_namelog_update( client, qfalse );
}
-
// if this is after !restart keepteams or !restart switchteams, apply said selection
if ( client->sess.restartTeam != PTE_NONE ) {
@@ -1602,35 +1607,58 @@ void ClientBegin( int clientNum )
// locate ent at a spawn point
ClientSpawn( ent, NULL, NULL, NULL );
-
+
// Ignore invisible players for this section:
if ( client->sess.invisible != qtrue )
{
trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname ) );
- // auto mute flag
- if( G_admin_permission( ent, ADMF_NO_CHAT ) )
- client->pers.muted = qtrue;
-
// 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" );
}
+
+ // auto mute flag
+ if( G_admin_permission( ent, ADMF_NO_CHAT ) )
+ client->pers.muted = qtrue;
+
+ if( g_karma.integer )
+ {
+ if( g_karma.integer > 1 &&
+ client->pers.adminLevel == 0 &&
+ client->pers.guid[0] != 'X' )
+ {
+ if( !(g_newbieNumbering.integer && g_newbieNamePrefix.string[ 0 ] &&
+ Q_stricmpn ( client->pers.netname, g_newbieNamePrefix.string,
+ strlen(g_newbieNamePrefix.string ) ) == 0 ) )
+ {
+ trap_SendConsoleCommand( EXEC_APPEND, va( "!l1 %d", clientNum ) );
+ trap_SendServerCommand( client->ps.clientNum,
+ "print \"^5The karma feature automatically !registers all players.\n\"" );
+ }
+ }
+ else if( client->pers.adminLevel > 0 &&
+ level.time - level.startTime > 60000 )
+ {
+ trap_SendServerCommand( client->ps.clientNum,
+ va( "print \"^5Welcome back, your karma is %d\n\"", client->pers.karma / 1000 ) );
+ }
+ }
+
G_LogPrintf( "ClientBegin: %i\n", clientNum );
if( g_clientUpgradeNotice.integer )
{
if( !Q_stricmp( ent->client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ) )
{
- trap_SendServerCommand( client->ps.clientNum, va( "print \"^3Your client is out of date. Updating your client will allow you to "
- "become an admin on servers and download maps much more quickly. Please replace your client executable with a newer client. \n\"" ) );
-
- trap_SendServerCommand( client->ps.clientNum, va("print \"^3Some available clients: \n"
- "^2TremFusion^7- ^3http://www.tremfusion.net/download/^7\n"
- "^2FSM-Trem^7 - ^3http://code.google.com/p/fsm-trem/^7\n"
- "^2MGClient^7 - ^3http://releases.mercenariesguild.net/client/^7\n\"" ) );
+ trap_SendServerCommand( client->ps.clientNum, va( "print \"^1Your client is out of date. Updating your client will allow you to "
+ "become an admin on servers and download maps much more quickly. Please replace your client executable with the one "
+ "at ^2http://trem.tjw.org/backport/^1 and reconnect. \n\"" ) );
}
}
@@ -1847,6 +1875,15 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
client->jetpackfuel = mod_jetpackFuel.value;
}
+ //free credits
+ if( g_freeCredits.integer && ent != spawn )
+ {
+ if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ client->ps.persistant[ PERS_CREDIT ] = ALIEN_MAX_KILLS;
+ else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ client->ps.persistant[ PERS_CREDIT ] = HUMAN_MAX_CREDITS;
+ }
+
G_SetOrigin( ent, spawn_origin );
VectorCopy( spawn_origin, client->ps.origin );
@@ -1999,7 +2036,8 @@ void ClientDisconnect( int clientNum )
Q_strncpyz( ptr->name, ent->client->pers.netname, MAX_NETNAME );
}
}
-
+
+ //update namelog only if they are not invisible
if ( ent->client->sess.invisible != qtrue )
G_admin_namelog_update( ent->client, qtrue );
G_LeaveTeam( ent );
diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c
index 3857ef1..b9cca49 100644
--- a/src/game/g_cmds.c
+++ b/src/game/g_cmds.c
@@ -760,19 +760,47 @@ void Cmd_Team_f( gentity_t *ent )
// stop team join spam
if( level.time - ent->client->pers.teamChangeTime < 1000 )
return;
+
+ if( ent->client->pers.teamSelection != PTE_NONE )
+ {
+ int cs_offset;
+
+ if( ent->client->pers.teamSelection == PTE_ALIENS )
+ cs_offset = 1;
+ else
+ cs_offset = 0;
+ if( level.teamVoteTime[ cs_offset ] )
+ {
+ trap_SendServerCommand( ent-g_entities, "print \"Can not leave team during a team vote\n\"" );
+ return;
+ }
+ }
+
// Prevent invisible players from joining a team
- if ( ent->client->sess.invisible == qtrue )
+ if( ent->client->sess.invisible == qtrue )
{
trap_SendServerCommand( ent-g_entities,
- va( "print \"You cannot join a team while invisible\n\"" ) );
+ va( "print \"You cannot join a team while invisible\n\"" ) );
return;
}
-
+
if( oldteam == PTE_ALIENS )
aliens--;
else if( oldteam == PTE_HUMANS )
humans--;
+ // practice mode
+ if( !force && g_practiceCount.integer )
+ {
+ char name[ MAX_NAME_LENGTH ];
+
+ G_DecolorString( ent->client->pers.netname, name );
+ if( strstr( name, g_practiceText.string ) )
+ {
+ force = qtrue;
+ }
+ }
+
// do warm up
if( g_doWarmup.integer && g_warmupMode.integer == 1 &&
level.time - level.startTime < g_warmup.integer * 1000 )
@@ -938,6 +966,10 @@ void Cmd_Team_f( gentity_t *ent )
Com_sprintf( buf, sizeof( buf ), "%s^7 abandoned humans and joined the aliens.", ent->client->pers.netname );
else
Com_sprintf( buf, sizeof( buf ), "%s^7 joined the aliens.", ent->client->pers.netname );
+
+ if( g_modAlienRegenRange.integer )
+ trap_SendServerCommand( ent-g_entities,
+ va( "print \"Reminder: aliens must be near base to regenerate health.\n\"" ) );
}
else if( team == PTE_HUMANS ) {
if ( oldteam == PTE_ALIENS )
@@ -991,15 +1023,12 @@ static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, cons
}
if( mode == SAY_ADMINS &&
- (!G_admin_permission( other, ADMF_ADMINCHAT ) || other->client->pers.ignoreAdminWarnings ) )
+ (!G_admin_permission( other, ADMF_ADMINCHAT) || other->client->pers.ignoreAdminWarnings ) )
return;
if( BG_ClientListTest( &other->client->sess.ignoreList, ent-g_entities ) )
ignore = qtrue;
-
- if ( ignore && g_fullIgnore.integer )
- return;
-
+
trap_SendServerCommand( other-g_entities, va( "%s \"%s%s%s%c%c%s\"",
( mode == SAY_TEAM || mode == SAY_ACTION_T ) ? "tchat" : "chat",
( ignore ) ? "[skipnotify]" : "",
@@ -1032,24 +1061,6 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText )
return;
}
- // Spam limit: If they said this message recently, ignore it.
- if( g_spamTime.integer )
- if ( ( level.time - ent->client->pers.lastMessageTime ) < ( g_spamTime.integer * 1000 ) &&
- !Q_stricmp( ent->client->pers.lastMessage, chatText) &&
- !G_admin_permission( ent, ADMF_NOCENSORFLOOD ) &&
- ent->client->pers.floodDemerits <= g_floodMaxDemerits.integer )
- {
- trap_SendServerCommand( ent-g_entities, "print \"Your message has been ignored to prevent spam\n\"" );
- return;
- }
- else
- {
- ent->client->pers.lastMessageTime = level.time;
-
- Q_strncpyz( ent->client->pers.lastMessage, chatText,
- sizeof( ent->client->pers.lastMessage ) );
- }
-
// Flood limit. If they're talking too fast, determine that and return.
if( g_floodMinTime.integer )
if ( G_Flood_Limited( ent ) )
@@ -1057,7 +1068,7 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText )
trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" );
return;
}
-
+
if (g_chatTeamPrefix.integer && ent && ent->client )
{
switch( ent->client->pers.teamSelection)
@@ -1158,16 +1169,6 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText )
Com_sprintf( text, sizeof( text ), "%s^7", chatText );
- if( ent && ent->client && g_aimbotAdvertBan.integer && ( Q_stricmp( text, "^1N^7ullify for ^1T^7remulous [beta] | Get it at CheatersUtopia.com^7" ) == 0 ) )
- {
- trap_SendConsoleCommand( 0,
- va( "!ban %s %s %s\n",
- ent->client->pers.ip,
- ( g_aimbotAdvertBanTime.string && Q_stricmp( g_aimbotAdvertBanTime.string, "0" ) == 1 ) ? g_aimbotAdvertBanTime.string : "" ,
- g_aimbotAdvertBanReason.string ) );
- Q_strncpyz( text, "^7has been caught hacking and will be dealt with.", sizeof( text ) );
- }
-
if( target )
{
G_SayTo( ent, target, mode, color, name, text, prefix );
@@ -1215,11 +1216,14 @@ static void Cmd_SayArea_f( gentity_t *ent )
int num, i;
int color = COLOR_BLUE;
const char *prefix;
- vec3_t range = { HELMET_RANGE, HELMET_RANGE, HELMET_RANGE };
+ vec3_t range = { 1000.0f, 1000.0f, 1000.0f };
vec3_t mins, maxs;
char *msg = ConcatArgs( 1 );
char name[ 64 ];
+ for(i = 0; i < 3; i++ )
+ range[ i ] = g_sayAreaRange.value;
+
if( g_floodMinTime.integer )
if ( G_Flood_Limited( ent ) )
{
@@ -1373,6 +1377,31 @@ static void Cmd_Say_f( gentity_t *ent )
}
}
+ args = G_SayConcatArgs(0);
+ if( !Q_stricmpn( args, "say /join", 9 ) )
+ {
+ Cmd_Join_f( ent );
+ return;
+ }
+ if( !Q_stricmpn( args, "say /part", 9 ) )
+ {
+ Cmd_Part_f( ent );
+ return;
+ }
+ if( !Q_stricmpn( args, "say /0", 6 ) ||
+ !Q_stricmpn( args, "say /1", 6 ) ||
+ !Q_stricmpn( args, "say /2", 6 ) ||
+ !Q_stricmpn( args, "say /3", 6 ) ||
+ !Q_stricmpn( args, "say /4", 6 ) ||
+ !Q_stricmpn( args, "say /5", 6 ) ||
+ !Q_stricmpn( args, "say /6", 6 ) ||
+ !Q_stricmpn( args, "say /7", 6 ) ||
+ !Q_stricmpn( args, "say /8", 6 ) ||
+ !Q_stricmpn( args, "say /9", 6 ) )
+ {
+ Cmd_Channel_f( ent );
+ return;
+ }
if( trap_Argc( ) < 2 )
return;
@@ -1417,6 +1446,244 @@ 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 skipargs = 0;
+ 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;
+ }
+
+ G_SayArgv( skipargs, arg, sizeof( arg ) );
+ if( !Q_stricmp( arg, "say" ) )
+ skipargs++;
+
+ if( G_SayArgc( ) < 2 + skipargs )
+ {
+ 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;
+ }
+
+ G_SayArgv( 1 + skipargs, 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;
+ }
+
+ pass[ 0 ] = '\0';
+ if ( G_SayArgc( ) >= 2 + skipargs )
+ G_SayArgv( 2 + skipargs, 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 skipargs = 0;
+ int chan;
+ int i;
+ gentity_t *target;
+
+ G_SayArgv( skipargs, arg, sizeof( arg ) );
+ if( !Q_stricmp( arg, "say" ) )
+ skipargs++;
+
+ if( G_SayArgc( ) < 2 + skipargs )
+ {
+ 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;
+ }
+
+ G_SayArgv( 1 + skipargs, 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 *cmd, *p;
+ char location[ 64 ];
+ char escaped[ 64 ];
+ int chan;
+ int i;
+ int num = 0;
+ int skipargs = 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;
+ }
+
+ G_SayArgv( skipargs, arg, sizeof( arg ) );
+ if( !Q_stricmp( arg, "say" ) )
+ {
+ skipargs++;
+ G_SayArgv( skipargs, arg, sizeof( arg ) );
+ }
+ cmd = arg;
+ if( *cmd == '/' )
+ cmd++;
+ chan = atoi( cmd );
+ 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\"", cmd ) );
+ return;
+ }
+
+ p = G_SayConcatArgs( 1 + skipargs );
+ 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( "chat \"(^1#%d^7 %d) [%s^7]%s: ^1%s^7\"",
+ chan, num, ent->client->pers.netname,
+ ( OnSameTeam( target, ent ) ) ? escaped : "",
+ p ) );
+ }
+ }
+}
+
/*
==================
Cmd_Where_f
@@ -1427,6 +1694,24 @@ void Cmd_Where_f( gentity_t *ent )
trap_SendServerCommand( ent-g_entities, va( "print \"%s\n\"", vtos( ent->s.origin ) ) );
}
+static int map_vote_percent( const char *map, int fallback )
+{
+ char maps[ MAX_CVAR_VALUE_STRING ];
+ char *token, *token_p;
+
+ if( !g_popularMapsVotePercent.integer )
+ return fallback;
+
+ Q_strncpyz( maps, g_popularMaps.string, sizeof( maps ) );
+ token_p = maps;
+ while( *( token = COM_Parse( &token_p ) ) )
+ {
+ if( !Q_stricmp( token, map ) )
+ return g_popularMapsVotePercent.integer;
+ }
+
+ return fallback;
+}
static qboolean map_is_votable( const char *map )
{
@@ -1469,7 +1754,7 @@ void Cmd_CallVote_f( gentity_t *ent )
arg1plus = G_SayConcatArgs( 1 );
arg2plus = G_SayConcatArgs( 2 );
-
+
// Invisible players cannot call votes
if( ent->client->sess.invisible == qtrue )
{
@@ -1483,6 +1768,12 @@ void Cmd_CallVote_f( gentity_t *ent )
return;
}
+ if( G_admin_permission( ent, ADMF_NO_VOTE ) )
+ {
+ trap_SendServerCommand( ent-g_entities, "print \"You have no voting rights\n\"" );
+ return;
+ }
+
// Flood limit. If they're talking too fast, determine that and return.
if( g_floodMinTime.integer )
if ( G_Flood_Limited( ent ) )
@@ -1491,13 +1782,6 @@ void Cmd_CallVote_f( gentity_t *ent )
return;
}
- //see if they can vote
- if( G_admin_permission( ent, ADMF_NO_VOTE ) )
- {
- trap_SendServerCommand( ent-g_entities, "print \"You have no voting rights\n\"" );
- return;
- }
-
if( g_voteMinTime.integer
&& ent->client->pers.firstConnect
&& level.time - ent->client->pers.enterTime < g_voteMinTime.integer * 1000
@@ -1554,6 +1838,10 @@ void Cmd_CallVote_f( gentity_t *ent )
{
G_admin_maplog_result( "m" );
}
+ else if( !Q_stricmpn( level.voteString, "!restart", 8 ) )
+ {
+ G_admin_maplog_result( "l" );
+ }
level.voteExecuteTime = 0;
trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.voteString ) );
@@ -1628,7 +1916,7 @@ void Cmd_CallVote_f( gentity_t *ent )
}
if( clientNum != -1 &&
- level.clients[ clientNum ].pers.connected != CON_CONNECTED )
+ level.clients[ clientNum ].pers.connected == CON_DISCONNECTED )
{
clientNum = -1;
}
@@ -1659,6 +1947,8 @@ void Cmd_CallVote_f( gentity_t *ent )
if( !Q_stricmp( arg1, "kick" ) )
{
+ char n1[ MAX_NAME_LENGTH ];
+
if( G_admin_permission( &g_entities[ clientNum ], ADMF_IMMUNITY ) )
{
trap_SendServerCommand( ent-g_entities,
@@ -1673,9 +1963,13 @@ void Cmd_CallVote_f( gentity_t *ent )
"!ban %s \"%s\" vote kick", level.clients[ clientNum ].pers.ip,
g_adminTempBan.string );
if ( reason[0]!='\0' )
- Q_strcat( level.voteString, sizeof( level.voteDisplayString ), va( "(%s^7)", reason ) );
+ Q_strcat( level.voteString, sizeof( level.voteString ), va( ": %s^7", reason ) );
+ G_SanitiseString( ent->client->pers.netname, n1, sizeof( n1 ) );
+ Q_strcat( level.voteString, sizeof( level.voteString ), va( ", %s", n1 ) );
Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ),
"Kick player \'%s\'", name );
+
+ level.votePassThreshold = g_kickVotesPercent.integer;
}
else if( !Q_stricmp( arg1, "spec" ) )
{
@@ -1772,7 +2066,7 @@ 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 );
- level.votePassThreshold = g_mapVotesPercent.integer;
+ level.votePassThreshold = map_vote_percent( arg2, g_mapVotesPercent.integer );
}
else if( !Q_stricmp( arg1, "nextmap" ) )
{
@@ -1808,7 +2102,7 @@ void Cmd_CallVote_f( gentity_t *ent )
"set g_nextMap %s", arg2 );
Com_sprintf( level.voteDisplayString,
sizeof( level.voteDisplayString ), "Set the next map to '%s^7'", arg2 );
- level.votePassThreshold = g_mapVotesPercent.integer;
+ level.votePassThreshold = map_vote_percent( arg2, g_mapVotesPercent.integer );
}
else if( !Q_stricmp( arg1, "draw" ) )
{
@@ -1817,16 +2111,53 @@ void Cmd_CallVote_f( gentity_t *ent )
"End match in a draw" );
level.votePassThreshold = g_mapVotesPercent.integer;
}
+ else if( !Q_stricmp( arg1, "layout" ) )
+ {
+ char map[ 64 ];
+
+ if( g_mapvoteMaxTime.integer
+ && (( level.time - level.startTime ) >= 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 layout change after %d seconds\n\"",
+ g_mapvoteMaxTime.integer ) );
+ G_admin_adminlog_log( ent, "vote", NULL, 0, qfalse );
+ return;
+ }
+
+ trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) );
+ if( Q_stricmp( arg2, "*BUILTIN*" ) &&
+ !trap_FS_FOpenFile( va( "layouts/%s/%s.dat", map, arg2 ), NULL, FS_READ ) )
+ {
+ trap_SendServerCommand( ent - g_entities, va( "print \"callvote: "
+ "layout '%s' could not be found on the server\n\"", arg2 ) );
+ return;
+ }
+ Com_sprintf( level.voteString, sizeof( level.voteString ), "!restart %s", arg2 );
+ Com_sprintf( level.voteDisplayString,
+ sizeof( level.voteDisplayString ), "Change to map layout '%s'", arg2 );
+ level.votePassThreshold = g_mapVotesPercent.integer;
+ }
else if( !Q_stricmp( arg1, "poll" ) )
{
- if( arg2plus[ 0 ] == '\0' )
+ if(!g_pollVotes.integer)
+ {
+ trap_SendServerCommand( ent-g_entities, "print \"Poll Votes have been disabled\n\"" );
+ return;
+ }
+ else if( arg2plus[ 0 ] == '\0' )
{
trap_SendServerCommand( ent-g_entities, "print \"callvote: You forgot to specify what people should vote on.\n\"" );
return;
}
- Com_sprintf( level.voteString, sizeof( level.voteString ), nullstring);
- Com_sprintf( level.voteDisplayString,
- sizeof( level.voteDisplayString ), "[Poll] \'%s\'", arg2plus );
+ else
+ {
+ Com_sprintf( level.voteString, sizeof( level.voteString ), nullstring);
+ Com_sprintf( level.voteDisplayString,
+ sizeof( level.voteDisplayString ), "[Poll] \'%s" S_COLOR_WHITE "\'", arg2plus );
+ }
}
else if( !Q_stricmp( arg1, "sudden_death" ) ||
!Q_stricmp( arg1, "suddendeath" ) )
@@ -1858,41 +2189,36 @@ void Cmd_CallVote_f( gentity_t *ent )
}
}
- else if( !Q_stricmp( arg1, "extend" ) )
- {
- if( !g_extendVotesPercent.integer )
- {
- trap_SendServerCommand( ent-g_entities, "print \"Extend votes have been disabled\n\"" );
- return;
- }
- if( g_extendVotesCount.integer
- && level.extend_vote_count >= g_extendVotesCount.integer )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"callvote: Maximum number of %d extend votes has been reached\n\"",
- g_extendVotesCount.integer ) );
- return;
- }
- if( !g_timelimit.integer ) {
- trap_SendServerCommand( ent-g_entities,
- "print \"This match has no timelimit so extend votes wont work\n\"" );
- return;
- }
- if( level.time - level.startTime <
- ( g_timelimit.integer - g_extendVotesTime.integer / 2 ) * 60000 )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"callvote: Extend votes only allowed with less than %d minutes remaining\n\"",
- g_extendVotesTime.integer / 2 ) );
- return;
- }
- level.extend_vote_count++;
- level.votePassThreshold = g_extendVotesPercent.integer;
- Com_sprintf( level.voteString, sizeof( level.voteString ),
- "timelimit %i", g_timelimit.integer + g_extendVotesTime.integer );
- Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ),
- "Extend the timelimit by %d minutes", g_extendVotesTime.integer );
- }
+ else if( !Q_stricmp( arg1, "extend" ) )
+ {
+ if( !g_extendVotesPercent.integer )
+ {
+ trap_SendServerCommand( ent-g_entities, "print \"Extend votes have been disabled\n\"" );
+ return;
+ }
+ if( g_extendVotesCount.integer
+ && level.extend_vote_count >= g_extendVotesCount.integer )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ va( "print \"callvote: Maximum number of %d extend votes has been reached\n\"",
+ g_extendVotesCount.integer ) );
+ return;
+ }
+ if( level.time - level.startTime <
+ ( g_timelimit.integer - g_extendVotesTime.integer / 2 ) * 60000 )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ va( "print \"callvote: Extend votes only allowed with less than %d minutes remaining\n\"",
+ g_extendVotesTime.integer / 2 ) );
+ return;
+ }
+ level.extend_vote_count++;
+ level.votePassThreshold = g_extendVotesPercent.integer;
+ Com_sprintf( level.voteString, sizeof( level.voteString ),
+ "timelimit %i", g_timelimit.integer + g_extendVotesTime.integer );
+ Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ),
+ "Extend the timelimit by %d minutes", g_extendVotesTime.integer );
+ }
else
{
qboolean match = qfalse;
@@ -1976,13 +2302,13 @@ void Cmd_CallVote_f( gentity_t *ent )
match = qtrue;
break;
}
- }
+ }
if( !match )
{
trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string\n\"" );
trap_SendServerCommand( ent-g_entities, "print \"Valid vote commands are: "
- "map, map_restart, draw, extend, nextmap, kick, spec, mute, unmute, poll, and sudden_death\n" );
+ "map, map_restart, nextmap, layout, draw, extend, kick, spec, mute, unmute, poll, and sudden_death\n" );
if( customVoteKeys[ 0 ] != '\0' )
trap_SendServerCommand( ent-g_entities,
va( "print \"Additional custom vote commands: %s\n\"", customVoteKeys ) );
@@ -1992,7 +2318,7 @@ void Cmd_CallVote_f( gentity_t *ent )
if( level.votePassThreshold!=50 )
{
- Q_strcat( level.voteDisplayString, sizeof( level.voteDisplayString ), va( "^7 (Needs > %d percent)", level.votePassThreshold ) );
+ Q_strcat( level.voteDisplayString, sizeof( level.voteDisplayString ), va( " (Needs > %d percent)", level.votePassThreshold ) );
}
if ( reason[0]!='\0' )
@@ -2002,6 +2328,7 @@ void Cmd_CallVote_f( gentity_t *ent )
trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE
" called a vote: %s" S_COLOR_WHITE "\n\"", ent->client->pers.netname, level.voteDisplayString ) );
+ trap_SendServerCommand( -1, "cp \"A vote has been called\n^2F1: Yes^7, ^1F2: No^7\"" );
G_LogPrintf("Vote: %s^7 called a vote: %s^7\n", ent->client->pers.netname, level.voteDisplayString );
@@ -2138,22 +2465,21 @@ void Cmd_CallTeamVote_f( gentity_t *ent )
else if(team==PTE_HUMANS)
numVoters = level.numHumanClients;
- if( !g_allowVote.integer )
+ if( !g_allowVote.integer || g_allowVote.integer == 2 )
{
- trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here\n\"" );
+ trap_SendServerCommand( ent-g_entities, "print \"Team voting not allowed here\n\"" );
return;
}
- if( level.teamVoteTime[ cs_offset ] )
+ if( G_admin_permission( ent, ADMF_NO_VOTE ) )
{
- trap_SendServerCommand( ent-g_entities, "print \"A team vote is already in progress\n\"" );
+ trap_SendServerCommand( ent-g_entities, "print \"You have no voting rights\n\"" );
return;
}
- //see if they can vote
- if( G_admin_permission( ent, ADMF_NO_VOTE ) )
+ if( level.teamVoteTime[ cs_offset ] )
{
- trap_SendServerCommand( ent-g_entities, "print \"You have no voting rights\n\"" );
+ trap_SendServerCommand( ent-g_entities, "print \"A team vote is already in progress\n\"" );
return;
}
@@ -2300,8 +2626,12 @@ void Cmd_CallTeamVote_f( gentity_t *ent )
}
}
+ level.teamVotePassThreshold[ cs_offset ] = 50;
+
if( !Q_stricmp( arg1, "kick" ) )
{
+ char n1[ MAX_NAME_LENGTH ];
+
if( G_admin_permission( &g_entities[ clientNum ], ADMF_IMMUNITY ) )
{
trap_SendServerCommand( ent-g_entities,
@@ -2311,15 +2641,28 @@ void Cmd_CallTeamVote_f( gentity_t *ent )
return;
}
+ if( g_allowVote.integer == 3 )
+ {
+ trap_SendServerCommand( ent-g_entities, "print \"Team kick votes are disabled\n\"" );
+ return;
+ }
// use ip in case this player disconnects before the vote ends
Com_sprintf( level.teamVoteString[ cs_offset ],
sizeof( level.teamVoteString[ cs_offset ] ),
"!ban %s \"%s\" team vote kick", level.clients[ clientNum ].pers.ip,
g_adminTempBan.string );
+ if( reason[0] )
+ Q_strcat( level.teamVoteString[ cs_offset ], sizeof( level.teamVoteString[ cs_offset ] ),
+ va( ": %s", reason ) );
+ G_SanitiseString( ent->client->pers.netname, n1, sizeof( n1 ) );
+ Q_strcat( level.teamVoteString[ cs_offset ], sizeof( level.teamVoteString[ cs_offset ] ),
+ va( ", %s", n1 ) );
Com_sprintf( level.teamVoteDisplayString[ cs_offset ],
sizeof( level.teamVoteDisplayString[ cs_offset ] ),
"Kick player '%s'", name );
+
+ level.teamVotePassThreshold[ cs_offset ] = g_kickVotesPercent.integer;
}
else if( !Q_stricmp( arg1, "denybuild" ) )
{
@@ -2405,6 +2748,16 @@ void Cmd_CallTeamVote_f( gentity_t *ent )
}
else if( !Q_stricmp( arg1, "admitdefeat" ) )
{
+ if( g_defeatVoteMinTime.integer &&
+ level.time - level.startTime < abs( g_defeatVoteMinTime.integer ) * 1000 &&
+ ( !G_admin_permission( ent, ADMF_NO_VOTE_LIMIT ) || g_defeatVoteMinTime.integer < 0 ) )
+ {
+ trap_SendServerCommand( ent-g_entities, va(
+ "print \"You cannot admit defeat until after %d seconds\n\"",
+ g_defeatVoteMinTime.integer ) );
+ G_admin_adminlog_log( ent, "vote", NULL, 0, qfalse );
+ return;
+ }
if( numVoters <=1 )
{
trap_SendServerCommand( ent-g_entities,
@@ -2440,6 +2793,12 @@ void Cmd_CallTeamVote_f( gentity_t *ent )
ent->client->pers.voteCount++;
G_admin_adminlog_log( ent, "teamvote", arg1, 0, qtrue );
+
+ if( level.teamVotePassThreshold[ cs_offset ] != 50 )
+ {
+ Q_strcat( level.teamVoteDisplayString[ cs_offset ], sizeof( level.teamVoteDisplayString[ cs_offset ] ),
+ va( " (Needs > %d percent)", level.teamVotePassThreshold[ cs_offset ] ) );
+ }
if ( reason[0]!='\0' )
Q_strcat( level.teamVoteDisplayString[ cs_offset ], sizeof( level.teamVoteDisplayString[ cs_offset ] ), va( " Reason: '%s'^7", reason ) );
@@ -2453,6 +2812,7 @@ void Cmd_CallTeamVote_f( gentity_t *ent )
{
trap_SendServerCommand( i, va("print \"%s " S_COLOR_WHITE
"called a team vote: %s^7 \n\"", ent->client->pers.netname, level.teamVoteDisplayString[ cs_offset ] ) );
+ trap_SendServerCommand( i, "cp \"A team vote has been called\n^2F1: Yes^7, ^1F2: No^7\"" );
}
else if( G_admin_permission( &g_entities[ i ], ADMF_ADMINCHAT ) &&
( ( !Q_stricmp( arg1, "kick" ) || !Q_stricmp( arg1, "denybuild" ) ) ||
@@ -2656,7 +3016,6 @@ void Cmd_Class_f( gentity_t *ent )
vec3_t mins, maxs;
int num;
gentity_t *other;
- qboolean humanNear = qfalse;
clientNum = ent->client - level.clients;
@@ -2779,21 +3138,11 @@ void Cmd_Class_f( gentity_t *ent )
if( ( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) ||
( other->s.eType == ET_BUILDABLE && other->biteam == BIT_HUMANS ) )
{
- humanNear = qtrue;
- }
- //If its the OM, then ignore all humans.
- if(other->s.eType == ET_BUILDABLE && other->s.modelindex == BA_A_OVERMIND)
- {
- humanNear = qfalse;
- break;
+ G_TriggerMenu( clientNum, MN_A_TOOCLOSE );
+ return;
}
}
- if(humanNear == qtrue) {
- G_TriggerMenu( clientNum, MN_A_TOOCLOSE );
- return;
- }
-
if( !level.overmindPresent )
{
G_TriggerMenu( clientNum, MN_A_NOOVMND_EVOLVE );
@@ -2902,19 +3251,33 @@ 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 );
+ }
}
}
@@ -2937,6 +3300,23 @@ void Cmd_Destroy_f( gentity_t *ent )
"print \"Your building rights have been revoked\n\"" );
return;
}
+ if( ent->client->pers.paused )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ "print \"You may not deconstruct while paused\n\"" );
+ return;
+ }
+
+ if( g_newbieNoBuild.integer
+ && g_newbieNamePrefix.string[ 0 ]
+ && Q_stricmpn ( ent->client->pers.netname, g_newbieNamePrefix.string, strlen(g_newbieNamePrefix.string ) ) == 0 )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ va( "print \"You cannot build until you set a name\n%s%s\"",
+ ( g_newbieNoBuildMessage.string[ 0 ] ) ? g_newbieNoBuildMessage.string : "",
+ ( g_newbieNoBuildMessage.string[ 0 ] ) ? "\n" : "" ) );
+ return;
+ }
trap_Argv( 0, cmd, sizeof( cmd ) );
if( Q_stricmp( cmd, "destroy" ) == 0 )
@@ -2947,11 +3327,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,
@@ -2981,21 +3360,13 @@ 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;
}
- // Check the minimum level to deconstruct
- if ( G_admin_level( ent ) < g_minDeconLevel.integer && !ent->client->pers.designatedBuilder )
- {
- trap_SendServerCommand( ent-g_entities,
- "print \"You do not have deconstructuction rights.\n\"" );
- return;
- }
// Prevent destruction of the last spawn
if( g_markDeconstruct.integer != 1 && !g_cheats.integer )
@@ -3024,8 +3395,7 @@ void Cmd_Destroy_f( gentity_t *ent )
!BG_FindReplaceableTestForBuildable( traceEnt->s.modelindex ) ) ||
( g_suddenDeathMode.integer == SDMODE_BP &&
BG_FindBuildPointsForBuildable( traceEnt->s.modelindex ) ) ||
- g_suddenDeathMode.integer == SDMODE_NO_BUILD ||
- g_suddenDeathMode.integer == SDMODE_NO_DECON ) )
+ g_suddenDeathMode.integer == SDMODE_NO_BUILD ) )
{
trap_SendServerCommand( ent-g_entities,
"print \"During Sudden Death you can only decon buildings that "
@@ -3055,6 +3425,7 @@ void Cmd_Destroy_f( gentity_t *ent )
new = G_Alloc( sizeof( buildHistory_t ) );
new->ID = ( ++level.lastBuildID > 1000 )
? ( level.lastBuildID = 1 ) : level.lastBuildID;
+ new->time = level.time;
new->ent = ent;
new->name[ 0 ] = 0;
new->buildable = traceEnt->s.modelindex;
@@ -3078,6 +3449,8 @@ void Cmd_Destroy_f( gentity_t *ent )
ent->client->pers.netname,
BG_FindNameForBuildable( traceEnt->s.modelindex ) );
}
+ else if( deconstruct )
+ G_RecoverBuildPoints( traceEnt );
if( !deconstruct )
G_Damage( traceEnt, ent, ent, forward, tr.endpos, 10000, 0, MOD_SUICIDE );
@@ -3112,12 +3485,21 @@ void Cmd_Mark_f( gentity_t *ent )
"print \"Your building rights have been revoked\n\"" );
return;
}
+ if( ent->client->pers.paused )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ "print \"You may not mark while paused\n\"" );
+ return;
+ }
- // Check the minimum level to deconstruct
- if ( G_admin_level( ent ) < g_minDeconLevel.integer && !ent->client->pers.designatedBuilder && g_minDeconAffectsMark.integer > 0 )
+ if( g_newbieNoBuild.integer
+ && g_newbieNamePrefix.string[ 0 ]
+ && Q_stricmpn ( ent->client->pers.netname, g_newbieNamePrefix.string, strlen(g_newbieNamePrefix.string ) ) == 0 )
{
trap_SendServerCommand( ent-g_entities,
- "print \"You do not have deconstructuction rights.\n\"" );
+ va( "print \"You cannot build until you set a name\n%s%s\"",
+ ( g_newbieNoBuildMessage.string[ 0 ] ) ? g_newbieNoBuildMessage.string : "",
+ ( g_newbieNoBuildMessage.string[ 0 ] ) ? "\n" : "" ) );
return;
}
@@ -3164,8 +3546,7 @@ void Cmd_Mark_f( gentity_t *ent )
!BG_FindReplaceableTestForBuildable( traceEnt->s.modelindex ) ) ||
( g_suddenDeathMode.integer == SDMODE_BP &&
BG_FindBuildPointsForBuildable( traceEnt->s.modelindex ) ) ||
- g_suddenDeathMode.integer == SDMODE_NO_BUILD ||
- g_suddenDeathMode.integer == SDMODE_NO_DECON ) )
+ g_suddenDeathMode.integer == SDMODE_NO_BUILD ) )
{
trap_SendServerCommand( ent-g_entities,
"print \"During Sudden Death you can only mark buildings that "
@@ -3186,6 +3567,65 @@ void Cmd_Mark_f( gentity_t *ent )
}
}
+void Cmd_Aim_f( gentity_t *ent )
+{
+ vec3_t forward, end;
+ trace_t tr;
+ gentity_t *traceEnt;
+
+ if( !g_turretAim.integer )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ "print \"Turret aim is disabled\n\"" );
+ return;
+ }
+
+ if( ent->client->pers.denyBuild ||
+ ent->client->pers.paused )
+ return;
+
+ if( g_newbieNoBuild.integer &&
+ g_newbieNamePrefix.string[ 0 ] &&
+ !Q_stricmpn ( ent->client->pers.netname, g_newbieNamePrefix.string, strlen(g_newbieNamePrefix.string ) ) )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ va( "print \"You cannot use /aim until you set a name\n%s%s\"",
+ ( g_newbieNoBuildMessage.string[ 0 ] ) ? g_newbieNoBuildMessage.string : "",
+ ( g_newbieNoBuildMessage.string[ 0 ] ) ? "\n" : "" ) );
+ return;
+ }
+
+ if( !( ent->client->ps.stats[ STAT_STATE ] & SS_INFESTING ) )
+ {
+ AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL );
+ VectorMA( ent->client->ps.origin, 100, forward, end );
+
+ trap_Trace( &tr, ent->client->ps.origin, NULL, NULL, end, ent->s.number, MASK_PLAYERSOLID );
+ traceEnt = &g_entities[ tr.entityNum ];
+
+ if( tr.fraction < 1.0f &&
+ ( traceEnt->s.eType == ET_BUILDABLE ) &&
+ ( traceEnt->s.modelindex == BA_H_MGTURRET ) &&
+ ( traceEnt->biteam == ent->client->pers.teamSelection ) &&
+ ( ( ent->client->ps.weapon >= WP_ABUILD ) &&
+ ( ent->client->ps.weapon <= WP_HBUILD ) ) )
+ {
+ if( traceEnt->rotatorAngle >= 360.0f )
+ {
+ traceEnt->rotatorAngle = 0.0f;
+ trap_SendServerCommand( ent-g_entities, "print \"Turret aim direction disabled\n\"" );
+ }
+ else
+ {
+ traceEnt->rotatorAngle = ent->client->ps.viewangles[ YAW ];
+ while( traceEnt->rotatorAngle < 360.0f )
+ traceEnt->rotatorAngle += 360.0f;
+ trap_SendServerCommand( ent-g_entities, "print \"Turret aim direction enabled\n\"" );
+ }
+ }
+ }
+}
+
/*
=================
@@ -3507,12 +3947,6 @@ void Cmd_Buy_f( gentity_t *ent )
if( !Q_stricmp( s, "retrigger" ) )
ent->client->retriggerArmouryMenu = level.framenum + RAM_FRAMES;
}
- if( ent->client->pers.paused )
- {
- trap_SendServerCommand( ent-g_entities,
- "print \"You may not deconstruct while paused\n\"" );
- return;
- }
//update ClientInfo
ClientUserinfoChanged( ent->client->ps.clientNum, qfalse );
@@ -3688,7 +4122,18 @@ void Cmd_Build_f( gentity_t *ent )
if( ent->client->pers.paused )
{
trap_SendServerCommand( ent-g_entities,
- "print \"You may not mark while paused\n\"" );
+ "print \"You may not build while paused\n\"" );
+ return;
+ }
+
+ if( g_newbieNoBuild.integer
+ && g_newbieNamePrefix.string[ 0 ]
+ && Q_stricmpn ( ent->client->pers.netname, g_newbieNamePrefix.string, strlen(g_newbieNamePrefix.string ) ) == 0 )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ va( "print \"You cannot build until you set a name\n%s%s\"",
+ ( g_newbieNoBuildMessage.string[ 0 ] ) ? g_newbieNoBuildMessage.string : "",
+ ( g_newbieNoBuildMessage.string[ 0 ] ) ? "\n" : "" ) );
return;
}
@@ -3722,6 +4167,13 @@ void Cmd_Build_f( gentity_t *ent )
}
}
+ if( g_vampireDeath.integer )
+ {
+ trap_SendServerCommand( ent-g_entities,
+ "print \"Building is not allowed during Vampire Sudden Death\n\"" );
+ return;
+ }
+
team = ent->client->ps.stats[ STAT_PTEAM ];
if( buildable != BA_NONE &&
@@ -4005,113 +4457,6 @@ char *G_statsString( statsCounters_t *sc, pTeam_t *pt )
return s;
}
- /*
- =================
- Cmd_AllStats_f
- =================
- */
- void Cmd_AllStats_f( gentity_t *ent )
- {
- int i;
- int NextViewTime;
- int NumResults = 0;
- int Teamcolor = 3;
- gentity_t *tmpent;
-
- //check if ent exists
- if(!ent) return;
-
- NextViewTime = ent->client->pers.statscounters.AllstatstimeLastViewed + g_AllStatsTime.integer * 1000;
- //check if you can use the cmd at this time
- if( !level.intermissiontime && level.time < NextViewTime)
- {
- ADMP( va("You may only check your stats every %i Seconds and during intermission. Next valid time is %d:%02d\n",( g_AllStatsTime.integer ) ? ( g_AllStatsTime.integer ) : 60, ( NextViewTime / 60000 ), ( NextViewTime / 1000 ) % 60 ) );
- return;
- }
- //see if allstats is enabled
- if( !g_AllStats.integer )
- {
- ADMP( "AllStats has been disabled\n");
- return;
- }
- ADMP("^3K^2=^7Kills ^3A^2=^7Assists ^3SK^2=^7StructKills\n^3D^2=^7Deaths ^3F^2=^7Feeds ^3S^2=^7Suicides ^3TK^2=^7Teamkills\n^3DD^2=^7Damage done ^3TDD^2=^7Team Damage done\n^3SB^2=^7Structs Built\n\n" );
- //display a header describing the data
- ADMP( "^3 #| K A SK| D F S TK| DD TDD| SB| Name\n" );
-
- //loop through the clients that are connected
- for( i = 0; i < level.numConnectedClients; i++ )
- {
- //assign a tempent 4 the hell of it
- tmpent = &g_entities[ level.sortedClients[ i ] ];
-
- //check for what mode we are working in and display relevent data
- if( g_AllStats.integer == 1 )
- {
- //check if client is connected and on same team
- if( tmpent->client && tmpent->client->pers.connected == CON_CONNECTED && tmpent->client->pers.teamSelection == ent->client->pers.teamSelection && tmpent->client->pers.teamSelection != PTE_NONE )
- {
- NumResults++;
- if( tmpent->client->pers.teamSelection == PTE_ALIENS ) Teamcolor = 1;
- if( tmpent->client->pers.teamSelection == PTE_HUMANS ) Teamcolor = 5;
- ADMP( va( "^%i%2i^3|^%i%3i %3i %3i^3|^%i%3i %3i %3i %3i^3|^%i%5i %5i^3|^%i%3i^3|^7 %s\n",
- Teamcolor,
- NumResults,
- Teamcolor,
- ( tmpent->client->pers.statscounters.kills ) ? tmpent->client->pers.statscounters.kills : 0,
- ( tmpent->client->pers.statscounters.assists ) ? tmpent->client->pers.statscounters.assists : 0,
- ( tmpent->client->pers.statscounters.structskilled ) ? tmpent->client->pers.statscounters.structskilled : 0,
- Teamcolor,
- ( tmpent->client->pers.statscounters.deaths ) ? tmpent->client->pers.statscounters.deaths : 0,
- ( tmpent->client->pers.statscounters.feeds ) ? tmpent->client->pers.statscounters.feeds : 0,
- ( tmpent->client->pers.statscounters.suicides ) ? tmpent->client->pers.statscounters.suicides : 0,
- ( tmpent->client->pers.statscounters.teamkills ) ? tmpent->client->pers.statscounters.teamkills : 0,
- Teamcolor,
- ( tmpent->client->pers.statscounters.dmgdone ) ? tmpent->client->pers.statscounters.dmgdone : 0,
- ( tmpent->client->pers.statscounters.ffdmgdone ) ? tmpent->client->pers.statscounters.ffdmgdone : 0,
- Teamcolor,
- ( tmpent->client->pers.statscounters.structsbuilt ) ? tmpent->client->pers.statscounters.structsbuilt : 0,
- ( tmpent->client->pers.netname ) ? tmpent->client->pers.netname : "Unknown" ) );
- }
- }
- else if( g_AllStats.integer == 2 )
- {
- //check if client is connected and has some stats or atleast is on a team
- if( tmpent->client && tmpent->client->pers.connected == CON_CONNECTED && ( tmpent->client->pers.teamSelection != PTE_NONE ) )
- {
- NumResults++;
- if( tmpent->client->pers.teamSelection == PTE_ALIENS ) Teamcolor = 1;
- if( tmpent->client->pers.teamSelection == PTE_HUMANS ) Teamcolor = 5;
- ADMP( va( "^%i%2i^3|^%i%3i %3i %3i^3|^%i%3i %3i %3i %3i^3|^%i%5i %5i^3|^%i%3i^3|^7 %s\n",
- Teamcolor,
- NumResults,
- Teamcolor,
- ( tmpent->client->pers.statscounters.kills ) ? tmpent->client->pers.statscounters.kills : 0,
- ( tmpent->client->pers.statscounters.assists ) ? tmpent->client->pers.statscounters.assists : 0,
- ( tmpent->client->pers.statscounters.structskilled ) ? tmpent->client->pers.statscounters.structskilled : 0,
- Teamcolor,
- ( tmpent->client->pers.statscounters.deaths ) ? tmpent->client->pers.statscounters.deaths : 0,
- ( tmpent->client->pers.statscounters.feeds ) ? tmpent->client->pers.statscounters.feeds : 0,
- ( tmpent->client->pers.statscounters.suicides ) ? tmpent->client->pers.statscounters.suicides : 0,
- ( tmpent->client->pers.statscounters.teamkills ) ? tmpent->client->pers.statscounters.teamkills : 0,
- Teamcolor,
- ( tmpent->client->pers.statscounters.dmgdone ) ? tmpent->client->pers.statscounters.dmgdone : 0,
- ( tmpent->client->pers.statscounters.ffdmgdone ) ? tmpent->client->pers.statscounters.ffdmgdone : 0,
- Teamcolor,
- ( tmpent->client->pers.statscounters.structsbuilt ) ? tmpent->client->pers.statscounters.structsbuilt : 0,
- ( tmpent->client->pers.netname ) ? tmpent->client->pers.netname : "Unknown" ) );
- }
- }
- }
- if( NumResults == 0 ) {
- ADMP( " ^3EMPTY!\n" );
- } else {
- ADMP( va( "^7 %i Players found!\n", NumResults ) );
- }
- //update time last viewed
-
- ent->client->pers.statscounters.AllstatstimeLastViewed = level.time;
- return;
-}
/*
=================
@@ -4412,12 +4757,6 @@ void Cmd_Follow_f( gentity_t *ent )
trap_SendServerCommand( ent - g_entities, "print \"follow: You cannot follow unless you are dead or on the spectators.\n\"" );
return;
}
- if( ent->client->pers.paused )
- {
- trap_SendServerCommand( ent-g_entities,
- "print \"You may not build while paused\n\"" );
- return;
- }
if( trap_Argc( ) != 2 )
{
@@ -4569,30 +4908,8 @@ void Cmd_PTRCRestore_f( gentity_t *ent )
connection = ent->client->pers.connection;
if( connection && connection->ptrCode == code )
{
- // Set the correct team
- if( !( ent->client->pers.specExpires > level.time ) )
- {
- // Check if the alien team is full
- if( connection->clientTeam == PTE_ALIENS &&
- !G_admin_permission(ent, ADMF_FORCETEAMCHANGE) &&
- g_teamForceBalance.integer &&
- level.numAlienClients > level.numHumanClients )
- {
- G_TriggerMenu( ent - g_entities, MN_A_TEAMFULL );
- }
- // Check if the human team is full
- else if( connection->clientTeam == PTE_HUMANS &&
- !G_admin_permission(ent, ADMF_FORCETEAMCHANGE) &&
- g_teamForceBalance.integer &&
- level.numHumanClients > level.numAlienClients )
- {
- G_TriggerMenu( ent - g_entities, MN_H_TEAMFULL );
- }
- else
- {
- G_ChangeTeam( ent, connection->clientTeam );
- }
- }
+ // set the correct team
+ G_ChangeTeam( ent, connection->clientTeam );
// set the correct credit etc.
ent->client->ps.persistant[ PERS_CREDIT ] = 0;
@@ -4688,39 +5005,39 @@ static void Cmd_Ignore_f( gentity_t *ent )
char arg1[ MAX_STRING_TOKENS ];
char arg2[ MAX_STRING_TOKENS ];
pTeam_t team;
-
+
if( !ent || !ent->client || ( ent->client->pers.teamSelection == PTE_NONE ) )
{
return;
}
-
+
if( !g_allowShare.integer )
{
trap_SendServerCommand( ent-g_entities, "print \"Share has been disabled.\n\"" );
return;
}
-
+
if( g_floodMinTime.integer )
if ( G_Flood_Limited( ent ) )
{
trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" );
return;
}
-
+
team = ent->client->pers.teamSelection;
-
+
G_SayArgv( 0, cmd, sizeof( cmd ) );
if( !Q_stricmp( cmd, "say" ) || !Q_stricmp( cmd, "say_team" ) )
{
skipargs = 1;
G_SayArgv( 1, cmd, sizeof( cmd ) );
}
-
+
// target player name is in arg1
G_SayArgv( 1+skipargs, arg1, sizeof( arg1 ) );
// amount to be shared is in arg2
G_SayArgv( 2+skipargs, arg2, sizeof( arg2 ) );
-
+
if( arg1[0] && !strchr( arg1, ';' ) && Q_stricmp( arg1, "target_in_aim" ) )
{
//check arg1 is a number
@@ -4732,7 +5049,7 @@ static void Cmd_Ignore_f( gentity_t *ent )
break;
}
}
-
+
if( clientNum >= 0 )
{
clientNum = atoi( arg1 );
@@ -4759,14 +5076,15 @@ static void Cmd_Ignore_f( gentity_t *ent )
vec3_t forward, end;
trace_t tr;
gentity_t *traceEnt;
-
+
+
// trace a teammate
AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL );
VectorMA( ent->client->ps.origin, 8192 * 16, forward, end );
-
+
trap_Trace( &tr, ent->client->ps.origin, NULL, NULL, end, ent->s.number, MASK_PLAYERSOLID );
traceEnt = &g_entities[ tr.entityNum ];
-
+
if( tr.fraction < 1.0f && traceEnt->client &&
( traceEnt->client->pers.teamSelection == team ) )
{
@@ -4780,7 +5098,7 @@ static void Cmd_Ignore_f( gentity_t *ent )
return;
}
}
-
+
// verify target player team
if( ( clientNum < 0 ) || ( clientNum >= level.maxclients ) ||
( level.clients[ clientNum ].pers.teamSelection != team ) )
@@ -4789,7 +5107,7 @@ static void Cmd_Ignore_f( gentity_t *ent )
"print \"share: not a valid player of your team.\n\"" );
return;
}
-
+
if( !arg2[0] || strchr( arg2, ';' ) )
{
// default credit count
@@ -4814,11 +5132,11 @@ static void Cmd_Ignore_f( gentity_t *ent )
return;
}
}
-
+
// credit count from parameter
creds = atoi( arg2 );
}
-
+
// player specified "0" to transfer
if( creds <= 0 )
{
@@ -4826,13 +5144,13 @@ static void Cmd_Ignore_f( gentity_t *ent )
"print \"Ooh, you are a generous one, indeed!\n\"" );
return;
}
-
+
// transfer only credits the player really has
if( creds > ent->client->pers.credit )
{
creds = ent->client->pers.credit;
}
-
+
// player has no credits
if( creds <= 0 )
{
@@ -4840,7 +5158,7 @@ static void Cmd_Ignore_f( gentity_t *ent )
"print \"Earn some first, lazy gal!\n\"" );
return;
}
-
+
// allow transfers only up to the credit/evo limit
if( ( team == PTE_HUMANS ) &&
( creds > HUMAN_MAX_CREDITS - level.clients[ clientNum ].pers.credit ) )
@@ -4852,7 +5170,7 @@ static void Cmd_Ignore_f( gentity_t *ent )
{
creds = ALIEN_MAX_KILLS - level.clients[ clientNum ].pers.credit;
}
-
+
// target cannot take any more credits
if( creds <= 0 )
{
@@ -4861,7 +5179,7 @@ static void Cmd_Ignore_f( gentity_t *ent )
( team == PTE_HUMANS ) ? "credits" : "evolvepoints" ) );
return;
}
-
+
// transfer credits
G_AddCreditToClient( ent->client, -creds, qfalse );
trap_SendServerCommand( ent-g_entities,
@@ -4873,7 +5191,7 @@ static void Cmd_Ignore_f( gentity_t *ent )
va( "print \"You have received %d %s from %s^7.\n\"", creds,
( team == PTE_HUMANS ) ? "credits" : "evolvepoints",
ent->client->pers.netname ) );
-
+
G_LogPrintf( "Share: %i %i %i %d: %s^7 transferred %d%s to %s^7\n",
ent->client->ps.clientNum,
clientNum,
@@ -4884,35 +5202,36 @@ static void Cmd_Ignore_f( gentity_t *ent )
( team == PTE_HUMANS ) ? "c" : "e",
level.clients[ clientNum ].pers.netname );
}
-
+
/*
=================
Cmd_Donate_f
-
+
Alms for the poor
=================
*/
void Cmd_Donate_f( gentity_t *ent ) {
char s[ MAX_TOKEN_CHARS ] = "", *type = "evo(s)";
int i, value, divisor, portion, new_credits, total=0,
- max = ALIEN_MAX_KILLS, *amounts, *totals;
+ max = ALIEN_MAX_KILLS, *amounts;
qboolean donated = qtrue;
-
+
if( !ent->client ) return;
-
+
if( !g_allowShare.integer )
{
trap_SendServerCommand( ent-g_entities, "print \"Donate has been disabled.\n\"" );
return;
}
-
+
if( g_floodMinTime.integer )
if ( G_Flood_Limited( ent ) )
{
trap_SendServerCommand( ent-g_entities, "print \"Your chat is flood-limited; wait before chatting again\n\"" );
return;
}
-
+
+
if( ent->client->pers.teamSelection == PTE_ALIENS )
divisor = level.numAlienClients-1;
else if( ent->client->pers.teamSelection == PTE_HUMANS ) {
@@ -4924,13 +5243,13 @@ static void Cmd_Ignore_f( gentity_t *ent )
va( "print \"donate: spectators cannot be so gracious\n\"" ) );
return;
}
-
+
if( divisor < 1 ) {
trap_SendServerCommand( ent-g_entities,
"print \"donate: get yourself some teammates first\n\"" );
return;
}
-
+
trap_Argv( 1, s, sizeof( s ) );
value = atoi(s);
if( value <= 0 ) {
@@ -4940,15 +5259,11 @@ static void Cmd_Ignore_f( gentity_t *ent )
}
if( value > ent->client->pers.credit)
value = ent->client->pers.credit;
-
+
// allocate memory for distribution amounts
amounts = G_Alloc( level.maxclients * sizeof( int ) );
- totals = G_Alloc( level.maxclients * sizeof( int ) );
- for( i = 0; i < level.maxclients; i++ ) {
- amounts[ i ] = 0;
- totals[ i ] = 0;
- }
-
+ for( i = 0; i < level.maxclients; i++ ) amounts[ i ] = 0;
+
// determine donation amounts for each client
total = value;
while( donated && value ) {
@@ -4962,10 +5277,8 @@ static void Cmd_Ignore_f( gentity_t *ent )
ent->client->pers.teamSelection ) {
new_credits = level.clients[ i ].pers.credit + portion;
amounts[ i ] = portion;
- totals[ i ] += portion;
if( new_credits > max ) {
amounts[ i ] -= new_credits - max;
- totals[ i ] -= new_credits - max;
new_credits = max;
}
if( amounts[ i ] ) {
@@ -4976,23 +5289,23 @@ static void Cmd_Ignore_f( gentity_t *ent )
}
}
}
-
+
// transfer funds
G_AddCreditToClient( ent->client, value - total, qtrue );
for( i = 0; i < level.maxclients; i++ )
- if( totals[ i ] ) {
+ if( amounts[ i ] ) {
trap_SendServerCommand( i,
va( "print \"%s^7 donated %d %s to you, don't forget to say 'thank you'!\n\"",
- ent->client->pers.netname, totals[ i ], type ) );
+ ent->client->pers.netname, amounts[ i ], type ) );
}
-
+
G_Free( amounts );
- G_Free( totals );
-
+
trap_SendServerCommand( ent-g_entities,
va( "print \"Donated %d %s to the cause.\n\"",
total-value, type ) );
}
+
commands_t cmds[ ] = {
// normal commands
@@ -5016,9 +5329,22 @@ 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 },
- { "allstats", 0|CMD_INTERMISSION, Cmd_AllStats_f },
{ "teamstatus", CMD_TEAM, Cmd_TeamStatus_f },
// cheats
@@ -5047,6 +5373,7 @@ commands_t cmds[ ] = {
{ "build", CMD_TEAM|CMD_LIVING, Cmd_Build_f },
{ "deconstruct", CMD_TEAM|CMD_LIVING, Cmd_Destroy_f },
{ "mark", CMD_TEAM|CMD_LIVING, Cmd_Mark_f },
+ { "aim", CMD_HUMAN|CMD_LIVING, Cmd_Aim_f },
{ "buy", CMD_HUMAN|CMD_LIVING, Cmd_Buy_f },
{ "sell", CMD_HUMAN|CMD_LIVING, Cmd_Sell_f },
@@ -5403,15 +5730,15 @@ void G_PrivateMessage( gentity_t *ent )
if( teamonly && !OnSameTeam( ent, tmpent ) )
continue;
-
+
// Ignore sending to invisible players
if( tmpent->client->sess.invisible == qtrue && !G_admin_permission( ent, "invisible" ) )
continue;
-
+
// Ignore sending to non-invisible-capable players while invisible
if( ent->client->sess.invisible == qtrue && !G_admin_permission( tmpent, "invisible" ) )
continue;
-
+
if( BG_ClientListTest( &tmpent->client->sess.ignoreList,
ent-g_entities ) )
{
@@ -5662,48 +5989,3 @@ qboolean G_IsMuted( gclient_t *client )
return muteState;
}
-
-/*
-==================
-G_TeamKill_Repent
-
-Determine whether a players team kill activity is high
-==================
-*/
-
-qboolean G_TeamKill_Repent( gentity_t *ent )
-{
- int millisSinceLastTeamKill;
-
- // Doesn't work if g_teamKillThreshold isn't set
- if( !g_teamKillThreshold.integer ||
- g_teamKillThreshold.integer == 0 )
- return qfalse;
-
- // Doesn't work when game is paused
- if( level.paused )
- return qfalse;
-
- millisSinceLastTeamKill = level.time - ent->client->pers.lastTeamKillTime;
- if( millisSinceLastTeamKill < 30000 )
- ent->client->pers.teamKillDemerits++;
- else
- {
- ent->client->pers.teamKillDemerits--;
- if( ent->client->pers.teamKillDemerits < 0 )
- ent->client->pers.teamKillDemerits = 0;
- }
-
- ent->client->pers.lastTeamKillTime = level.time;
-
- if ( ent->client->pers.teamKillDemerits >= ( g_teamKillThreshold.integer + 2 ) )
- trap_SendConsoleCommand( 0, va( "!ban %s 30m team killing\n", ent->client->pers.ip ) );
- else if ( ent->client->pers.teamKillDemerits == ( g_teamKillThreshold.integer + 1 ) )
- trap_SendConsoleCommand( 0, va( "!warn %i team killing\n", ent->client->ps.clientNum ) );
- else if ( ent->client->pers.teamKillDemerits == g_teamKillThreshold.integer )
- G_AdminsPrintf( "Team killer %s^7 has team killed ^6%i^7 times.\n",
- ent->client->pers.netname,
- ent->client->pers.statscounters.teamkills );
-
- return qfalse;
-}
diff --git a/src/game/g_combat.c b/src/game/g_combat.c
index 0cc079a..0d8d013 100644
--- a/src/game/g_combat.c
+++ b/src/game/g_combat.c
@@ -138,6 +138,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 )
@@ -163,6 +164,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
if( attacker != self && attacker->client->ps.stats[ STAT_PTEAM ] == self->client->ps.stats[ STAT_PTEAM ] )
{
attacker->client->pers.statscounters.teamkills++;
+ attacker->client->pers.karma -= 300;
if( attacker->client->pers.teamSelection == PTE_ALIENS )
{
level.alienStatsCounters.teamkills++;
@@ -205,6 +207,16 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
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 )
{
trap_SendServerCommand( -1,
@@ -233,13 +245,14 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
va( "cp \"You killed ^1TEAMMATE^7 %s\"", self->client->pers.netname ) );
G_LogOnlyPrintf("%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 ] );
- G_TeamKill_Repent( attacker );
+ G_admin_tklog_log( attacker, self, meansOfDeath );
}
self->enemy = attacker;
self->client->ps.persistant[ PERS_KILLED ]++;
self->client->pers.statscounters.deaths++;
+ self->client->pers.karma -= 10;
if( self->client->pers.teamSelection == PTE_ALIENS )
{
level.alienStatsCounters.deaths++;
@@ -280,6 +293,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
attacker->client->lastKillTime = level.time;
attacker->client->pers.statscounters.kills++;
+ attacker->client->pers.karma += 50;
if( attacker->client->pers.teamSelection == PTE_ALIENS )
{
level.alienStatsCounters.kills++;
@@ -288,11 +302,26 @@ 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;
+ attacker->client->pers.karma += 50;
+ 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 )
{
attacker->client->pers.statscounters.suicides++;
+ attacker->client->pers.karma -= 100;
if( attacker->client->pers.teamSelection == PTE_ALIENS )
{
level.alienStatsCounters.suicides++;
@@ -441,7 +470,16 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
if( g_alienStage.integer < 2 )
{
self->client->pers.statscounters.feeds++;
+ self->client->pers.karma -= 25;
level.humanStatsCounters.feeds++;
+
+ if( g_feedingSpree.integer &&
+ level.reactorPresent &&
+ !G_BuildableRange( self->client->ps.origin, 600, BA_H_REACTOR ) &&
+ !G_BuildableRange( self->client->ps.origin, 200, BA_H_SPAWN ) )
+ {
+ self->client->pers.statscounters.spreefeeds += SPREE_FEED_VALUE;
+ }
}
}
else if( self->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
@@ -450,7 +488,16 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
if( g_humanStage.integer < 2 )
{
self->client->pers.statscounters.feeds++;
+ self->client->pers.karma -= 25;
level.alienStatsCounters.feeds++;
+
+ if( g_feedingSpree.integer &&
+ level.overmindPresent &&
+ !G_BuildableRange( self->client->ps.origin, 600, BA_A_OVERMIND ) &&
+ !G_BuildableRange( self->client->ps.origin, 200, BA_A_SPAWN ) )
+ {
+ self->client->pers.statscounters.spreefeeds += SPREE_FEED_VALUE;
+ }
}
}
}
@@ -482,6 +529,9 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
level.humanStatsCounters.assists++;
}
+ if( spreeRate && player == attacker )
+ percentDamage *= (float)spreeRate;
+
//add credit
G_AddCreditToClient( player->client,
(int)( classValue * percentDamage ), qtrue );
@@ -516,6 +566,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int
if( percentDamage > 0 && percentDamage < 1)
{
player->client->pers.statscounters.assists++;
+ player->client->pers.karma += 25;
level.alienStatsCounters.assists++;
}
@@ -524,7 +575,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;
@@ -565,7 +619,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;
@@ -618,6 +675,38 @@ finish_dying: // from MOD_SLAP
// g_forcerespawn may force spawning at some later time
self->client->respawnTime = level.time + 1700;
+ if( g_feedingSpree.integer )
+ {
+ int maxfeed;
+
+ maxfeed = g_feedingSpree.integer * SPREE_FEED_VALUE;
+ if( self->client->pers.statscounters.spreefeeds > maxfeed )
+ {
+ self->client->respawnTime += SPREE_FEED_DELAY * (self->client->pers.statscounters.spreefeeds - maxfeed );
+ self->client->pers.karma -= 20;
+ 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 ) );
+ }
+ }
+
+ if( self->client->pers.bleeder )
+ {
+ int spreeLength;
+
+ spreeLength = self->client->pers.statscounters.spreebleeds / 10;
+ self->client->respawnTime += 9 * 1000;
+
+ trap_SendServerCommand( self->client->ps.clientNum,
+ va( "print \"^3Bleeding has made you an enemy of your own base for ^7%d:%02d\n^1Respawn delayed ^7%d^1 seconds\n",
+ spreeLength / 60, spreeLength % 60,
+ ( self->client->respawnTime - level.time) / 1000 ) );
+ trap_SendServerCommand( self->client->ps.clientNum,
+ va( "cp \"^3Bleeding made you an enemy of your own base!\n\n^7for %d:%02d\n\n^1Respawn delayed %d seconds\"",
+ spreeLength / 60, spreeLength % 60,
+ ( self->client->respawnTime - level.time) / 1000 ) );
+ }
+
// remove powerups
memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) );
@@ -1013,6 +1102,7 @@ static float G_CalcDamageModifier( vec3_t point, gentity_t *targ, gentity_t *att
if( attacker && attacker->client && modifier == 2 )
{
attacker->client->pers.statscounters.headshots++;
+ attacker->client->pers.karma += 5;
level.alienStatsCounters.headshots++;
}
@@ -1149,7 +1239,7 @@ dflags these flags are used to control how T_Damage works
void G_SelectiveDamage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
vec3_t dir, vec3_t point, int damage, int dflags, int mod, int team )
{
- if( targ->client && ( team != targ->client->ps.stats[ STAT_PTEAM ] ) )
+ if( targ->client && ( team != targ->client->ps.stats[ STAT_PTEAM ] || targ->client->pers.bleeder ) )
G_Damage( targ, inflictor, attacker, dir, point, damage, dflags, mod );
}
@@ -1398,6 +1488,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
targ->client->lastPoisonTime = level.time;
targ->client->lastPoisonClient = attacker;
attacker->client->pers.statscounters.repairspoisons++;
+ attacker->client->pers.karma += 1;
level.alienStatsCounters.repairspoisons++;
}
}
@@ -1429,6 +1520,8 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
if( targ->biteam == attacker->client->pers.teamSelection || OnSameTeam( targ, attacker ) )
{
attacker->client->pers.statscounters.ffdmgdone += takeNoOverkill;
+ if( attacker != targ )
+ attacker->client->pers.karma -= take;
if( attacker->client->pers.teamSelection == PTE_ALIENS )
{
level.alienStatsCounters.ffdmgdone+=takeNoOverkill;
@@ -1437,10 +1530,88 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
{
level.humanStatsCounters.ffdmgdone+=takeNoOverkill;
}
+
+ // bleeding spree
+ if( g_bleedingSpree.integer > 1 && attacker != targ &&
+ mod != MOD_SUICIDE && mod != MOD_TELEFRAG )
+ {
+ attacker->client->pers.statscounters.spreebleeds += take;
+
+ if( g_bleedingSpreeKick.integer == 2 )
+ {
+ if( attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 100 )
+ {
+ char buf[ MAX_STRING_CHARS ];
+
+ G_admin_autorevert( attacker );
+
+ Com_sprintf( buf, sizeof( buf ),
+ "%s^7 moved from %s to spectators due to excessive team damage\n",
+ attacker->client->pers.netname,
+ ( attacker->client->pers.teamSelection == PTE_ALIENS ) ? "aliens" : "humans" );
+ trap_SendServerCommand( -1, va( "print \"%s\"", buf ) );
+ G_LogOnlyPrintf( "Teamkilling: %s", buf );
+
+ G_admin_tklog_log( attacker, NULL, mod );
+
+ trap_SendConsoleCommand( EXEC_APPEND, va( "!putteam %d s %s\n", attacker - g_entities, g_adminTempSpec.string ) );
+ attacker->client->pers.statscounters.spreebleeds = 0;
+ }
+ else if( attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 66 &&
+ attacker->client->pers.bleederLastWarn + 20 * 1000 < level.time )
+ {
+ attacker->client->pers.bleederLastWarn = level.time;
+ trap_SendServerCommand( attacker - g_entities,
+ "print \"^3Please do not damage your teammates or your base\n\"" );
+ trap_SendServerCommand( attacker - g_entities,
+ "cp \"^1Please do not damage your teammates or your base\"" );
+ }
+ }
+ else if( g_bleedingSpreeKick.integer && attacker->client->pers.bleeder &&
+ attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 100 + g_bleedingSpree.integer * 20 )
+ {
+ trap_SendConsoleCommand( EXEC_APPEND,
+ va("!kick %d auto-kick for team bleeding\n", ( attacker - g_entities )) );
+ attacker->client->pers.statscounters.spreebleeds = -9000;
+ }
+ else if( attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 100 &&
+ !attacker->client->pers.bleeder )
+ {
+ attacker->client->pers.bleeder = qtrue;
+ attacker->client->pers.karma -= 500;
+ level.bleeders++;
+ trap_SendServerCommand( -1,
+ va( "print \"%s^3 has become an enemy of their own base\n\"",
+ attacker->client->pers.netname ) );
+ trap_SendServerCommand( attacker - g_entities,
+ "cp \"^1Your team bleeding has irritated your base!\"" );
+
+ G_admin_tklog_log( attacker, NULL, mod );
+
+ if( g_bleedingSpreeKick.integer )
+ trap_SendServerCommand( attacker - g_entities,
+ "print \"^1Continued bleeding will result in kick\n\"" );
+ }
+ else if( attacker->client->pers.statscounters.spreebleeds > g_bleedingSpree.integer * 66 &&
+ !attacker->client->pers.bleeder &&
+ attacker->client->pers.bleederLastWarn + 20 * 1000 < level.time )
+ {
+ attacker->client->pers.bleederLastWarn = level.time;
+ trap_SendServerCommand( attacker - g_entities,
+ "print \"^3Your bleeding is close to aggravating your base!\n\"" );
+ trap_SendServerCommand( attacker - g_entities,
+ "cp \"^1Your bleeding is close to aggravating your base!\"" );
+
+ if( g_bleedingSpreeKick.integer )
+ trap_SendServerCommand( attacker - g_entities,
+ "print \"^1Continued bleeding will result in kick\n\"" );
+ }
+ }
}
else if( targ->s.eType == ET_BUILDABLE )
{
attacker->client->pers.statscounters.structdmgdone += takeNoOverkill;
+ attacker->client->pers.karma += ( take / 10 );
if( attacker->client->pers.teamSelection == PTE_ALIENS )
{
@@ -1454,6 +1625,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
if( targ->health > 0 && ( targ->health - take ) <=0 )
{
attacker->client->pers.statscounters.structskilled++;
+ attacker->client->pers.karma += 10;
if( attacker->client->pers.teamSelection == PTE_ALIENS )
{
level.alienStatsCounters.structskilled++;
@@ -1461,12 +1633,27 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
else if( attacker->client->pers.teamSelection == PTE_HUMANS )
{
level.humanStatsCounters.structskilled++;
+
+ if( attacker->client->pers.statscounters.spreefeeds )
+ {
+ attacker->client->pers.statscounters.spreefeeds -= take;
+ if( attacker->client->pers.statscounters.spreefeeds < 0 )
+ attacker->client->pers.statscounters.spreefeeds = 0;
+ }
+
+ if( attacker->client->pers.statscounters.spreebleeds > 10 )
+ {
+ attacker->client->pers.statscounters.spreebleeds -= take / 3;
+ if( attacker->client->pers.statscounters.spreebleeds < 10 )
+ attacker->client->pers.statscounters.spreebleeds = 10;
+ }
}
}
}
else if( targ->client )
{
attacker->client->pers.statscounters.dmgdone +=takeNoOverkill;
+ attacker->client->pers.karma += ( take / 10 );
attacker->client->pers.statscounters.hits++;
if( attacker->client->pers.teamSelection == PTE_ALIENS )
{
@@ -1476,9 +1663,52 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
{
level.humanStatsCounters.dmgdone+=takeNoOverkill;
}
+
+ if( attacker->client->pers.statscounters.spreefeeds )
+ {
+ attacker->client->pers.statscounters.spreefeeds -= take;
+ if( attacker->client->pers.statscounters.spreefeeds < 0 )
+ attacker->client->pers.statscounters.spreefeeds = 0;
+ }
+
+ if( attacker->client->pers.statscounters.spreebleeds > 10 )
+ {
+ attacker->client->pers.statscounters.spreebleeds -= take / 3;
+ if( attacker->client->pers.statscounters.spreebleeds < 10 )
+ attacker->client->pers.statscounters.spreebleeds = 10;
+ }
}
}
+ if( g_vampireDeath.integer && targ->client && attacker->client && targ != attacker )
+ {
+ if( OnSameTeam( targ, attacker ) )
+ {
+ if( mod == MOD_GRENADE )
+ return;
+
+ if( take > attacker->health )
+ take = attacker->health;
+
+ attacker->health -= take;
+ attacker->client->ps.stats[ STAT_HEALTH ] = attacker->health;
+ attacker->lastDamageTime = level.time;
+
+ if( attacker->health <= 0 )
+ {
+ if( attacker->health < -999 )
+ attacker->health = -999;
+ attacker->die( attacker, attacker, attacker, take, MOD_SUICIDE );
+ }
+
+ take = 0 - take;
+ }
+ else
+ {
+ attacker->health += takeNoOverkill;
+ attacker->client->ps.stats[ STAT_HEALTH ] = attacker->health;
+ }
+ }
//Do the damage
targ->health = targ->health - take;
diff --git a/src/game/g_local.h b/src/game/g_local.h
index 8c65998..2294f6f 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -53,19 +53,6 @@ typedef struct gclient_s gclient_t;
#define FL_NO_HUMANS 0x00004000 // spawn point just for bots
#define FL_FORCE_GESTURE 0x00008000 // spawn point just for bots
-typedef struct
-{
- qboolean isNB;
- float Area;
- float Height;
-} noBuild_t;
-
-typedef struct
-{
- gentity_t *Marker;
- vec3_t Origin;
-} nbMarkers_t;
-
// movers are things like doors, plats, buttons, etc
typedef enum
{
@@ -256,9 +243,6 @@ struct gentity_s
int lastDamageTime;
int bdnumb; // buildlog entry ID
-
- // For nobuild!
- noBuild_t noBuild;
};
typedef enum
@@ -319,8 +303,8 @@ typedef struct
spectatorState_t spectatorState;
int spectatorClient; // for chasecam and follow mode
int wins, losses; // tournament stats
- qboolean invisible; // for being invisible on the server - ghosts!
qboolean teamLeader; // true when this client is a team leader
+ qboolean invisible; // for being invisible on the server - ghosts!
clientList_t ignoreList;
} clientSession_t;
@@ -356,11 +340,13 @@ typedef struct
short headshots;
int hits;
int hitslocational;
+ short spreekills;
+ short spreefeeds;
+ int spreebleeds;
short teamkills;
int dretchbasytime;
int jetpackusewallwalkusetime;
int timeLastViewed;
- int AllstatstimeLastViewed;
} statsCounters_t;
typedef struct
@@ -426,11 +412,11 @@ typedef struct
int lastFloodTime; // level.time of last flood-limited command
int floodDemerits; // number of flood demerits accumulated
- char lastMessage[ MAX_SAY_TEXT ]; // last message said by this player
- int lastMessageTime; // level.time of last message said by this player
+ int vampireSuckFraction;
- int lastTeamKillTime; // level.time of last team kill
- int teamKillDemerits; // number of team kill demerits accumulated
+ int spreeTime1000;
+ qboolean bleeder;
+ int bleederLastWarn;
vec3_t lastDeathLocation;
char guid[ 33 ];
@@ -446,6 +432,9 @@ typedef struct
int adminLevel;
char adminName[ MAX_NETNAME ];
qboolean designatedBuilder;
+ char chat[ CHAT_MAXCHAN ][ CHAT_MAXPASS ];
+ int karma;
+ int bubbleTime;
qboolean firstConnect; // This is the first map since connect
qboolean useUnlagged;
statsCounters_t statscounters;
@@ -615,6 +604,7 @@ typedef struct armourRegion_s
typedef enum
{
TW_NOT = 0,
+ TW_SOON,
TW_IMMINENT,
TW_PASSED
} timeWarning_t;
@@ -633,6 +623,7 @@ typedef struct buildHistory_s buildHistory_t;
struct buildHistory_s
{
int ID; // persistent ID to aid in specific reverting
+ int time; // time the event occured
gentity_t *ent; // who, NULL if they've disconnected (or aren't an ent)
char name[ MAX_NETNAME ]; // who, saves name if ent is NULL
int buildable; // what
@@ -702,6 +693,7 @@ typedef struct
char teamVoteString[ 2 ][ MAX_STRING_CHARS ];
char teamVoteDisplayString[ 2 ][ MAX_STRING_CHARS ];
int teamVoteTime[ 2 ]; // level.time vote was called
+ int teamVotePassThreshold[ 2 ];
int teamVoteYes[ 2 ];
int teamVoteNo[ 2 ];
int numteamVotingClients[ 2 ]; // set by CalculateRanks
@@ -747,6 +739,14 @@ typedef struct
int humanBuildPoints;
int humanBuildPointsPowered;
+ int alienRecoverBuildPoints;
+ int alienRecoverTime;
+ int humanRecoverBuildPoints;
+ int humanRecoverTime;
+
+ int defaultAlienBuildPoints;
+ int defaultHumanBuildPoints;
+
gentity_t *markedBuildables[ MAX_GENTITIES ];
int numBuildablesForRemoval;
@@ -769,6 +769,10 @@ typedef struct
timeWarning_t timelimitWarning;
int extend_vote_count;
+ int vampireDeath;
+ int vampireDeathBeginTime;
+ timeWarning_t vampireDeathWarning;
+
spawnQueue_t alienSpawnQueue;
spawnQueue_t humanSpawnQueue;
@@ -789,6 +793,8 @@ typedef struct
int pause_ff;
int pause_ffb;
+ int bleeders;
+
int lastCreditedAlien;
int lastCreditedHuman;
@@ -807,12 +813,6 @@ typedef struct
statsCounters_level alienStatsCounters;
statsCounters_level humanStatsCounters;
-
- qboolean noBuilding;
- float nbArea;
- float nbHeight;
-
- nbMarkers_t nbMarkers[ MAX_GENTITIES ];
} level_locals_t;
#define CMD_CHEAT 0x01
@@ -863,6 +863,9 @@ void G_LeaveTeam( gentity_t *self );
void G_ChangeTeam( gentity_t *ent, pTeam_t newTeam );
void G_SanitiseString( char *in, char *out, int len );
void G_PrivateMessage( gentity_t *ent );
+void Cmd_Join_f( gentity_t *ent );
+void Cmd_Part_f( gentity_t *ent );
+void Cmd_Channel_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 );
@@ -871,7 +874,6 @@ void Cmd_Builder_f( gentity_t *ent );
void G_WordWrap( char *buffer, int maxwidth );
void G_CP( gentity_t *ent );
qboolean G_IsMuted( gclient_t *ent );
-qboolean G_TeamKill_Repent( gentity_t *ent );
//
// g_physics.c
@@ -919,8 +921,11 @@ qboolean G_IsPowered( vec3_t origin );
qboolean G_IsDCCBuilt( void );
qboolean G_IsOvermindBuilt( void );
+void G_RecoverBuildPoints( gentity_t *self );
+
void G_BuildableThink( gentity_t *ent, int msec );
qboolean G_BuildableRange( vec3_t origin, float r, buildable_t buildable );
+float G_BuildableRangeClosest( vec3_t origin, float r, buildable_t buildable );
itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance, vec3_t origin );
qboolean G_BuildingExists( int bclass ) ;
qboolean G_BuildIfValid( gentity_t *ent, buildable_t buildable );
@@ -941,8 +946,16 @@ qboolean G_RevertCanFit( buildHistory_t *bh );
int G_LogBuild( buildHistory_t *new );
int G_CountBuildLog( void );
char *G_FindBuildLogName( int id );
-void G_NobuildSave( void );
-void G_NobuildLoad( void );
+
+void nobuild_init( void );
+void nobuild_add( gentity_t *ent );
+void nobuild_del( gentity_t *ent );
+void nobuild_command( gentity_t *ent, qboolean next_zone, qboolean next_type, float size );
+void nobuild_set( qboolean enable, gentity_t *ent );
+void nobuild_save( void );
+void nobuild_go( gentity_t *ent );
+void nobuild_list( gentity_t *ent );
+int nobuild_check( vec3_t origin );
//
// g_utils.c
@@ -1131,7 +1144,6 @@ void QDECL G_LogPrintf( const char *fmt, ... );
void QDECL G_LogPrintfColoured( const char *fmt, ... );
void QDECL G_LogOnlyPrintf( const char *fmt, ... );
void QDECL G_AdminsPrintf( const char *fmt, ... );
-void QDECL G_WarningsPrintf( char *flag, const char *fmt, ... );
void QDECL G_LogOnlyPrintf( const char *fmt, ... );
void SendScoreboardMessageToAllClients( void );
void QDECL G_Printf( const char *fmt, ... );
@@ -1140,6 +1152,7 @@ void CheckVote( void );
void CheckTeamVote( int teamnum );
void LogExit( const char *string );
int G_TimeTilSuddenDeath( void );
+int G_TimeTilVampireDeath( void );
void CheckMsgTimer( void );
qboolean G_Flood_Limited( gentity_t *ent );
@@ -1205,8 +1218,7 @@ typedef enum
MCV_RANDOM,
MCV_NUMCLIENTS,
MCV_LASTWIN,
- MCV_VOTE,
- MCV_SELECTEDRANDOM
+ MCV_VOTE
} mapConditionVariable_t;
typedef enum
@@ -1268,6 +1280,7 @@ qboolean G_AdvanceMapRotation( void );
qboolean G_StartMapRotation( char *name, qboolean changeMap );
void G_StopMapRotation( void );
qboolean G_MapRotationActive( void );
+qboolean G_CurrentMapIsRotation( void );
void G_InitMapRotations( void );
qboolean G_MapExists( char *name );
int G_GetCurrentMap( int rotation );
@@ -1277,7 +1290,6 @@ qboolean G_IntermissionMapVoteWinner( void );
void G_IntermissionMapVoteMessage( gentity_t *ent );
void G_IntermissionMapVoteMessageAll( void );
void G_IntermissionMapVoteCommand( gentity_t *ent, qboolean next, qboolean choose );
-static qboolean G_GetRandomMap( char *name, int size, int rotation, int map );
//
// g_ptr.c
@@ -1296,6 +1308,11 @@ extern gentity_t g_entities[ MAX_GENTITIES ];
#define FOFS(x) ((int)&(((gentity_t *)0)->x))
+//spree values
+#define SPREE_FEED_VALUE 120
+#define SPREE_FEED_FADE 3
+#define SPREE_FEED_DELAY 50
+
extern vmCvar_t g_dedicated;
extern vmCvar_t g_cheats;
extern vmCvar_t g_maxclients; // allow this many total, including spectators
@@ -1307,11 +1324,16 @@ extern vmCvar_t g_minNameChangePeriod;
extern vmCvar_t g_maxNameChanges;
extern vmCvar_t g_newbieNumbering;
extern vmCvar_t g_newbieNamePrefix;
+extern vmCvar_t g_newbieNoBuild;
+extern vmCvar_t g_newbieNoBuildMessage;
extern vmCvar_t g_timelimit;
extern vmCvar_t g_suddenDeathTime;
extern vmCvar_t g_suddenDeath;
extern vmCvar_t g_suddenDeathMode;
+extern vmCvar_t g_vampireDeathTime;
+extern vmCvar_t g_vampireDeath;
+extern vmCvar_t g_vampireDeathInfo;
extern vmCvar_t g_friendlyFire;
extern vmCvar_t g_friendlyFireHumans;
extern vmCvar_t g_friendlyFireAliens;
@@ -1325,6 +1347,7 @@ extern vmCvar_t g_speed;
extern vmCvar_t g_knockback;
extern vmCvar_t g_quadfactor;
extern vmCvar_t g_inactivity;
+extern vmCvar_t g_inactivityMode;
extern vmCvar_t g_debugMove;
extern vmCvar_t g_debugAlloc;
extern vmCvar_t g_debugDamage;
@@ -1339,6 +1362,7 @@ extern vmCvar_t g_blood;
extern vmCvar_t g_allowVote;
extern vmCvar_t g_requireVoteReasons;
extern vmCvar_t g_voteLimit;
+extern vmCvar_t g_pollVotes;
extern vmCvar_t g_suddenDeathVotePercent;
extern vmCvar_t g_suddenDeathVoteDelay;
extern vmCvar_t g_extendVotesPercent;
@@ -1357,13 +1381,12 @@ extern vmCvar_t g_customVote8;
extern vmCvar_t g_customVotePercent;
extern vmCvar_t g_mapVotesPercent;
extern vmCvar_t g_mapRotationVote;
-extern vmCvar_t g_extendVotesPercent;
-extern vmCvar_t g_extendVotesTime;
-extern vmCvar_t g_extendVotesCount;
extern vmCvar_t g_readyPercent;
extern vmCvar_t g_designateVotes;
extern vmCvar_t g_teamAutoJoin;
extern vmCvar_t g_teamForceBalance;
+extern vmCvar_t g_popularMaps;
+extern vmCvar_t g_popularMapsVotePercent;
extern vmCvar_t g_banIPs;
extern vmCvar_t g_filterBan;
extern vmCvar_t g_smoothClients;
@@ -1396,6 +1419,9 @@ extern vmCvar_t g_unlagged;
extern vmCvar_t g_disabledEquipment;
extern vmCvar_t g_disabledClasses;
extern vmCvar_t g_disabledBuildables;
+extern vmCvar_t g_buildPointsRecoverRate;
+extern vmCvar_t g_dynamicBuildPoints;
+extern vmCvar_t g_instantBuild;
extern vmCvar_t g_markDeconstruct;
extern vmCvar_t g_markDeconstructMode;
@@ -1405,12 +1431,13 @@ extern vmCvar_t g_debugMapRotation;
extern vmCvar_t g_currentMapRotation;
extern vmCvar_t g_currentMap;
extern vmCvar_t g_nextMap;
+extern vmCvar_t g_idleMapSwitch;
extern vmCvar_t g_initialMapRotation;
extern vmCvar_t g_chatTeamPrefix;
+extern vmCvar_t g_sayAreaRange;
extern vmCvar_t g_actionPrefix;
extern vmCvar_t g_floodMaxDemerits;
extern vmCvar_t g_floodMinTime;
-extern vmCvar_t g_spamTime;
extern vmCvar_t g_shove;
@@ -1426,27 +1453,39 @@ extern vmCvar_t g_adminSayFilter;
extern vmCvar_t g_adminNameProtect;
extern vmCvar_t g_adminTempMute;
extern vmCvar_t g_adminTempBan;
+extern vmCvar_t g_adminBanRepeatKicks;
extern vmCvar_t g_adminMaxBan;
extern vmCvar_t g_adminTempSpec;
extern vmCvar_t g_adminMapLog;
+extern vmCvar_t g_adminRegisterLevel;
+extern vmCvar_t g_adminRegisterAdminPass;
+extern vmCvar_t g_adminRegisterAdminLevel;
extern vmCvar_t g_minLevelToJoinTeam;
-extern vmCvar_t g_minDeconLevel;
-extern vmCvar_t g_minDeconAffectsMark;
extern vmCvar_t g_forceAutoSelect;
extern vmCvar_t g_minLevelToSpecMM1;
extern vmCvar_t g_banNotice;
+extern vmCvar_t g_karma;
+extern vmCvar_t g_chat;
+extern vmCvar_t g_adminExpireTime;
extern vmCvar_t g_devmapKillerHP;
extern vmCvar_t g_killerHP;
+extern vmCvar_t g_maxGhosts;
+
extern vmCvar_t g_privateMessages;
-extern vmCvar_t g_fullIgnore;
extern vmCvar_t g_decolourLogfiles;
extern vmCvar_t g_publicSayadmins;
extern vmCvar_t g_myStats;
extern vmCvar_t g_teamStatus;
extern vmCvar_t g_antiSpawnBlock;
+extern vmCvar_t g_killingSpree;
+extern vmCvar_t g_feedingSpree;
+extern vmCvar_t g_bleedingSpree;
+extern vmCvar_t g_bleedingSpreeKick;
+extern vmCvar_t g_autoRevert;
+
extern vmCvar_t g_dretchPunt;
extern vmCvar_t g_devmapNoGod;
@@ -1458,6 +1497,11 @@ extern vmCvar_t g_slapDamage;
extern vmCvar_t g_voteMinTime;
extern vmCvar_t g_mapvoteMaxTime;
extern vmCvar_t g_votableMaps;
+extern vmCvar_t g_defeatVoteMinTime;
+
+extern vmCvar_t g_practiceText;
+extern vmCvar_t g_practiceCount;
+extern vmCvar_t g_freeCredits;
extern vmCvar_t g_msg;
extern vmCvar_t g_msgTime;
@@ -1466,23 +1510,27 @@ extern vmCvar_t g_welcomeMsgTime;
extern vmCvar_t g_buildLogMaxLength;
-extern vmCvar_t g_AllStats;
-extern vmCvar_t g_AllStatsTime;
+extern vmCvar_t g_turretAim;
+
+extern vmCvar_t g_modBuildableHealth; // Buildable health
+extern vmCvar_t g_modBuildableSpeed; // Buildable fire rate
+extern vmCvar_t g_modBuildableCount; // Number of OMs/RCs allowed
+extern vmCvar_t g_modHumanStamina; // Human stamina
+extern vmCvar_t g_modHumanHealth; // Human health
+extern vmCvar_t g_modAlienHealth; // Alien health
+extern vmCvar_t g_modAlienRegenRange; // Alien regen rate is based on distance from base
+extern vmCvar_t g_modHumanRate; // Human fire rate
+extern vmCvar_t g_modAlienRate; // Alien fire rate
+extern vmCvar_t g_modWeaponAmmo; // Weapon ammo per clip
+extern vmCvar_t g_modWeaponReload; // Weapon reload time
+extern vmCvar_t g_modTurretAngle; // Allow high turret build angles
+extern vmCvar_t g_modStage3Strength; // Alter stage 3 buildables
+extern vmCvar_t g_modMainStrength; // Damage/range modifier for Reactor and Overmind
extern vmCvar_t mod_jetpackFuel;
extern vmCvar_t mod_jetpackConsume;
extern vmCvar_t mod_jetpackRegen;
-extern vmCvar_t g_adminExpireTime;
-
-extern vmCvar_t g_autoGhost;
-
-extern vmCvar_t g_teamKillThreshold;
-
-extern vmCvar_t g_aimbotAdvertBan;
-extern vmCvar_t g_aimbotAdvertBanTime;
-extern vmCvar_t g_aimbotAdvertBanReason;
-
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 b946238..732fecd 100644
--- a/src/game/g_main.c
+++ b/src/game/g_main.c
@@ -23,8 +23,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "g_local.h"
-#define QVM_NAME "Slackers QVM" " (Lakitu7 5.5)"
-#define QVM_VERSIONNUM "1.1+"
+#define QVM_NAME "cQVM"
+#define MAJ_VERSION "5.5"
+
+#ifdef SVN_ID
+ #define QVM_VERSIONNUM MAJ_VERSION " svn" SVN_ID
+#else
+ #define QVM_VERSIONNUM MAJ_VERSION
+#endif
level_locals_t level;
@@ -47,6 +53,9 @@ vmCvar_t g_timelimit;
vmCvar_t g_suddenDeathTime;
vmCvar_t g_suddenDeath;
vmCvar_t g_suddenDeathMode;
+vmCvar_t g_vampireDeathTime;
+vmCvar_t g_vampireDeath;
+vmCvar_t g_vampireDeathInfo;
vmCvar_t g_capturelimit;
vmCvar_t g_friendlyFire;
vmCvar_t g_friendlyFireAliens;
@@ -65,6 +74,7 @@ vmCvar_t g_cheats;
vmCvar_t g_knockback;
vmCvar_t g_quadfactor;
vmCvar_t g_inactivity;
+vmCvar_t g_inactivityMode;
vmCvar_t g_debugMove;
vmCvar_t g_debugDamage;
vmCvar_t g_debugAlloc;
@@ -85,6 +95,7 @@ vmCvar_t g_podiumDrop;
vmCvar_t g_allowVote;
vmCvar_t g_requireVoteReasons;
vmCvar_t g_voteLimit;
+vmCvar_t g_pollVotes;
vmCvar_t g_suddenDeathVotePercent;
vmCvar_t g_suddenDeathVoteDelay;
vmCvar_t g_extendVotesPercent;
@@ -101,14 +112,13 @@ vmCvar_t g_customVote7;
vmCvar_t g_customVote8;
vmCvar_t g_customVotePercent;
vmCvar_t g_mapVotesPercent;
-vmCvar_t g_extendVotesPercent;
-vmCvar_t g_extendVotesTime;
-vmCvar_t g_extendVotesCount;
vmCvar_t g_mapRotationVote;
vmCvar_t g_readyPercent;
vmCvar_t g_designateVotes;
vmCvar_t g_teamAutoJoin;
vmCvar_t g_teamForceBalance;
+vmCvar_t g_popularMaps;
+vmCvar_t g_popularMapsVotePercent;
vmCvar_t g_banIPs;
vmCvar_t g_filterBan;
vmCvar_t g_smoothClients;
@@ -122,6 +132,8 @@ vmCvar_t g_minNameChangePeriod;
vmCvar_t g_maxNameChanges;
vmCvar_t g_newbieNumbering;
vmCvar_t g_newbieNamePrefix;
+vmCvar_t g_newbieNoBuild;
+vmCvar_t g_newbieNoBuildMessage;
vmCvar_t g_humanBuildPoints;
vmCvar_t g_alienBuildPoints;
@@ -142,6 +154,9 @@ vmCvar_t g_unlagged;
vmCvar_t g_disabledEquipment;
vmCvar_t g_disabledClasses;
vmCvar_t g_disabledBuildables;
+vmCvar_t g_buildPointsRecoverRate;
+vmCvar_t g_dynamicBuildPoints;
+vmCvar_t g_instantBuild;
vmCvar_t g_markDeconstruct;
vmCvar_t g_markDeconstructMode;
@@ -151,16 +166,17 @@ vmCvar_t g_debugMapRotation;
vmCvar_t g_currentMapRotation;
vmCvar_t g_currentMap;
vmCvar_t g_nextMap;
+vmCvar_t g_idleMapSwitch;
vmCvar_t g_initialMapRotation;
vmCvar_t g_shove;
vmCvar_t g_mapConfigs;
vmCvar_t g_chatTeamPrefix;
+vmCvar_t g_sayAreaRange;
vmCvar_t g_actionPrefix;
vmCvar_t g_floodMaxDemerits;
vmCvar_t g_floodMinTime;
-vmCvar_t g_spamTime;
vmCvar_t g_layouts;
vmCvar_t g_layoutAuto;
@@ -172,31 +188,42 @@ vmCvar_t g_adminSayFilter;
vmCvar_t g_adminNameProtect;
vmCvar_t g_adminTempMute;
vmCvar_t g_adminTempBan;
+vmCvar_t g_adminBanRepeatKicks;
vmCvar_t g_adminMaxBan;
vmCvar_t g_adminTempSpec;
vmCvar_t g_adminMapLog;
+vmCvar_t g_adminRegisterLevel;
+vmCvar_t g_adminRegisterAdminPass;
+vmCvar_t g_adminRegisterAdminLevel;
vmCvar_t g_minLevelToJoinTeam;
-vmCvar_t g_minDeconLevel;
-vmCvar_t g_minDeconAffectsMark;
vmCvar_t g_forceAutoSelect;
vmCvar_t g_privateMessages;
-vmCvar_t g_fullIgnore;
vmCvar_t g_decolourLogfiles;
vmCvar_t g_minLevelToSpecMM1;
vmCvar_t g_publicSayadmins;
vmCvar_t g_myStats;
-vmCvar_t g_AllStats;
-vmCvar_t g_AllStatsTime;
vmCvar_t g_teamStatus;
vmCvar_t g_antiSpawnBlock;
+
+vmCvar_t g_killingSpree;
+vmCvar_t g_feedingSpree;
+vmCvar_t g_bleedingSpree;
+vmCvar_t g_bleedingSpreeKick;
+vmCvar_t g_autoRevert;
+
vmCvar_t g_banNotice;
+vmCvar_t g_karma;
+vmCvar_t g_chat;
+vmCvar_t g_adminExpireTime;
vmCvar_t g_devmapKillerHP;
vmCvar_t g_killerHP;
vmCvar_t g_buildLogMaxLength;
+vmCvar_t g_maxGhosts;
+
vmCvar_t g_tag;
vmCvar_t g_dretchPunt;
@@ -213,27 +240,38 @@ vmCvar_t g_slapDamage;
vmCvar_t g_voteMinTime;
vmCvar_t g_mapvoteMaxTime;
vmCvar_t g_votableMaps;
+vmCvar_t g_defeatVoteMinTime;
+
+vmCvar_t g_practiceText;
+vmCvar_t g_practiceCount;
+vmCvar_t g_freeCredits;
vmCvar_t g_msg;
vmCvar_t g_msgTime;
vmCvar_t g_welcomeMsg;
vmCvar_t g_welcomeMsgTime;
+vmCvar_t g_turretAim;
+
+vmCvar_t g_modBuildableHealth;
+vmCvar_t g_modBuildableSpeed;
+vmCvar_t g_modBuildableCount;
+vmCvar_t g_modHumanStamina;
+vmCvar_t g_modHumanHealth;
+vmCvar_t g_modAlienHealth;
+vmCvar_t g_modAlienRegenRange;
+vmCvar_t g_modHumanRate;
+vmCvar_t g_modAlienRate;
+vmCvar_t g_modWeaponAmmo;
+vmCvar_t g_modWeaponReload;
+vmCvar_t g_modTurretAngle;
+vmCvar_t g_modStage3Strength;
+vmCvar_t g_modMainStrength;
vmCvar_t mod_jetpackFuel;
vmCvar_t mod_jetpackConsume;
vmCvar_t mod_jetpackRegen;
-vmCvar_t g_adminExpireTime;
-
-vmCvar_t g_autoGhost;
-
-vmCvar_t g_teamKillThreshold;
-
-vmCvar_t g_aimbotAdvertBan;
-vmCvar_t g_aimbotAdvertBanTime;
-vmCvar_t g_aimbotAdvertBanReason;
-
static cvarTable_t gameCvarTable[ ] =
{
// don't override the cheat state set by the system
@@ -260,6 +298,10 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_suddenDeathMode, "g_suddenDeathMode", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
{ &g_suddenDeath, "g_suddenDeath", "0", CVAR_SERVERINFO | CVAR_NORESTART, 0, qtrue },
+ { &g_vampireDeathTime, "g_vampireDeathTime", "0", CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
+ { &g_vampireDeath, "g_vampireDeath", "0", CVAR_NORESTART, 0, qtrue },
+ { &g_vampireDeathInfo, "g_vampireDeathInfo", "( !info vampire )", CVAR_ARCHIVE, 0, qtrue },
+
{ &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse },
{ &g_friendlyFire, "g_friendlyFire", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qtrue },
@@ -272,11 +314,14 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_devmapNoStructDmg, "g_devmapNoStructDmg", "0", CVAR_ARCHIVE, 0, qtrue },
{ &g_slapKnockback, "g_slapKnockback", "200", CVAR_ARCHIVE, 0, qfalse},
- { &g_slapDamage, "g_slapDamage", "0", CVAR_ARCHIVE, 0, qfalse},
+ { &g_slapDamage, "g_slapDamage", "5", CVAR_ARCHIVE, 0, qfalse},
{ &g_teamAutoJoin, "g_teamAutoJoin", "0", CVAR_ARCHIVE },
{ &g_teamForceBalance, "g_teamForceBalance", "1", CVAR_ARCHIVE },
+ { &g_popularMaps, "g_popularMaps", "arachnid2 atcs karith nexus6 niveus transit tremor", CVAR_ARCHIVE },
+ { &g_popularMapsVotePercent, "g_popularMapsVotePercent", "0", CVAR_ARCHIVE },
+
{ &g_warmup, "g_warmup", "10", CVAR_ARCHIVE, 0, qtrue },
{ &g_warmupMode, "g_warmupMode", "1", CVAR_ARCHIVE, 0, qtrue },
{ &g_doWarmup, "g_doWarmup", "1", CVAR_ARCHIVE, 0, qtrue },
@@ -289,8 +334,6 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_filterBan, "g_filterBan", "1", CVAR_ARCHIVE, 0, qfalse },
{ &g_needpass, "g_needpass", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse },
-
- { &g_autoGhost, "g_autoGhost", "1", CVAR_SERVERINFO, 0, qfalse },
{ &g_dedicated, "dedicated", "0", 0, 0, qfalse },
@@ -301,6 +344,7 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_weaponRespawn, "g_weaponrespawn", "5", 0, 0, qtrue },
{ &g_weaponTeamRespawn, "g_weaponTeamRespawn", "30", 0, 0, qtrue },
{ &g_inactivity, "g_inactivity", "0", 0, 0, qtrue },
+ { &g_inactivityMode, "g_inactivityMode", "0", 0, 0, qfalse },
{ &g_debugMove, "g_debugMove", "0", 0, 0, qfalse },
{ &g_debugDamage, "g_debugDamage", "0", 0, 0, qfalse },
{ &g_debugAlloc, "g_debugAlloc", "0", 0, 0, qfalse },
@@ -312,12 +356,19 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_allowVote, "g_allowVote", "1", CVAR_ARCHIVE, 0, qfalse },
{ &g_requireVoteReasons, "g_requireVoteReasons", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_pollVotes, "g_pollVotes", "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_votableMaps, "g_votableMaps", "", CVAR_ARCHIVE, 0, qtrue },
+ { &g_defeatVoteMinTime, "g_defeatVoteMinTime", "60", CVAR_ARCHIVE, 0, qfalse },
{ &g_suddenDeathVotePercent, "g_suddenDeathVotePercent", "74", CVAR_ARCHIVE, 0, qfalse },
{ &g_suddenDeathVoteDelay, "g_suddenDeathVoteDelay", "180", CVAR_ARCHIVE, 0, qfalse },
+ { &g_extendVotesPercent, "g_extendVotesPercent", "74", CVAR_ARCHIVE, 0, qfalse },
+ { &g_extendVotesTime, "g_extendVotesTime", "10", CVAR_ARCHIVE, 0, qfalse },
+ { &g_extendVotesCount, "g_extendVotesCount", "2", CVAR_ARCHIVE, 0, qfalse },
+ { &g_kickVotesPercent, "g_kickVotesPercent", "50", CVAR_ARCHIVE, 0, qtrue },
+
{ &g_customVote1, "g_customVote1", "", CVAR_ARCHIVE, 0, qfalse },
{ &g_customVote2, "g_customVote2", "", CVAR_ARCHIVE, 0, qfalse },
{ &g_customVote3, "g_customVote3", "", CVAR_ARCHIVE, 0, qfalse },
@@ -327,13 +378,15 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_customVote7, "g_customVote7", "", CVAR_ARCHIVE, 0, qfalse },
{ &g_customVote8, "g_customVote8", "", CVAR_ARCHIVE, 0, qfalse },
{ &g_customVotePercent, "g_customVotePercent", "50", CVAR_ARCHIVE, 0, qfalse },
+
{ &g_mapVotesPercent, "g_mapVotesPercent", "50", CVAR_ARCHIVE, 0, qfalse },
- { &g_extendVotesPercent, "g_extendVotesPercent", "74", CVAR_ARCHIVE, 0, qfalse },
- { &g_extendVotesTime, "g_extendVotesTime", "10", CVAR_ARCHIVE, 0, qfalse },
- { &g_extendVotesCount, "g_extendVotesCount", "2", CVAR_ARCHIVE, 0, qfalse },
{ &g_mapRotationVote, "g_mapRotationVote", "15", CVAR_ARCHIVE, 0, qfalse },
{ &g_readyPercent, "g_readyPercent", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_designateVotes, "g_designateVotes", "0", CVAR_ARCHIVE, 0, qfalse },
+
+ { &g_practiceText, "g_practiceText", "", 0, 0, qfalse},
+ { &g_practiceCount, "g_practiceCount", "0", 0, 0, qfalse},
+ { &g_freeCredits, "g_freeCredits", "0", CVAR_ARCHIVE, 0, qtrue },
{ &g_listEntity, "g_listEntity", "0", 0, 0, qfalse },
{ &g_minCommandPeriod, "g_minCommandPeriod", "500", 0, 0, qfalse},
@@ -341,6 +394,9 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_maxNameChanges, "g_maxNameChanges", "5", 0, 0, qfalse},
{ &g_newbieNumbering, "g_newbieNumbering", "0", CVAR_ARCHIVE, 0, qfalse},
{ &g_newbieNamePrefix, "g_newbieNamePrefix", "Newbie#", CVAR_ARCHIVE, 0, qfalse},
+ { &g_newbieNoBuild, "g_newbieNoBuild", "0", CVAR_ARCHIVE, 0, qfalse},
+ { &g_newbieNoBuildMessage, "g_newbieNoBuildMessage",
+ "Set a name by pressing Escape and choosing Options", CVAR_ARCHIVE, 0, qfalse},
{ &g_smoothClients, "g_smoothClients", "1", 0, 0, qfalse},
{ &g_clientUpgradeNotice, "g_clientUpgradeNotice", "1", 0, 0, qfalse},
@@ -362,17 +418,20 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_teamImbalanceWarnings, "g_teamImbalanceWarnings", "30", CVAR_ARCHIVE, 0, qfalse },
- { &g_unlagged, "g_unlagged", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue },
+ { &g_unlagged, "g_unlagged", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse },
{ &g_disabledEquipment, "g_disabledEquipment", "", CVAR_ROM, 0, qfalse },
{ &g_disabledClasses, "g_disabledClasses", "", CVAR_ROM, 0, qfalse },
{ &g_disabledBuildables, "g_disabledBuildables", "", CVAR_ROM, 0, qfalse },
+ { &g_buildPointsRecoverRate, "g_buildPointsRecoverRate", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_dynamicBuildPoints, "g_dynamicBuildPoints", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_instantBuild, "g_instantBuild", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_chatTeamPrefix, "g_chatTeamPrefix", "1", CVAR_ARCHIVE },
+ { &g_sayAreaRange, "g_sayAreaRange", "1000", CVAR_ARCHIVE, 0, qtrue },
{ &g_actionPrefix, "g_actionPrefix", "* ", CVAR_ARCHIVE, 0, qfalse },
{ &g_floodMaxDemerits, "g_floodMaxDemerits", "5000", CVAR_ARCHIVE, 0, qfalse },
{ &g_floodMinTime, "g_floodMinTime", "2000", CVAR_ARCHIVE, 0, qfalse },
- { &g_spamTime, "g_spamTime", "2", CVAR_ARCHIVE, 0, qfalse },
{ &g_markDeconstruct, "g_markDeconstruct", "0", CVAR_ARCHIVE, 0, qtrue },
{ &g_markDeconstructMode, "g_markDeconstructMode", "0", CVAR_ARCHIVE, 0, qfalse },
@@ -382,6 +441,7 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_currentMapRotation, "g_currentMapRotation", "-1", 0, 0, qfalse }, // -1 = NOT_ROTATING
{ &g_currentMap, "g_currentMap", "0", 0, 0, qfalse },
{ &g_nextMap, "g_nextMap", "", 0 , 0, qtrue },
+ { &g_idleMapSwitch, "g_idleMapSwitch", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_initialMapRotation, "g_initialMapRotation", "", CVAR_ARCHIVE, 0, qfalse },
{ &g_shove, "g_shove", "15", CVAR_ARCHIVE, 0, qfalse },
{ &g_mapConfigs, "g_mapConfigs", "", CVAR_ARCHIVE, 0, qfalse },
@@ -397,53 +457,74 @@ static cvarTable_t gameCvarTable[ ] =
{ &g_adminNameProtect, "g_adminNameProtect", "1", CVAR_ARCHIVE, 0, qfalse },
{ &g_adminTempMute, "g_adminTempMute", "5m", CVAR_ARCHIVE, 0, qfalse },
{ &g_adminTempBan, "g_adminTempBan", "2m", CVAR_ARCHIVE, 0, qfalse },
+ { &g_adminBanRepeatKicks, "g_adminBanRepeatKicks", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_adminMaxBan, "g_adminMaxBan", "2w", CVAR_ARCHIVE, 0, qfalse },
{ &g_adminTempSpec, "g_adminTempSpec", "2m", CVAR_ARCHIVE, 0, qfalse },
{ &g_adminMapLog, "g_adminMapLog", "", CVAR_ROM, 0, qfalse },
+ { &g_adminRegisterLevel, "g_adminRegisterLevel", "1", CVAR_ARCHIVE, 0, qfalse },
+ { &g_adminRegisterAdminPass, "g_adminRegisterAdminPass", "", CVAR_ARCHIVE, 0, qfalse },
+ { &g_adminRegisterAdminLevel, "g_adminRegisterAdminLevel", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_minLevelToJoinTeam, "g_minLevelToJoinTeam", "0", CVAR_ARCHIVE, 0, qfalse },
- { &g_minDeconLevel, "g_minDeconLevel", "0", CVAR_ARCHIVE, 0, qfalse},
- { &g_minDeconAffectsMark, "g_minDeconAffectsMark", "0", CVAR_ARCHIVE, 0, qfalse},
{ &g_forceAutoSelect, "g_forceAutoSelect", "0", CVAR_ARCHIVE, 0, qtrue },
- { &g_adminExpireTime, "g_adminExpireTime", "0", CVAR_ARCHIVE, 0, qfalse },
+
+ { &g_maxGhosts, "g_maxGhosts", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_privateMessages, "g_privateMessages", "1", CVAR_ARCHIVE, 0, qfalse },
- { &g_fullIgnore, "g_fullIgnore", "1", CVAR_ARCHIVE, 0, qtrue },
{ &g_decolourLogfiles, "g_decolourLogfiles", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_buildLogMaxLength, "g_buildLogMaxLength", "50", CVAR_ARCHIVE, 0, qfalse },
{ &g_myStats, "g_myStats", "1", CVAR_ARCHIVE, 0, qtrue },
- { &g_AllStats, "g_AllStats", "0", CVAR_ARCHIVE, 0, qtrue },
- { &g_AllStatsTime, "g_AllStatsTime", "60", CVAR_ARCHIVE, 0, qfalse },
- { &g_teamStatus, "g_teamStatus", "0", CVAR_ARCHIVE, 0, qtrue },
+ { &g_teamStatus, "g_teamStatus", "0", 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_karma, "g_karma", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_chat, "g_chat", "chat.dat", CVAR_ARCHIVE, 0, qfalse },
+ { &g_adminExpireTime, "g_adminExpireTime", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_devmapKillerHP, "g_devmapKillerHP", "0", CVAR_ARCHIVE, 0, qtrue },
- { &g_killerHP, "g_killerHP", "0", CVAR_ARCHIVE, 0, qtrue },
+ { &g_killerHP, "g_killerHP", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_tag, "g_tag", "main", CVAR_INIT, 0, qfalse },
{ &g_dretchPunt, "g_dretchPunt", "1", CVAR_ARCHIVE, 0, qfalse },
+
+ { &g_killingSpree, "g_killingSpree", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_feedingSpree, "g_feedingSpree", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_bleedingSpree, "g_bleedingSpree", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_bleedingSpreeKick, "g_bleedingSpreeKick", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_autoRevert, "g_autoRevert", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_msg, "g_msg", "", CVAR_ARCHIVE, 0, qfalse },
{ &g_msgTime, "g_msgTime", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_welcomeMsg, "g_welcomeMsg", "", CVAR_ARCHIVE, 0, qfalse },
{ &g_welcomeMsgTime, "g_welcomeMsgTime", "0", CVAR_ARCHIVE, 0, qfalse },
+
+ { &g_turretAim, "g_turretAim", "0", CVAR_ARCHIVE, 0, qfalse },
+
+ { &g_modBuildableHealth, "g_modBuildableHealth", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modBuildableSpeed, "g_modBuildableSpeed", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modBuildableCount, "g_modBuildableCount", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modHumanStamina, "g_modHumanStamina", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modHumanHealth, "g_modHumanHealth", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modAlienHealth, "g_modAlienHealth", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modAlienRegenRange, "g_modAlienRegenRange", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modHumanRate, "g_modHumanRate", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modAlienRate, "g_modAlienRate", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modWeaponAmmo, "g_modWeaponAmmo", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modWeaponReload, "g_modWeaponReload", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modTurretAngle, "g_modTurretAngle", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modStage3Strength, "g_modStage3Strength", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &g_modMainStrength, "g_modMainStrength", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_rankings, "g_rankings", "0", 0, 0, qfalse },
{ &g_allowShare, "g_allowShare", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse},
{ &g_creditOverflow, "g_creditOverflow", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse},
{ &g_banNotice, "g_banNotice", "", CVAR_ARCHIVE, 0, qfalse },
- { &mod_jetpackFuel, "mod_jetpackFuel", "0", CVAR_ARCHIVE, 0, qtrue },
+ { &mod_jetpackFuel, "mod_jetpackFuel", "0", CVAR_ARCHIVE, 0, qfalse },
{ &mod_jetpackConsume, "mod_jetpackConsume", "2", CVAR_ARCHIVE, 0, qfalse },
- { &mod_jetpackRegen, "mod_jetpackRegen", "3", CVAR_ARCHIVE, 0, qfalse },
-
- { &g_teamKillThreshold, "g_teamKillThreshold", "0", CVAR_ARCHIVE, 0, qfalse },
+ { &mod_jetpackRegen, "mod_jetpackRegen", "3", CVAR_ARCHIVE, 0, qfalse }
- { &g_aimbotAdvertBan, "g_aimbotAdvertBan", "0", CVAR_ARCHIVE, 0, qfalse },
- { &g_aimbotAdvertBanTime, "g_aimbotAdvertBanTime", "0", CVAR_ARCHIVE, 0, qfalse },
- { &g_aimbotAdvertBanReason, "g_aimbotAdvertBanReason", "AUTOBAN: AIMBOT", CVAR_ARCHIVE, 0, qfalse }
};
static int gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[ 0 ] );
@@ -676,6 +757,23 @@ void G_UpdateCvars( void )
G_RemapTeamShaders( );
}
+static void G_InitModCvars( void )
+{
+ BG_MOD_set( MOD_BG_BUILDABLE_HEALTH, g_modBuildableHealth.integer );
+ BG_MOD_set( MOD_BG_BUILDABLE_SPEED, g_modBuildableSpeed.integer );
+ BG_MOD_set( MOD_BG_HUMAN_STAMINA, g_modHumanStamina.integer );
+ BG_MOD_set( MOD_BG_HUMAN_HEALTH, g_modHumanHealth.integer );
+ BG_MOD_set( MOD_BG_ALIEN_HEALTH, g_modAlienHealth.integer );
+ BG_MOD_set( MOD_BG_HUMAN_RATE, g_modHumanRate.integer );
+ BG_MOD_set( MOD_BG_ALIEN_RATE, g_modAlienRate.integer );
+ BG_MOD_set( MOD_BG_WEAPON_AMMO, g_modWeaponAmmo.integer );
+ BG_MOD_set( MOD_BG_WEAPON_RELOAD, g_modWeaponReload.integer );
+ BG_MOD_set( MOD_BG_TURRET_ANGLE, g_modTurretAngle.integer );
+ BG_MOD_set( MOD_BG_STAGE3_STRENGTH, g_modStage3Strength.integer );
+
+ BG_MOD_update( );
+}
+
/*
=================
G_MapConfigs
@@ -813,13 +911,29 @@ void G_InitGame( int levelTime, int randomSeed, int restart )
// load up a custom building layout if there is one
G_LayoutLoad( );
-
- // load any nobuild markers that have been saved
- G_NobuildLoad( );
+
+ // load up nobuild zones
+ nobuild_init( );
// the map might disable some things
BG_InitAllowedGameElements( );
+ // practice counter
+ if( g_practiceCount.integer > 0 )
+ {
+ trap_Cvar_Set( "g_practiceCount", va( "%d", g_practiceCount.integer - 1 ) );
+ }
+
+ // free credits expiration
+ if( g_freeCredits.integer > 1 )
+ {
+ trap_Cvar_Set( "g_freeCredits",
+ va( "%d", ( g_freeCredits.integer > 2 ) ? g_freeCredits.integer - 1 : 0 ) );
+ }
+
+ // syncronize mod cvars
+ G_InitModCvars( );
+
// general initialization
G_FindTeams( );
@@ -841,6 +955,7 @@ void G_InitGame( int levelTime, int randomSeed, int restart )
trap_Cvar_Set( "g_humanKills", 0 );
trap_Cvar_Set( "g_suddenDeath", 0 );
level.suddenDeathBeginTime = g_suddenDeathTime.integer * 60000;
+ trap_Cvar_Set( "g_vampireDeath", 0 );
G_Printf( "-----------------------------------\n" );
@@ -891,6 +1006,8 @@ void G_ShutdownGame( int restart )
G_Printf( "==== ShutdownGame ====\n" );
+ G_admin_chat_writeconfig();
+
if( level.logFile )
{
G_LogPrintf( "ShutdownGame:\n" );
@@ -898,15 +1015,13 @@ void G_ShutdownGame( int restart )
trap_FS_FCloseFile( level.logFile );
}
- // write admin.dat for !seen data
- admin_writeconfig();
-
// write all the client session data so we can get it back
G_WriteSessionData( );
G_admin_cleanup( );
G_admin_namelog_cleanup( );
G_admin_adminlog_cleanup( );
+ G_admin_tklog_cleanup( );
level.restarted = qfalse;
level.surrenderTeam = PTE_NONE;
@@ -1281,6 +1396,15 @@ int G_TimeTilSuddenDeath( void )
return ( ( level.suddenDeathBeginTime ) - ( level.time - level.startTime ) );
}
+int G_TimeTilVampireDeath( void )
+{
+ if( !g_vampireDeathTime.integer )
+ return 3600000; // Always some time away
+
+ return ( g_vampireDeathTime.integer * 60000 ) -
+ ( level.time - level.startTime );
+}
+
#define PLAYER_COUNT_MOD 5.0f
@@ -1366,6 +1490,43 @@ void G_CalculateBuildPoints( void )
}
}
+ //vampire death
+ if( !g_vampireDeath.integer && level.vampireDeath )
+ {
+ level.vampireDeath = 0;
+ level.vampireDeathWarning = 0;
+ }
+ if( !level.vampireDeath )
+ {
+ if( g_vampireDeath.integer || G_TimeTilVampireDeath( ) <= 0 ) //Conditions to enter VD
+ {
+ if( level.vampireDeathWarning < TW_PASSED )
+ {
+ trap_SendServerCommand( -1, "cp \"^1Vampire Sudden Death!\"" );
+ level.vampireDeath = 1;
+ level.vampireDeathBeginTime = level.time;
+ level.vampireDeathWarning = TW_PASSED;
+ g_vampireDeath.integer = 1;
+ }
+ }
+ else if( G_TimeTilVampireDeath( ) <= 60000 && level.vampireDeathWarning < TW_IMMINENT )
+ {
+ trap_SendServerCommand( -1,
+ va( "cp \"^1Vampire\n^7Sudden Death in 60 seconds!%s%s\"",
+ ( g_vampireDeathInfo.string[ 0 ] ) ? "\n\n" : "",
+ g_vampireDeathInfo.string ) );
+ level.vampireDeathWarning = TW_IMMINENT;
+ }
+ else if( G_TimeTilVampireDeath( ) <= 300000 && level.vampireDeathWarning < TW_SOON )
+ {
+ trap_SendServerCommand( -1,
+ va ("cp \"^1Vampire\n^7Sudden Death in 5 minutes!%s%s\"",
+ ( g_vampireDeathInfo.string[ 0 ] ) ? "\n\n" : "",
+ g_vampireDeathInfo.string ) );
+ level.vampireDeathWarning = TW_SOON;
+ }
+ }
+
//set BP at each cycle
if( g_suddenDeath.integer )
{
@@ -1378,8 +1539,34 @@ void G_CalculateBuildPoints( void )
localATP = g_alienBuildPoints.integer;
}
- level.humanBuildPoints = level.humanBuildPointsPowered = localHTP;
- level.alienBuildPoints = localATP;
+ // Build Point Recovery
+ // each gained stage adds 50% delay
+ if( g_buildPointsRecoverRate.integer )
+ {
+ while( level.humanRecoverBuildPoints > 0 &&
+ level.humanRecoverTime < level.time )
+ {
+ level.humanRecoverBuildPoints--;
+ level.humanRecoverTime +=
+ ( 60000 + g_humanStage.integer * 30000 ) / g_buildPointsRecoverRate.integer;
+ }
+ while( level.alienRecoverBuildPoints > 0 &&
+ level.alienRecoverTime < level.time )
+ {
+ level.alienRecoverBuildPoints--;
+ level.alienRecoverTime +=
+ ( 60000 + g_alienStage.integer * 30000 ) / g_buildPointsRecoverRate.integer;
+ }
+ }
+ else
+ {
+ // recover may have been turned off, reset
+ level.humanRecoverBuildPoints = 0;
+ level.alienRecoverBuildPoints = 0;
+ }
+
+ level.humanBuildPoints = level.humanBuildPointsPowered = localHTP - level.humanRecoverBuildPoints;
+ level.alienBuildPoints = localATP - level.alienRecoverBuildPoints;
level.reactorPresent = qfalse;
level.overmindPresent = qfalse;
@@ -1392,6 +1579,9 @@ void G_CalculateBuildPoints( void )
if( ent->s.eType != ET_BUILDABLE )
continue;
+ if( ent->s.eFlags & EF_DEAD )
+ continue;
+
buildable = ent->s.modelindex;
if( buildable != BA_NONE )
@@ -1612,6 +1802,7 @@ void CalculateRanks( void )
level.numHumanClients = 0;
level.numLiveAlienClients = 0;
level.numLiveHumanClients = 0;
+ level.bleeders = 0;
for( i = 0; i < level.maxclients; i++ )
{
@@ -1644,6 +1835,9 @@ void CalculateRanks( void )
if( level.clients[ i ].sess.sessionTeam != TEAM_SPECTATOR )
level.numLiveHumanClients++;
}
+
+ if( level.clients[ i ].pers.bleeder )
+ level.bleeders++;
}
}
}
@@ -1940,7 +2134,7 @@ void QDECL G_AdminsPrintf( const char *fmt, ... )
for( j = 0; j < level.maxclients; j++ )
{
tempent = &g_entities[ j ];
- if( G_admin_permission( tempent, ADMF_ADMINCHAT ) &&
+ if( G_admin_permission( tempent, ADMF_ADMINCHAT) &&
!tempent->client->pers.ignoreAdminWarnings )
{
trap_SendServerCommand(tempent-g_entities,va( "print \"^6[Admins]^7 %s\"", string) );
@@ -1950,37 +2144,7 @@ void QDECL G_AdminsPrintf( const char *fmt, ... )
G_LogPrintf("%s",string);
}
-/*
-=================
-G_WarningsPrintf
-
-Print to everyone with a certain flag, and the logfile with a time stamp if it is open, and to the console
-(just a copy of the G_AdminsPrintf with flag suport)
-=================
-*/
-void QDECL G_WarningsPrintf( char *flag, const char *fmt, ... )
-{
- va_list argptr;
- char string[ 1024 ];
- gentity_t *tempent;
- int j;
-
- va_start( argptr, fmt );
- vsprintf( string, fmt,argptr );
- va_end( argptr );
- for( j = 0; j < level.maxclients; j++ )
- {
- tempent = &g_entities[ j ];
- if( G_admin_permission( tempent, flag ) )
- {
- trap_SendServerCommand(tempent-g_entities,va( "print \"^6[Warnings]^7 %s\"", string) );
- }
- }
-
- G_LogPrintf("%s",string);
-
-}
/*
=================
G_LogPrintf
@@ -2148,7 +2312,7 @@ void G_SendGameStat( pTeam_t team )
int ping;
cl = &level.clients[ level.sortedClients[ i ] ];
-
+
// Ignore invisible players
if ( cl->sess.invisible == qtrue )
continue;
@@ -2474,6 +2638,8 @@ void CheckVote( void )
{
int votePassThreshold=level.votePassThreshold;
int voteYesPercent;
+ int minVotes = 0;
+ qboolean pass = qfalse;
if( level.voteExecuteTime && level.voteExecuteTime < level.time )
{
@@ -2487,7 +2653,10 @@ void CheckVote( void )
{
G_admin_maplog_result( "m" );
}
-
+ else if( !Q_stricmpn( level.voteString, "!restart", 8 ) )
+ {
+ G_admin_maplog_result( "l" );
+ }
if( !Q_stricmp( level.voteString, "suddendeath" ) )
{
@@ -2512,6 +2681,13 @@ void CheckVote( void )
if( !level.voteTime )
return;
+ if( !Q_stricmpn( level.voteString, "ban", 3 ) )
+ {
+ minVotes = 3;
+ if( level.numConnectedClients < minVotes )
+ minVotes = level.numConnectedClients;
+ }
+
if( level.voteYes + level.voteNo > 0 )
voteYesPercent = (int)( 100 * ( level.voteYes ) / ( level.voteYes + level.voteNo ) );
else
@@ -2520,20 +2696,22 @@ void CheckVote( void )
if( ( level.time - level.voteTime >= VOTE_TIME ) ||
( level.voteYes + level.voteNo == level.numConnectedClients ) )
{
- if( voteYesPercent> votePassThreshold || level.voteNo == 0 )
+ if( level.voteYes + level.voteNo < minVotes )
+ {
+ // not enough voters
+ trap_SendServerCommand( -1, va( "print \"This vote type requires at least %d voters\n\"", minVotes ) );
+ pass = qfalse;
+ }
+ else if( voteYesPercent > votePassThreshold )
{
// execute the command, then remove the vote
- trap_SendServerCommand( -1, va("print \"Vote passed (%d - %d)\n\"",
- level.voteYes, level.voteNo ) );
- G_LogPrintf( "Vote: Vote passed (%d-%d)\n", level.voteYes, level.voteNo );
level.voteExecuteTime = level.time + 3000;
+ pass = qtrue;
}
else
{
// same behavior as a timeout
- trap_SendServerCommand( -1, va("print \"Vote failed (%d - %d)\n\"",
- level.voteYes, level.voteNo ) );
- G_LogPrintf( "Vote: Vote failed (%d - %d)\n", level.voteYes, level.voteNo );
+ pass = qfalse;
}
}
else
@@ -2542,18 +2720,14 @@ void CheckVote( void )
( (double) votePassThreshold/100.0 ) ) )
{
// execute the command, then remove the vote
- trap_SendServerCommand( -1, va("print \"Vote passed (%d - %d)\n\"",
- level.voteYes, level.voteNo ) );
- G_LogPrintf( "Vote: Vote passed (%d - %d)\n", level.voteYes, level.voteNo );
level.voteExecuteTime = level.time + 3000;
+ pass = qtrue;
}
else if( level.voteNo > (int)( (double) level.numConnectedClients *
( (double) ( 100.0-votePassThreshold )/ 100.0 ) ) )
{
// same behavior as a timeout
- trap_SendServerCommand( -1, va("print \"Vote failed (%d - %d)\n\"",
- level.voteYes, level.voteNo ) );
- G_LogPrintf("Vote failed\n");
+ pass = qfalse;
}
else
{
@@ -2562,6 +2736,22 @@ void CheckVote( void )
}
}
+ trap_SendServerCommand( -1,
+ va( "print \"Vote %s^7 (^2Y:%d^7-^1N:%d^7, %d percent) (%s)\n\"",
+ ( pass ) ? "^2passed" : "^1failed",
+ level.voteYes, level.voteNo, voteYesPercent,
+ level.voteDisplayString ) );
+
+ G_LogPrintf( "Vote: Vote %s (%d - %d)\n",
+ ( pass ) ? "passed" : "failed",
+ level.voteYes, level.voteNo );
+
+ G_admin_adminlog_log( NULL, "vote",
+ va( "%s^7 (^2Y:%d^7-^1N:%d^7, %d percent)",
+ ( pass ) ? "^2passed" : "^1failed",
+ level.voteYes, level.voteNo, voteYesPercent ),
+ 0, pass );
+
level.voteTime = 0;
trap_SetConfigstring( CS_VOTE_TIME, "" );
trap_SetConfigstring( CS_VOTE_STRING, "" );
@@ -2576,6 +2766,9 @@ CheckTeamVote
void CheckTeamVote( int team )
{
int cs_offset;
+ int votePassThreshold;
+ int voteYesPercent = 0;
+ qboolean pass = qfalse;
if ( team == PTE_HUMANS )
cs_offset = 0;
@@ -2584,38 +2777,44 @@ void CheckTeamVote( int team )
else
return;
+ votePassThreshold = level.teamVotePassThreshold[ cs_offset ];
+
if( !level.teamVoteTime[ cs_offset ] )
return;
+ if( ( level.teamVoteYes[ cs_offset ] + level.teamVoteNo[ cs_offset ] ) )
+ {
+ voteYesPercent = (100 * level.teamVoteYes[ cs_offset ]) /
+ (level.teamVoteYes[ cs_offset ] + level.teamVoteNo[ cs_offset ]);
+ }
+
if( level.time - level.teamVoteTime[ cs_offset ] >= VOTE_TIME )
{
- if( level.teamVoteYes[ cs_offset ] > level.teamVoteNo[ cs_offset ] && level.teamVoteYes[ cs_offset ] >= 2 )
+ if( voteYesPercent > votePassThreshold && level.teamVoteYes[ cs_offset ] >= 2 )
{
// execute the command, then remove the vote
- trap_SendServerCommand( -1, va("print \"Team vote passed (%d - %d)\n\"", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ) );
trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.teamVoteString[ cs_offset ] ) );
+ pass = qtrue;
}
else
{
- trap_SendServerCommand( -1, va("print \"Team vote failed (%d - %d)\n\"", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ) );
- G_LogPrintf( "Teamvote: Team vote failed (%d - %d)\n", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] );
+ pass = qfalse;
}
}
else
{
- if( level.teamVoteYes[ cs_offset ] > level.numteamVotingClients[ cs_offset ] / 2 )
+ if( level.teamVoteYes[ cs_offset ] > (int)((double)level.numteamVotingClients[ cs_offset ] *
+ (double)votePassThreshold/100.0) )
{
// execute the command, then remove the vote
- trap_SendServerCommand( -1, va("print \"Team vote passed (%d - %d)\n\"", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ) );
- G_LogPrintf( "Teamvote: Team vote passed (%d - %d)\n", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] );
- //
trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.teamVoteString[ cs_offset ] ) );
+ pass = qtrue;
}
- else if( level.teamVoteNo[ cs_offset ] >= level.numteamVotingClients[ cs_offset ] / 2 )
+ else if( level.teamVoteNo[ cs_offset ] > (int)((double)level.numteamVotingClients[ cs_offset ] *
+ ((double)(100.0-votePassThreshold)/100.0)) )
{
// same behavior as a timeout
- trap_SendServerCommand( -1, va("print \"Team vote failed (%d - %d)\n\"", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] ) );
- G_LogPrintf( "Teamvote: Team vote failed (%d - %d)\n", level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] );
+ pass = qfalse;
}
else
{
@@ -2624,11 +2823,70 @@ void CheckTeamVote( int team )
}
}
+ trap_SendServerCommand( -1,
+ va( "print \"Team vote %s^7 (^2Y:%d^7-^1N:%d^7, %d percent)\n\"",
+ ( pass ) ? "^2passed" : "^1failed",
+ level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ], voteYesPercent ) );
+
+ G_LogPrintf( "Teamvote: Team vote %s (%d - %d)\n",
+ ( pass ) ? "passed" : "failed",
+ level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ] );
+
+ G_admin_adminlog_log( NULL, "teamvote",
+ va( "%s^7 (^2Y:%d^7-^1N:%d^7, %d percent)",
+ ( pass ) ? "^2passed" : "^1failed",
+ level.teamVoteYes[ cs_offset ], level.teamVoteNo[ cs_offset ], voteYesPercent ),
+ 0, pass );
+
level.teamVoteTime[ cs_offset ] = 0;
trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, "" );
trap_SetConfigstring( CS_TEAMVOTE_STRING + cs_offset, "" );
}
+
+/*
+==================
+CheckIdleMap
+==================
+*/
+void CheckIdleMap( void )
+{
+ static int nextCheck = 0;
+ static int idleCount = 0;
+ static qboolean triggered = qfalse;
+
+ if( !g_idleMapSwitch.integer )
+ return;
+
+ if( triggered ||
+ nextCheck > level.time )
+ return;
+
+ nextCheck = level.time + 60000;
+ if( !level.numPlayingClients )
+ idleCount++;
+ else
+ idleCount = 0;
+
+ if( idleCount > g_idleMapSwitch.integer &&
+ !G_CurrentMapIsRotation() )
+ {
+ int newTL;
+
+ triggered = qtrue;
+
+ newTL = ( level.time - level.startTime ) / 60000;
+ newTL += 5;
+ if( !g_timelimit.integer || g_timelimit.integer > newTL )
+ {
+ trap_Cvar_Set( "timelimit", va( "%d", newTL ) );
+ trap_SendServerCommand( -1, va( "print \"Timelimit reduced due to idle server\n\"" ) );
+ G_LogPrintf( "Server is idle, timelimit reduced to %d\n", newTL );
+ }
+ }
+}
+
+
/*
==================
CheckMsgTimer
@@ -2723,6 +2981,60 @@ void CheckCountdown( void )
/*
==================
+G_CalculateDynamicBuildPoints
+==================
+*/
+static void G_CalculateDynamicBuildPoints( void )
+{
+ static int LastTime = 0;
+ int count = 0;
+ gentity_t *ent;
+ int bps;
+ int i;
+
+ if( !g_dynamicBuildPoints.integer )
+ return;
+
+ if( level.time - LastTime < 10000 )
+ return;
+
+ if( level.time - level.startTime < 30000 ||
+ !level.defaultAlienBuildPoints )
+ {
+ // remember default build points, after some time for exec commands
+ if( level.time - level.startTime > 1000 &&
+ level.defaultAlienBuildPoints == 0 )
+ {
+ level.defaultAlienBuildPoints = g_alienBuildPoints.integer;
+ level.defaultHumanBuildPoints = g_humanBuildPoints.integer;
+ }
+ return;
+ }
+
+ LastTime = level.time;
+
+ for( i = 0; i < level.maxclients; i++ )
+ {
+ ent = &g_entities[ i ];
+ if( ent && ent->client &&
+ ent->client->pers.connected == CON_CONNECTED &&
+ (ent->client->pers.teamSelection == PTE_ALIENS || ent->client->pers.teamSelection == PTE_HUMANS) )
+ {
+ count++;
+ }
+ }
+
+ bps = level.defaultAlienBuildPoints + count * g_dynamicBuildPoints.integer;
+ if( g_alienBuildPoints.integer < bps )
+ trap_Cvar_Set( "g_alienBuildPoints", va( "%d", bps ) );
+
+ bps = level.defaultHumanBuildPoints + count * g_dynamicBuildPoints.integer;
+ if( g_humanBuildPoints.integer < bps )
+ trap_Cvar_Set( "g_humanBuildPoints", va( "%d", bps ) );
+}
+
+/*
+==================
CheckCvars
==================
*/
@@ -2773,6 +3085,179 @@ void CheckCvars( void )
}
/*
+==================
+G_CheckVampireDeathBuildables
+==================
+*/
+static int G_VampireDeathKillBuildable( buildable_t buildable, int count )
+{
+ int i;
+ gentity_t *ent;
+ int n = 0;
+
+ for ( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ )
+ {
+ if( ent->s.eType != ET_BUILDABLE )
+ continue;
+
+ if( ent->s.modelindex == buildable && ent->health > 0 )
+ {
+ G_Damage( ent, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE );
+ n++;
+
+ if( count && n >= count )
+ return n;
+ }
+ }
+
+ return n;
+}
+
+static void G_CheckVampireDeathBuildables( void )
+{
+ static int LastAmmoTime = 0;
+ static int LastSuckTime = 0;
+ static int LastDestructTime = 0;
+ int i;
+ gentity_t *ent;
+ qboolean done = qtrue;
+
+ if( !level.vampireDeath )
+ return;
+
+ if( level.intermissionQueued ||
+ level.intermissiontime )
+ return;
+
+ // humans get more ammo every 20 seconds
+ if( level.time - LastAmmoTime >= 20000 )
+ {
+ LastAmmoTime = level.time;
+
+ for( i = 0; i < level.maxclients; i++ )
+ {
+ ent = g_entities + i;
+ if( !ent->inuse || ent->health <= 0 ||
+ ent->client->pers.connected != CON_CONNECTED)
+ continue;
+
+ if( ent->client->pers.teamSelection == PTE_HUMANS )
+ {
+ int ammo, clips, maxClips, maxAmmo;
+
+ BG_FindAmmoForWeapon( ent->client->ps.weapon, &maxAmmo, &maxClips );
+ BG_UnpackAmmoArray( ent->client->ps.weapon, ent->client->ps.ammo,
+ ent->client->ps.powerups, &ammo, &clips );
+
+ if( maxClips )
+ {
+ if( clips < maxClips )
+ clips++;
+ }
+ else
+ {
+ ammo += maxAmmo / 5;
+ if( ammo > maxAmmo )
+ ammo = maxAmmo;
+ }
+
+ BG_PackAmmoArray( ent->client->ps.weapon, ent->client->ps.ammo,
+ ent->client->ps.powerups, ammo, clips );
+ }
+ }
+ }
+
+ // health countdown
+ if( level.time - LastSuckTime >= 3000 )
+ {
+ int value;
+ int hp_rate;
+ int damage;
+
+ LastSuckTime = level.time;
+
+ // increase health removal each minute after 1
+ hp_rate = 1 + ( level.time - level.vampireDeathBeginTime ) / 60000;
+ if( hp_rate < 1 ) hp_rate = 1;
+ if( hp_rate > 10) hp_rate = 10;
+
+ for( i = 0; i < level.maxclients; i++ )
+ {
+ ent = g_entities + i;
+ if( !ent->inuse ||
+ ent->health <= 0 ||
+ !ent->client ||
+ ent->client->pers.connected != CON_CONNECTED ||
+ ent->client->sess.sessionTeam == TEAM_SPECTATOR ||
+ ( ent->client->pers.teamSelection != PTE_ALIENS && ent->client->pers.teamSelection != PTE_HUMANS ) )
+ continue;
+
+ value = BG_FindHealthForClass( ent->client->pers.classSelection );
+ if( value < 1 )
+ value = 1;
+ // death from full HP in 60s with damage every 3s, 1000 / 60 * 3 = 50
+ ent->client->pers.vampireSuckFraction += value * 50 * hp_rate;
+
+ damage = ent->client->pers.vampireSuckFraction / 1000;
+ ent->client->pers.vampireSuckFraction -= damage * 1000;
+
+ if ( ent->health > damage )
+ ent->health -= damage;
+ else
+ G_Damage( ent, NULL, NULL, NULL, NULL, damage,
+ DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_PROTECTION|DAMAGE_NO_LOCDAMAGE, MOD_SUICIDE );
+ }
+ }
+
+ // base self destruct
+ if( level.vampireDeath == 2 )
+ return;
+
+ if( level.time - LastDestructTime < 1000 )
+ return;
+
+ LastDestructTime = level.time;
+
+ if( level.numLiveAlienClients == 0 &&
+ level.numLiveHumanClients == 0 )
+ {
+ level.lastWin = PTE_NONE;
+ trap_SendServerCommand( -1, "print \"Timelimit hit\n\"" );
+ trap_SetConfigstring( CS_WINNER, "Stalemate" );
+ LogExit( "Timelimit hit." );
+ G_admin_maplog_result( "t" );
+ }
+
+ if( G_VampireDeathKillBuildable( BA_A_ACIDTUBE, 1 ) ||
+ G_VampireDeathKillBuildable( BA_A_HIVE, 1 ) ||
+ G_VampireDeathKillBuildable( BA_A_TRAPPER, 1 ) ||
+ G_VampireDeathKillBuildable( BA_A_BARRICADE, 1 ) ||
+ G_VampireDeathKillBuildable( BA_A_BOOSTER, 1 ) ||
+ G_VampireDeathKillBuildable( BA_A_SPAWN, 1 ) ||
+ G_VampireDeathKillBuildable( BA_A_OVERMIND, 1 ) )
+ {
+ done = qfalse;
+ }
+
+ if( G_VampireDeathKillBuildable( BA_H_MGTURRET, 1 ) ||
+ G_VampireDeathKillBuildable( BA_H_TESLAGEN, 1 ) ||
+ G_VampireDeathKillBuildable( BA_H_DCC, 1 ) ||
+ G_VampireDeathKillBuildable( BA_H_REPEATER, 1 ) ||
+ G_VampireDeathKillBuildable( BA_H_MEDISTAT, 1 ) ||
+ G_VampireDeathKillBuildable( BA_H_ARMOURY, 1 ) ||
+ G_VampireDeathKillBuildable( BA_H_SPAWN, 1 ) ||
+ G_VampireDeathKillBuildable( BA_H_REACTOR, 1 ) )
+ {
+ done = qfalse;
+ }
+
+ if( done )
+ {
+ level.vampireDeath = 2;
+ }
+}
+
+/*
=============
G_RunThink
@@ -2960,6 +3445,8 @@ void G_RunFrame( int levelTime )
ClientEndFrame( ent );
}
+ G_CheckVampireDeathBuildables();
+
// save position information for all active clients
G_UnlaggedStore( );
@@ -2974,8 +3461,11 @@ void G_RunFrame( int levelTime )
G_CalculateAvgPlayers( );
G_UpdateZaps( msec );
+ G_CalculateDynamicBuildPoints( );
+
// see if it is time to end the level
CheckExitRules( );
+ CheckIdleMap( );
// update to team status?
CheckTeamStatus( );
diff --git a/src/game/g_maprotation.c b/src/game/g_maprotation.c
index 60fd696..eb76d70 100644
--- a/src/game/g_maprotation.c
+++ b/src/game/g_maprotation.c
@@ -334,68 +334,6 @@ static qboolean G_ParseMapRotation( mapRotation_t *mr, char **text_p )
continue;
}
- else if( !Q_stricmp( token, "*RANDOM*" ) )
- {
- if( mr->numMaps == MAX_MAP_ROTATION_MAPS )
- {
- G_Printf( S_COLOR_RED "ERROR: maximum number of maps in one rotation (%d) reached\n",
- MAX_MAP_ROTATION_MAPS );
- return qfalse;
- }
- mre = &mr->maps[ mr->numMaps ];
- Q_strncpyz( mre->name, token, sizeof( mre->name ) );
-
- token = COM_Parse( text_p );
-
- if( !Q_stricmp( token, "{" ) )
- {
- while( 1 )
- {
- token = COM_Parse( text_p );
-
- if( !token )
- break;
-
- if( !Q_stricmp( token, "}" ) )
- {
- break;
- }
- else
- {
- if( mre->numConditions < MAX_MAP_ROTATION_CONDS )
- {
- mrc = &mre->conditions[ mre->numConditions ];
- mrc->lhs = MCV_SELECTEDRANDOM;
- mrc->unconditional = qfalse;
- Q_strncpyz( mrc->dest, token, sizeof( mrc->dest ) );
-
- mre->numConditions++;
- }
- else
- {
- G_Printf( S_COLOR_YELLOW "WARNING: maximum number of maps for one Random Slot (%d) reached\n",
- MAX_MAP_ROTATION_CONDS );
- }
- }
- }
- if( !mre->numConditions )
- {
- G_Printf( S_COLOR_YELLOW "WARNING: no maps in *RANDOM* section\n" );
- }
- else
- {
- mr->numMaps++;
- mnSet = qtrue;
- }
- }
- else
- {
- G_Printf( S_COLOR_RED "ERROR: *RANDOM* with no section\n" );
- return qfalse;
- }
-
- continue;
- }
else if( !Q_stricmp( token, "}" ) )
return qtrue; //reached the end of this map rotation
@@ -526,7 +464,6 @@ static qboolean G_ParseMapRotationFile( const char *fileName )
for( j = 0; j < mapRotations.rotations[ i ].numMaps; j++ )
{
if( Q_stricmp( mapRotations.rotations[ i ].maps[ j ].name, "*VOTE*") != 0 &&
- Q_stricmp( mapRotations.rotations[ i ].maps[ j ].name, "*RANDOM*") != 0 &&
!G_MapExists( mapRotations.rotations[ i ].maps[ j ].name ) )
{
G_Printf( S_COLOR_RED "ERROR: map \"%s\" doesn't exist\n",
@@ -696,21 +633,6 @@ static void G_IssueMapChange( int rotation )
return;
}
}
- else if(!Q_stricmp( newmap, "*RANDOM*") )
- {
- fileHandle_t f;
-
- G_GetRandomMap( newmap, sizeof( newmap ), rotation, map );
- if( trap_FS_FOpenFile( va("maps/%s.bsp", newmap), &f, FS_READ ) > 0 )
- {
- trap_FS_FCloseFile( f );
- }
- else
- {
- G_AdvanceMapRotation();
- return;
- }
- }
// allow a manually defined g_layouts setting to override the maprotation
if( !g_layouts.string[ 0 ] &&
@@ -720,8 +642,7 @@ static void G_IssueMapChange( int rotation )
mapRotations.rotations[ rotation ].maps[ map ].layouts );
}
- trap_SendConsoleCommand( EXEC_APPEND, va( "map %s\n",
- newmap ) );
+ trap_SendConsoleCommand( EXEC_APPEND, va( "map %s\n", newmap ) );
// load up map defaults if g_mapConfigs is set
G_MapConfigs( newmap );
@@ -808,9 +729,6 @@ static qboolean G_EvaluateMapCondition( mapRotationCondition_t *mrc )
case MCV_VOTE:
// ignore vote for conditions;
break;
- case MCV_SELECTEDRANDOM:
- // ignore vote for conditions;
- break;
default:
case MCV_ERR:
@@ -940,6 +858,51 @@ qboolean G_MapRotationActive( void )
/*
===============
+G_CurrentMapIsRotation
+
+Test if the current map is from the rotation
+===============
+*/
+
+qboolean G_CurrentMapIsRotation( void )
+{
+ char mapname[ 64 ];
+ int map, rotation;
+ int i;
+
+ // Check for an active map rotation,
+ // only return false if rotation is running and current map is not from it
+ if ( !G_MapRotationActive() || g_currentMapRotation.integer == NOT_ROTATING )
+ return qtrue;
+
+ rotation = g_currentMapRotation.integer;
+ if( !( rotation >= 0 && rotation < mapRotations.numRotations ) )
+ return qtrue;
+
+ map = G_GetCurrentMap( rotation );
+ if( !(map >= 0 && map < mapRotations.rotations[ rotation ].numMaps ) )
+ return qtrue;
+
+ trap_Cvar_VariableStringBuffer( "mapname", mapname, sizeof( mapname ) );
+
+ if( !Q_stricmp( mapRotations.rotations[ rotation ].maps[ map ].name, mapname ) )
+ return qtrue;
+
+ if( !Q_stricmp( mapRotations.rotations[ rotation ].maps[ map ].name, "*VOTE*" ) )
+ {
+ for( i = 0; i < mapRotations.rotations[ rotation ].maps[ map ].numConditions; i++ )
+ {
+ if( mapRotations.rotations[ rotation ].maps[ map ].conditions[ i ].lhs == MCV_VOTE &&
+ !Q_stricmp( mapRotations.rotations[ rotation ].maps[ map ].conditions[ i ].dest, mapname ) )
+ return qtrue;
+ }
+ }
+
+ return qfalse;
+}
+
+/*
+===============
G_InitMapRotations
Load and intialise the map rotations
@@ -1173,9 +1136,6 @@ static void G_IntermissionMapVoteMessageReal( gentity_t *ent, int winner, int wi
"^7+------------------+\n", sizeof( string ) );
for( i = 0; i < rotationVoteLen; i++ )
{
- if( !G_MapExists( rotationVoteList[ i ] ) )
- continue;
-
if( i == selection )
color = "^5";
else if( i == index )
@@ -1275,42 +1235,3 @@ void G_IntermissionMapVoteCommand( gentity_t *ent, qboolean next, qboolean choos
G_IntermissionMapVoteMessage( ent );
}
-static qboolean G_GetRandomMap( char *name, int size, int rotation, int map )
-{
- mapRotation_t *mr;
- mapRotationEntry_t *mre;
- mapRotationCondition_t *mrc;
- int i, nummaps;
- int randompick = 0;
- int maplist[ 32 ];
-
- mr = &mapRotations.rotations[ rotation ];
- mre = &mr->maps[ map ];
-
- nummaps = 0;
- //count the number of map votes
- for( i = 0; i < mre->numConditions; i++ )
- {
- mrc = &mre->conditions[ i ];
-
- if( mrc->lhs == MCV_SELECTEDRANDOM )
- {
- //map doesnt exist
- if( !G_MapExists( mrc->dest ) ) {
- continue;
- }
- maplist[ nummaps ] = i;
- nummaps++;
- }
- }
-
- if( nummaps == 0 ) {
- return qfalse;
- }
-
- randompick = (int)( random() * nummaps );
-
- Q_strncpyz( name, mre->conditions[ maplist[ randompick ] ].dest, size );
-
- return qtrue;
-}
diff --git a/src/game/g_mem.c b/src/game/g_mem.c
index 6935194..ea8e438 100644
--- a/src/game/g_mem.c
+++ b/src/game/g_mem.c
@@ -23,7 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "g_local.h"
-#define POOLSIZE ( 1024 * 1024 )
+#define POOLSIZE ( 1024 * 1024 * 4 )
#define FREEMEMCOOKIE ((int)0xDEADBE3F) // Any unlikely to be used value
#define ROUNDBITS 31 // Round to 32 bytes
diff --git a/src/game/g_missile.c b/src/game/g_missile.c
index 6317636..71b5432 100644
--- a/src/game/g_missile.c
+++ b/src/game/g_missile.c
@@ -571,6 +571,12 @@ void AHive_ReturnToHive( gentity_t *self )
self->think = G_ExplodeMissile;
self->nextthink = level.time + 15000;
}
+
+ if( g_modStage3Strength.integer > 0 )
+ {
+ int nt = self->nextthink - level.time;
+ self->nextthink = level.time + nt * 100 / g_modStage3Strength.integer;
+ }
}
/*
@@ -584,6 +590,7 @@ void AHive_SearchAndDestroy( gentity_t *self )
{
vec3_t dir;
trace_t tr;
+ int speed;
trap_Trace( &tr, self->r.currentOrigin, self->r.mins, self->r.maxs,
self->target_ent->r.currentOrigin, self->r.ownerNum, self->clipmask );
@@ -603,13 +610,26 @@ void AHive_SearchAndDestroy( gentity_t *self )
VectorSubtract( self->target_ent->r.currentOrigin, self->r.currentOrigin, dir );
VectorNormalize( dir );
+ if( g_modStage3Strength.integer > 0 )
+ speed = HIVE_SPEED * g_modStage3Strength.integer / 100;
+ else
+ speed = HIVE_SPEED;
+
//change direction towards the player
- VectorScale( dir, HIVE_SPEED, self->s.pos.trDelta );
+ VectorScale( dir, speed, self->s.pos.trDelta );
SnapVector( self->s.pos.trDelta ); // save net bandwidth
VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
self->s.pos.trTime = level.time;
self->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD;
+
+ if( g_modBuildableSpeed.integer > 0)
+ self->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD * 100 / g_modBuildableSpeed.integer;
+ if( g_modStage3Strength.integer > 0)
+ {
+ int nt = self->nextthink - level.time;
+ self->nextthink = level.time + nt * 100 / g_modStage3Strength.integer;
+ }
}
}
@@ -642,6 +662,12 @@ gentity_t *fire_hive( gentity_t *self, vec3_t start, vec3_t dir )
bolt->clipmask = MASK_SHOT;
bolt->target_ent = self->target_ent;
+ if( g_modBuildableSpeed.integer > 0)
+ {
+ bolt->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD * 100 / g_modBuildableSpeed.integer;
+ bolt->damage = bolt->damage * g_modBuildableSpeed.integer / 100;
+ }
+
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
@@ -805,6 +831,9 @@ gentity_t *fire_bounceBall( gentity_t *self, vec3_t start, vec3_t dir )
VectorCopy( start, bolt->r.currentOrigin );
/*bolt->s.eFlags |= EF_BOUNCE;*/
+ if( g_modAlienRate.integer > 0 )
+ bolt->damage = bolt->damage * g_modAlienRate.integer / 100;
+
return bolt;
}
diff --git a/src/game/g_session.c b/src/game/g_session.c
index ef78e8a..e1705ea 100644
--- a/src/game/g_session.c
+++ b/src/game/g_session.c
@@ -78,10 +78,10 @@ void G_ReadSessionData( gclient_t *client )
// bk001205 - format
int teamLeader;
+ int invisible;
int spectatorState;
int sessionTeam;
int restartTeam;
- int invisible;
var = va( "session%i", client - level.clients );
trap_Cvar_VariableStringBuffer( var, s, sizeof(s) );
diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c
index a9e04e6..4da6205 100644
--- a/src/game/g_weapon.c
+++ b/src/game/g_weapon.c
@@ -701,7 +701,8 @@ void teslaFire( gentity_t *ent )
if( !traceEnt->client )
return;
- if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS )
+ if( traceEnt->client && traceEnt->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS &&
+ !traceEnt->client->pers.bleeder )
return;
//so the client side knows
@@ -709,8 +710,13 @@ void teslaFire( gentity_t *ent )
if( traceEnt->takedamage )
{
+ int dmg = TESLAGEN_DMG;
+
+ if( g_modStage3Strength.integer > 0 )
+ dmg = dmg * g_modStage3Strength.integer / 100;
+
G_Damage( traceEnt, ent, ent, forward, tr.endpos,
- TESLAGEN_DMG, 0, MOD_TESLAGEN );
+ dmg, 0, MOD_TESLAGEN );
}
// snap the endpos to integers to save net bandwidth, but nudged towards the line
@@ -781,6 +787,7 @@ void cancelBuildFire( gentity_t *ent )
traceEnt->health += HBUILD_HEALRATE;
ent->client->pers.statscounters.repairspoisons++;
+ ent->client->pers.karma += 1;
level.humanStatsCounters.repairspoisons++;
if( traceEnt->health > bHealth )
@@ -814,7 +821,7 @@ void buildFire( gentity_t *ent, dynMenu_t menu )
if( G_BuildIfValid( ent, ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) )
{
- if( g_cheats.integer )
+ if( g_cheats.integer || g_instantBuild.integer )
{
ent->client->ps.stats[ STAT_MISC ] = 0;
}
@@ -1184,6 +1191,8 @@ static void G_CreateNewZap( gentity_t *creator, gentity_t *target )
zap->used = qtrue;
zap->timeToLive = LEVEL2_AREAZAP_TIME;
+ if( g_modAlienRate.integer > 0 )
+ zap->timeToLive = zap->timeToLive * g_modAlienRate.integer / 100;
zap->damageUsed = 0;
zap->creator = creator;
@@ -1277,6 +1286,8 @@ void G_UpdateZaps( int msec )
{
G_Damage( target, source, zap->creator, forward, target->s.origin,
damage, DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP );
+ if( g_modAlienRate.integer > 0 )
+ damage = damage * 100 / g_modAlienRate.integer;
zap->damageUsed += damage;
}
}
diff --git a/src/game/tremulous.h b/src/game/tremulous.h
index 0d2d1c1..c007489 100644
--- a/src/game/tremulous.h
+++ b/src/game/tremulous.h
@@ -620,7 +620,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//do to increment the stage kill counters
// g_suddenDeathMode settings
-#define SDMODE_BP 0
+#define SDMODE_BP 0
#define SDMODE_NO_BUILD 1
#define SDMODE_SELECTIVE 2
-#define SDMODE_NO_DECON 3