From 5ad9e26c3be1f2ebc6cdb340b685aef30ae16db7 Mon Sep 17 00:00:00 2001
From: Paweł Redman <pawel@redman.xyz>
Date: Thu, 13 Apr 2017 11:30:00 +0000
Subject: import the cQVM game module

replacing the existing one
---
 src/game/bg_lib.c        |    8 +-
 src/game/bg_misc.c       |  134 ++
 src/game/bg_pmove.c      |   10 +-
 src/game/bg_public.h     |   21 +
 src/game/g_active.c      |  201 ++-
 src/game/g_admin.c       | 4055 ++++++++++++++++++++++++++++------------------
 src/game/g_admin.h       |   60 +-
 src/game/g_buildable.c   | 1038 +++++++++---
 src/game/g_client.c      |  144 +-
 src/game/g_cmds.c        | 1040 +++++++-----
 src/game/g_combat.c      |  238 ++-
 src/game/g_local.h       |  156 +-
 src/game/g_main.c        |  708 ++++++--
 src/game/g_maprotation.c |  171 +-
 src/game/g_mem.c         |    2 +-
 src/game/g_missile.c     |   31 +-
 src/game/g_session.c     |    2 +-
 src/game/g_weapon.c      |   17 +-
 src/game/tremulous.h     |    3 +-
 19 files changed, 5532 insertions(+), 2507 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,772 +2376,542 @@ 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 )
+int G_admin_parse_time( const char *time )
 {
-  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 seconds = 0, num = 0;
   int i;
-
-  if( !flag[ 0 ] )
-  {
-    return "invalid admin flag";
-  }
-
-  allflag[ 0 ] = '\0';
-  token_p = oldflags;
-  while( *( token = COM_Parse( &token_p ) ) )
+  for( i = 0; time[ i ]; i++ )
   {
-    key = token;
-    if( *key == '-' || *key == '+' )
-      key++;
-
-    if( !strcmp( key, flag ) )
+    if( isdigit( time[ i ] ) )
     {
-      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 );
+      num = num * 10 + time[ i ] - '0';
+      continue;
     }
-    else
+    if( i == 0 || !isdigit( time[ i - 1 ] ) )
+      return -1;
+    switch( time[ i ] )
     {
-      Q_strncpyz( newflag, token, sizeof( newflag ) );
+      case 'w': num *= 7;
+      case 'd': num *= 24;
+      case 'h': num *= 60;
+      case 'm': num *= 60;
+      case 's': break;
+      default:  return -1;
     }
+    seconds += num;
+    num = 0;
+  }
+  if( num )
+    seconds += num;
+  // overflow
+  if( seconds < 0 )
+    seconds = 0;
+  return seconds;
+}
 
-    if( !strcmp( key, ADMF_ALLFLAGS ) )
+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 )
     {
-      if( !allflag[ 0 ] )
-        Q_strncpyz( allflag, newflag, sizeof( allflag ) );
       continue;
     }
 
-    if( !allflag[ 0 ] )
+    if( i != ban &&
+      strstr( g_admin_bans[ ban ]->ip, g_admin_bans[ i ]->ip ) == g_admin_bans[ ban ]->ip )
     {
-      if( head_count < MAX_USER_FLAGS )
+      immune = qfalse;
+
+      for( j = 0; j < MAX_ADMIN_ADMINS && g_admin_admins[ j ]; j++ )
       {
-        Q_strncpyz( head_flags[ head_count ], newflag,
-                    sizeof( head_flags[ head_count ] ) );
-        head_count++;
+        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" : "" );
     }
-    else
+  }
+}
+
+static qboolean admin_create_ban( gentity_t *ent,
+  char *netname,
+  char *guid,
+  char *ip,
+  int seconds,
+  char *reason ) 
+{
+  g_admin_ban_t *b = NULL;
+  qtime_t qt;
+  int t;
+  int i;
+
+  t = trap_RealTime( &qt );
+  b = G_Alloc( sizeof( g_admin_ban_t ) );
+
+  if( !b )
+    return qfalse;
+
+  Q_strncpyz( b->name, netname, sizeof( b->name ) );
+  Q_strncpyz( b->guid, guid, sizeof( b->guid ) );
+  Q_strncpyz( b->ip, ip, sizeof( b->ip ) );
+  b->suspend = 0;
+
+  //strftime( b->made, sizeof( b->made ), "%m/%d/%y %H:%M:%S", lt );
+  Q_strncpyz( b->made, va( "%02i/%02i/%02i %02i:%02i:%02i",
+    (qt.tm_mon + 1), qt.tm_mday, (qt.tm_year - 100),
+    qt.tm_hour, qt.tm_min, qt.tm_sec ),
+    sizeof( b->made ) );
+
+  Q_strncpyz( b->banner, G_admin_get_adminname( ent ), sizeof( b->banner ) );
+
+  if( ent )
+    b->bannerlevel = G_admin_level( ent );
+  else
+    b->bannerlevel = 0;
+
+  if( !seconds )
+    b->expires = 0;
+  else
+    b->expires = t + seconds;
+  if( !*reason )
+    Q_strncpyz( b->reason, "banned by admin", sizeof( b->reason ) );
+  else
+    Q_strncpyz( b->reason, reason, sizeof( b->reason ) );
+  for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ )
+    ;
+  if( i == MAX_ADMIN_BANS )
+  {
+    ADMP( "^3!ban: ^7too many bans\n" );
+    G_Free( b );
+    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( tail_count < MAX_USER_FLAGS )
+      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) ))
       {
-        Q_strncpyz( tail_flags[ tail_count ], newflag,
-                    sizeof( tail_flags[ tail_count ] ) );
-        tail_count++;
+        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( !flagset && !clear )
+  if( !admin_create_ban( ent, netname, guid, ip, seconds, reason ) )
+    return qfalse;
+
+  if( g_adminBanRepeatKicks.integer && seconds > 0 && isKick )
   {
-    if( !strcmp( flag, ADMF_ALLFLAGS ) )
-    {
-      Com_sprintf( allflag, sizeof( allflag ), "%s%s",
-        ( add ) ? "" : "-", ADMF_ALLFLAGS );
-    }
-    else if( !allflag[ 0 ] )
+    int newestBan = -1;
+    int length;
+
+    length = g_adminBanRepeatKicks.integer * 60 * 60;
+    if( admin_create_ban( ent, netname, guid, ip, length, reason ) )
     {
-      if( head_count < MAX_USER_FLAGS )
+      for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ )
       {
-        Com_sprintf( head_flags[ head_count ], sizeof( head_flags[ head_count ] ),
-          "%s%s", ( add ) ? "" : "-", flag );
-        head_count++;
+        newestBan = i;
       }
-    }
-    else
-    {
-      if( tail_count < MAX_USER_FLAGS )
+      if( newestBan >= 0 &&
+          !Q_stricmp( ip, g_admin_bans[ newestBan ]->ip ) )
       {
-        Com_sprintf( tail_flags[ tail_count ], sizeof( tail_flags[ tail_count ] ),
-          "%s%s", ( add ) ? "+" : "-", flag );
-        tail_count++;
+        g_admin_bans[ newestBan ]->suspend = t + length;
+        Q_strncpyz( g_admin_bans[ newestBan ]->banner, "^3auto-banner", sizeof( g_admin_bans[ newestBan ]->banner ) );
       }
     }
   }
 
-  qsort( head_flags, head_count, sizeof( head_flags[ 0 ] ), SortFlags );
-  qsort( tail_flags, tail_count, sizeof( tail_flags[ 0 ] ), SortFlags );
+  return qtrue;
+}
 
-  // rebuild flags
-  newflags[ 0 ] = '\0';
-  for( i = 0; i < head_count; i++ )
+qboolean G_admin_kick( gentity_t *ent, int skiparg )
+{
+  int pids[ MAX_CLIENTS ];
+  char name[ MAX_NAME_LENGTH ], *reason, err[ MAX_STRING_CHARS ];
+  int minargc;
+  gentity_t *vic;
+  char notice[51];
+  
+  trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) );
+
+  minargc = 3 + skiparg;
+  if( G_admin_permission( ent, ADMF_UNACCOUNTABLE ) )
+    minargc = 2 + skiparg;
+
+  if( G_SayArgc() < minargc )
   {
-    Q_strcat( newflags, size,
-              va( "%s%s", ( i ) ? " ": "", head_flags[ i ] ) );
+    ADMP( "^3!kick: ^7usage: !kick [name] [reason]\n" );
+    return qfalse;
   }
-  if( allflag[ 0 ] )
+  G_SayArgv( 1 + skiparg, name, sizeof( name ) );
+  reason = G_SayConcatArgs( 2 + skiparg );
+  if( G_ClientNumbersFromString( name, pids ) != 1 )
   {
-    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 ] ) );
-    }
+    G_MatchOnePlayer( pids, err, sizeof( err ) );
+    ADMP( va( "^3!kick: ^7%s\n", err ) );
+    return qfalse;
   }
-
-  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++ )
+  if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) )
   {
-    ADMBP( va( "  %s%-20s ^7%s\n",
-      ( adminFlagList[ i ].flag[ 0 ] != '.' ) ? "^5" : "^1",
-      adminFlagList[ i ].flag,
-      adminFlagList[ i ].description ) );
+    ADMP( "^3!kick: ^7sorry, but your intended victim has a higher admin"
+        " level than you\n" );
+    return qfalse;
   }
+  vic = &g_entities[ pids[ 0 ] ];
+  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 ),
+    ( *reason ) ? reason : "kicked by admin" );
+  if( g_admin.string[ 0 ] )
+    admin_writeconfig();
 
-  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++;
-  }
+  vic->client->pers.karma -= 5000;
 
-  ADMBP( va( "^3!flaglist: ^7listed %d abilities and %d command flags\n",
-    adminNumFlags, count ) );
+ 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 ) );
 
-  ADMBP_end();
+  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",
+    ( *reason ) ? reason : "kicked by admin" ) );
 
   return qtrue;
 }
 
-qboolean G_admin_flag( gentity_t *ent, int skiparg )
+qboolean G_admin_ban( 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 )
+  int seconds;
+  char search[ MAX_NAME_LENGTH ];
+  char secs[ 7 ];
+  char *reason;
+  int minargc;
+  char duration[ 32 ];
+  int logmatch = -1, logmatches = 0;
+  int i, j;
+  qboolean exactmatch = qfalse;
+  char n2[ MAX_NAME_LENGTH ];
+  char s2[ MAX_NAME_LENGTH ];
+  char guid_stub[ 9 ];
+  char notice[51];
+  gentity_t *vic;
+  
+  trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) );
+  
+  if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) &&
+       G_admin_permission( ent, ADMF_UNACCOUNTABLE ) )
   {
-    ADMP( va( "^3!%s: ^7usage: !%s slot# flag\n", cmd, cmd ) );
+    minargc = 2 + skiparg;
+  }
+  else if( ( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) || g_adminMaxBan.integer ) ||
+            G_admin_permission( ent, ADMF_UNACCOUNTABLE ) )
+  {
+    minargc = 3 + skiparg;
+  }
+  else
+  {
+    minargc = 4 + skiparg;
+  }
+  if( G_SayArgc() < minargc )
+  {
+    ADMP( "^3!ban: ^7usage: !ban [name|slot|ip] [time] [reason]\n" );
     return qfalse;
   }
+  G_SayArgv( 1 + skiparg, search, sizeof( search ) );
+  G_SanitiseString( search, s2, sizeof( s2 ) );
+  G_SayArgv( 2 + skiparg, secs, sizeof( secs ) );
 
-  G_SayArgv( 1 + skiparg, name, sizeof( name ) );
-  if( name[ 0 ] == '*' )
+  seconds = G_admin_parse_time( secs );
+  if( seconds <= 0 )
   {
-    if( ent )
+    if( g_adminMaxBan.integer && !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) )
     {
-      ADMP( va( "^3!%s: only console can change admin level flags\n", cmd ) );
-      return qfalse;
+      ADMP( va( "^3!ban: ^7using your admin level's maximum ban length of %s\n",
+        g_adminMaxBan.string ) );
+      seconds = G_admin_parse_time( g_adminMaxBan.string );
     }
-    id = atoi( name + 1 );
-    for( i = 0; i < MAX_ADMIN_LEVELS && g_admin_levels[ i ]; i++ )
+    else if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) )
     {
-      if( g_admin_levels[ i ]->level == id )
-      {
-        admin_level = i;
-        break;
-      }
+      seconds = 0;
     }
-    if( admin_level < 0 )
+    else
     {
-      ADMP( va( "^3!%s: admin level %d does not exist\n", cmd, id ) );
+      ADMP( "^3!ban: ^7ban time must be positive\n" );
       return qfalse;
     }
-    Com_sprintf( adminname, sizeof( adminname ), "admin level %d", id );
+    reason = G_SayConcatArgs( 2 + skiparg );
   }
   else
   {
-    id = G_admin_find_admin_slot( ent, name, cmd, adminname, sizeof( adminname ) );
-    if( id < 0 )
-      return qfalse;
+    reason = G_SayConcatArgs( 3 + skiparg );
 
-    if( ent && !admin_higher_guid( ent->client->pers.guid, g_admin_admins[ id ]->guid ) )
+    if( g_adminMaxBan.integer &&
+        seconds > G_admin_parse_time( g_adminMaxBan.string ) &&
+        !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) )
     {
-      ADMP( va( "^3%s:^7 your intended victim has a higher admin level than you\n", cmd ) );
-      return qfalse;
+      seconds = G_admin_parse_time( g_adminMaxBan.string );
+      ADMP( va( "^3!ban: ^7ban length limited to %s for your admin level\n",
+        g_adminMaxBan.string ) );
     }
   }
 
-  if( G_SayArgc() < 3 + skiparg )
+  for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ )
   {
-    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
+    // skip players in the namelog who have already been banned
+    if( g_admin_namelog[ i ]->banned )
+      continue;
+
+    // skip disconnected players when banning on slot number
+    if( g_admin_namelog[ i ]->slot == -1 )
+      continue;
+
+    if( !Q_stricmp( va( "%d", g_admin_namelog[ i ]->slot ), s2 ) )
     {
-      flag = g_admin_levels[ admin_level ]->flags;
-      level = g_admin_levels[ admin_level ]->level;
+      logmatches = 1;
+      logmatch = i;
+      exactmatch = qtrue;
+      break;
     }
-    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 ] == '+' )
+  for( i = 0;
+       !exactmatch && i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ];
+       i++ )
   {
-    add = ( flag[ 0 ] == '+' );
-    flag++;
+    // skip players in the namelog who have already been banned
+    if( g_admin_namelog[ i ]->banned )
+      continue;
+
+    if( !Q_stricmp( g_admin_namelog[ i ]->ip, s2 ) )
+    {
+      logmatches = 1;
+      logmatch = i;
+      exactmatch = qtrue;
+      break;
+    }
+    for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES
+      && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ )
+    {
+      G_SanitiseString(g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) );
+      if( strstr( n2, s2 ) )
+      {
+        if( logmatch != i )
+          logmatches++;
+        logmatch = i; 
+      }
+    }
   }
-  if( ent && !Q_stricmp( ent->client->pers.guid, g_admin_admins[ id ]->guid ) )
+  
+  if( !logmatches ) 
   {
-    ADMP( va( "^3%s:^7 you may not change your own flags (use rcon)\n", cmd ) );
+    ADMP( "^3!ban: ^7no player found by that name, IP, or slot number\n" );
     return qfalse;
-  }
-  if( flag[ 0 ] != '.' && !G_admin_permission( ent, flag ) )
+  } 
+  else if( logmatches > 1 )
   {
-    ADMP( va( "^3%s:^7 you can only change flags that you also have\n", cmd ) );
+    ADMBP_begin();
+    ADMBP( "^3!ban: ^7multiple recent clients match name, use IP or slot#:\n" );
+    for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ )
+    {
+      for( j = 0; j < 8; j++ )
+        guid_stub[ j ] = g_admin_namelog[ i ]->guid[ j + 24 ];
+      guid_stub[ j ] = '\0';
+      for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES
+        && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ )
+      {
+        G_SanitiseString(g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) );
+        if( strstr( n2, s2 ) )
+        {
+          if( g_admin_namelog[ i ]->slot > -1 )
+            ADMBP( "^3" );
+          ADMBP( va( "%-2s (*%s) %15s ^7'%s^7'\n",
+           (g_admin_namelog[ i ]->slot > -1) ?
+             va( "%d", g_admin_namelog[ i ]->slot ) : "-",
+           guid_stub,
+           g_admin_namelog[ i ]->ip,
+           g_admin_namelog[ i ]->name[ j ] ) );
+        }
+      }
+    }
+    ADMBP_end();
     return qfalse;
   }
+  
+  G_admin_duration( ( seconds ) ? seconds : -1,
+    duration, sizeof( duration ) );
 
-  if( !Q_stricmp( cmd, "unflag" ) )
+  if( ent && !admin_higher_guid( ent->client->pers.guid,
+    g_admin_namelog[ logmatch ]->guid ) )
   {
-    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 ) );
+    ADMP( "^3!ban: ^7sorry, but your intended victim has a higher admin"
+      " level than you\n" );
     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" );
-  }
+  admin_create_ban_check_repeats( ent,
+    g_admin_namelog[ logmatch ]->name[ 0 ],
+    g_admin_namelog[ logmatch ]->guid,
+    g_admin_namelog[ logmatch ]->ip,
+    seconds, reason ); 
+
+  g_admin_namelog[ logmatch ]->banned = qtrue;
 
   if( !g_admin.string[ 0 ] )
-    ADMP( va( "^3!%s: ^7WARNING g_admin not set, not saving admin record "
-      "to a file\n", cmd ) );
+    ADMP( "^3!ban: ^7WARNING g_admin not set, not saving ban to a file\n" );
   else
     admin_writeconfig();
 
-  return qtrue;
-}
+  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" );
 
-int G_admin_parse_time( const char *time )
-{
-  int seconds = 0, num = 0;
-  int i;
-  for( i = 0; time[ i ]; i++ )
+  if( g_admin_namelog[ logmatch ]->slot == -1 ) 
   {
-    if( isdigit( time[ i ] ) )
-    {
-      num = num * 10 + time[ i ] - '0';
-      continue;
-    }
-    if( i == 0 || !isdigit( time[ i - 1 ] ) )
-      return -1;
-    switch( time[ i ] )
-    {
-      case 'w': num *= 7;
-      case 'd': num *= 24;
-      case 'h': num *= 60;
-      case 'm': num *= 60;
-      case 's': break;
-      default:  return -1;
-    }
-    seconds += num;
-    num = 0;
+    // client is already disconnected so stop here
+    AP( va( "print \"^3!ban:^7 %s^7 has been banned by %s^7 "
+      "duration: %s, reason: %s\n\"",
+      g_admin_namelog[ logmatch ]->name[ 0 ],
+      ( ent ) ? G_admin_adminPrintName( ent ) : "console",
+      duration,
+      ( *reason ) ? reason : "banned by admin" ) );
+    return qtrue;
   }
-  if( num )
-    seconds += num;
-  // overflow
-  if( seconds < 0 )
-    seconds = 0;
-  return seconds;
+  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"
+      "admin:\n%s^7\nduration:\n%s\nreason:\n%s\n%s\"",
+      ( ent ) ? G_admin_adminPrintName( ent ) : "console",
+      duration,
+      ( *reason ) ? reason : "banned by admin", notice ) );
+
+  trap_DropClient(  g_admin_namelog[ logmatch ]->slot,
+    va( "banned by %s^7, duration: %s, reason: %s",
+      ( ent ) ? G_admin_adminPrintName( ent ) : "console",
+      duration,
+      ( *reason ) ? reason : "banned by admin" ) );
+  return qtrue;
 }
 
-static qboolean admin_create_ban( gentity_t *ent,
-  char *netname,
-  char *guid,
-  char *ip,
-  int seconds,
-  char *reason ) 
+qboolean G_admin_adjustban( gentity_t *ent, int skiparg )
 {
-  g_admin_ban_t *b = NULL;
-  qtime_t qt;
-  int t;
-  int i;
-
-  t = trap_RealTime( &qt );
-  b = G_Alloc( sizeof( g_admin_ban_t ) );
+  int bnum;
+  int length;
+  int expires;
+  int time = trap_RealTime( NULL );
+  char duration[ 32 ] = {""};
+  char *reason;
+  char bs[ 5 ];
+  char secs[ MAX_TOKEN_CHARS ];
+  char mode = '\0';
 
-  if( !b )
+  if( G_SayArgc() < 3 + skiparg )
+  {
+    ADMP( "^3!adjustban: ^7usage: !adjustban [ban#] [time] [reason]\n" );
     return qfalse;
-
-  Q_strncpyz( b->name, netname, sizeof( b->name ) );
-  Q_strncpyz( b->guid, guid, sizeof( b->guid ) );
-  Q_strncpyz( b->ip, ip, sizeof( b->ip ) );
-  b->suspend = 0;
-
-  //strftime( b->made, sizeof( b->made ), "%m/%d/%y %H:%M:%S", lt );
-  Q_strncpyz( b->made, va( "%02i/%02i/%02i %02i:%02i:%02i",
-    (qt.tm_mon + 1), qt.tm_mday, (qt.tm_year - 100),
-    qt.tm_hour, qt.tm_min, qt.tm_sec ),
-    sizeof( b->made ) );
-
-  Q_strncpyz( b->banner, G_admin_get_adminname( ent ), sizeof( b->banner ) );
-
-  if( ent )
-    b->bannerlevel = G_admin_level( ent );
-  else
-    b->bannerlevel = 0;
-
-  if( !seconds )
-    b->expires = 0;
-  else
-    b->expires = t + seconds;
-  if( !*reason )
-    Q_strncpyz( b->reason, "banned by admin", sizeof( b->reason ) );
-  else
-    Q_strncpyz( b->reason, reason, sizeof( b->reason ) );
-  for( i = 0; i < MAX_ADMIN_BANS && g_admin_bans[ i ]; i++ )
-    ;
-  if( i == MAX_ADMIN_BANS )
+  }
+  G_SayArgv( 1 + skiparg, bs, sizeof( bs ) );
+  bnum = atoi( bs );
+  if( bnum < 1 || bnum > MAX_ADMIN_BANS || !g_admin_bans[ bnum - 1] )
   {
-    ADMP( "^3!ban: ^7too many bans\n" );
-    G_Free( b );
+    ADMP( "^3!adjustban: ^7invalid ban#\n" );
     return qfalse;
   }
-  g_admin_bans[ i ] = b;
-  return qtrue;
-}
-
-qboolean G_admin_kick( gentity_t *ent, int skiparg )
-{
-  int pids[ MAX_CLIENTS ];
-  char name[ MAX_NAME_LENGTH ], *reason, err[ MAX_STRING_CHARS ];
-  int minargc;
-  gentity_t *vic;
-  char notice[51];
-  
-  trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) );
-
-  minargc = 3 + skiparg;
-  if( G_admin_permission( ent, ADMF_UNACCOUNTABLE ) )
-    minargc = 2 + skiparg;
-
-  if( G_SayArgc() < minargc )
+  if( g_admin_bans[ bnum - 1 ]->expires == 0 &&
+      !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) )
   {
-    ADMP( "^3!kick: ^7usage: !kick [name] [reason]\n" );
+    ADMP( "^3!adjustban: ^7you cannot modify permanent bans\n" );
     return qfalse;
   }
-  G_SayArgv( 1 + skiparg, name, sizeof( name ) );
-  reason = G_SayConcatArgs( 2 + skiparg );
-  if( G_ClientNumbersFromString( name, pids ) != 1 )
+  if( g_admin_bans[ bnum - 1 ]->bannerlevel > G_admin_level( ent ) )
   {
-    G_MatchOnePlayer( pids, err, sizeof( err ) );
-    ADMP( va( "^3!kick: ^7%s\n", err ) );
+    ADMP( "^3!adjustban: ^7you cannot modify Bans made by admins higher than you\n" );
     return qfalse;
   }
-  if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) )
+  if( g_adminMaxBan.integer &&
+      !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) &&
+      g_admin_bans[ bnum - 1 ]->expires - time > G_admin_parse_time( g_adminMaxBan.string ) )
   {
-    ADMP( "^3!kick: ^7sorry, but your intended victim has a higher admin"
-        " level than you\n" );
-    return qfalse;
-  }
-  vic = &g_entities[ pids[ 0 ] ];
-  admin_create_ban( ent,
-    vic->client->pers.netname,
-    vic->client->pers.guid,
-    vic->client->pers.ip, G_admin_parse_time( g_adminTempBan.string ),
-    ( *reason ) ? reason : "kicked by admin" );
-  if( g_admin.string[ 0 ] )
-    admin_writeconfig();
-
- 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 ) );
-  
-  trap_DropClient( pids[ 0 ], va( "kicked%s^7, reason: %s",
-    ( ent ) ? va( " by %s", G_admin_adminPrintName( ent ) ) : " by console",
-    ( *reason ) ? reason : "kicked by admin" ) );
-
-  return qtrue;
-}
-
-qboolean G_admin_ban( gentity_t *ent, int skiparg )
-{
-  int seconds;
-  char search[ MAX_NAME_LENGTH ];
-  char secs[ 7 ];
-  char *reason;
-  int minargc;
-  char duration[ 32 ];
-  int logmatch = -1, logmatches = 0;
-  int i, j;
-  qboolean exactmatch = qfalse;
-  char n2[ MAX_NAME_LENGTH ];
-  char s2[ MAX_NAME_LENGTH ];
-  char guid_stub[ 9 ];
-  char notice[51];
-  
-  trap_Cvar_VariableStringBuffer( "g_banNotice", notice, sizeof( notice ) );
-  
-  if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) &&
-       G_admin_permission( ent, ADMF_UNACCOUNTABLE ) )
-  {
-    minargc = 2 + skiparg;
-  }
-  else if( ( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) || g_adminMaxBan.integer ) ||
-            G_admin_permission( ent, ADMF_UNACCOUNTABLE ) )
-  {
-    minargc = 3 + skiparg;
-  }
-  else
-  {
-    minargc = 4 + skiparg;
-  }
-  if( G_SayArgc() < minargc )
-  {
-    ADMP( "^3!ban: ^7usage: !ban [name|slot|ip] [time] [reason]\n" );
-    return qfalse;
-  }
-  G_SayArgv( 1 + skiparg, search, sizeof( search ) );
-  G_SanitiseString( search, s2, sizeof( s2 ) );
-  G_SayArgv( 2 + skiparg, secs, sizeof( secs ) );
-
-  seconds = G_admin_parse_time( secs );
-  if( seconds <= 0 )
-  {
-    if( g_adminMaxBan.integer && !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) )
-    {
-      ADMP( va( "^3!ban: ^7using your admin level's maximum ban length of %s\n",
-        g_adminMaxBan.string ) );
-      seconds = G_admin_parse_time( g_adminMaxBan.string );
-    }
-    else if( G_admin_permission( ent, ADMF_CAN_PERM_BAN ) )
-    {
-      seconds = 0;
-    }
-    else
-    {
-      ADMP( "^3!ban: ^7ban time must be positive\n" );
-      return qfalse;
-    }
-    reason = G_SayConcatArgs( 2 + skiparg );
-  }
-  else
-  {
-    reason = G_SayConcatArgs( 3 + skiparg );
-
-    if( g_adminMaxBan.integer &&
-        seconds > G_admin_parse_time( g_adminMaxBan.string ) &&
-        !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) )
-    {
-      seconds = G_admin_parse_time( g_adminMaxBan.string );
-      ADMP( va( "^3!ban: ^7ban length limited to %s for your admin level\n",
-        g_adminMaxBan.string ) );
-    }
-  }
-
-  for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ )
-  {
-    // skip players in the namelog who have already been banned
-    if( g_admin_namelog[ i ]->banned )
-      continue;
-
-    // skip disconnected players when banning on slot number
-    if( g_admin_namelog[ i ]->slot == -1 )
-      continue;
-
-    if( !Q_stricmp( va( "%d", g_admin_namelog[ i ]->slot ), s2 ) )
-    {
-      logmatches = 1;
-      logmatch = i;
-      exactmatch = qtrue;
-      break;
-    }
-  } 
-
-  for( i = 0;
-       !exactmatch && i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ];
-       i++ )
-  {
-    // skip players in the namelog who have already been banned
-    if( g_admin_namelog[ i ]->banned )
-      continue;
-
-    if( !Q_stricmp( g_admin_namelog[ i ]->ip, s2 ) )
-    {
-      logmatches = 1;
-      logmatch = i;
-      exactmatch = qtrue;
-      break;
-    }
-    for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES
-      && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ )
-    {
-      G_SanitiseString(g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) );
-      if( strstr( n2, s2 ) )
-      {
-        if( logmatch != i )
-          logmatches++;
-        logmatch = i; 
-      }
-    }
-  }
-  
-  if( !logmatches ) 
-  {
-    ADMP( "^3!ban: ^7no player found by that name, IP, or slot number\n" );
-    return qfalse;
-  } 
-  else if( logmatches > 1 )
-  {
-    ADMBP_begin();
-    ADMBP( "^3!ban: ^7multiple recent clients match name, use IP or slot#:\n" );
-    for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ )
-    {
-      for( j = 0; j < 8; j++ )
-        guid_stub[ j ] = g_admin_namelog[ i ]->guid[ j + 24 ];
-      guid_stub[ j ] = '\0';
-      for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES
-        && g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ )
-      {
-        G_SanitiseString(g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) );
-        if( strstr( n2, s2 ) )
-        {
-          if( g_admin_namelog[ i ]->slot > -1 )
-            ADMBP( "^3" );
-          ADMBP( va( "%-2s (*%s) %15s ^7'%s^7'\n",
-           (g_admin_namelog[ i ]->slot > -1) ?
-             va( "%d", g_admin_namelog[ i ]->slot ) : "-",
-           guid_stub,
-           g_admin_namelog[ i ]->ip,
-           g_admin_namelog[ i ]->name[ j ] ) );
-        }
-      }
-    }
-    ADMBP_end();
-    return qfalse;
-  }
-  
-  G_admin_duration( ( seconds ) ? seconds : -1,
-    duration, sizeof( duration ) );
-
-  if( ent && !admin_higher_guid( ent->client->pers.guid,
-    g_admin_namelog[ logmatch ]->guid ) )
-  {
-
-    ADMP( "^3!ban: ^7sorry, but your intended victim has a higher admin"
-      " level than you\n" );
-    return qfalse;
-  }
-
-  admin_create_ban( ent,
-    g_admin_namelog[ logmatch ]->name[ 0 ],
-    g_admin_namelog[ logmatch ]->guid,
-    g_admin_namelog[ logmatch ]->ip,
-    seconds, reason ); 
-
-  g_admin_namelog[ logmatch ]->banned = qtrue;
-
-  if( !g_admin.string[ 0 ] )
-    ADMP( "^3!ban: ^7WARNING g_admin not set, not saving ban to a file\n" );
-  else
-    admin_writeconfig();
-
-  if( g_admin_namelog[ logmatch ]->slot == -1 ) 
-  {
-    // client is already disconnected so stop here
-    AP( va( "print \"^3!ban:^7 %s^7 has been banned by %s^7 "
-      "duration: %s, reason: %s\n\"",
-      g_admin_namelog[ logmatch ]->name[ 0 ],
-      ( ent ) ? G_admin_adminPrintName( ent ) : "console",
-      duration,
-      ( *reason ) ? reason : "banned by admin" ) );
-    return qtrue;
-  }
-  
-  trap_SendServerCommand( g_admin_namelog[ logmatch ]->slot,
-    va( "disconnect \"You have been banned.\n"
-      "admin:\n%s^7\nduration:\n%s\nreason:\n%s\n%s\"",
-      ( ent ) ? G_admin_adminPrintName( ent ) : "console",
-      duration,
-      ( *reason ) ? reason : "banned by admin", notice ) );
-
-  trap_DropClient(  g_admin_namelog[ logmatch ]->slot,
-    va( "banned by %s^7, duration: %s, reason: %s",
-      ( ent ) ? G_admin_adminPrintName( ent ) : "console",
-      duration,
-      ( *reason ) ? reason : "banned by admin" ) );
-  return qtrue;
-}
-
-qboolean G_admin_adjustban( gentity_t *ent, int skiparg )
-{
-  int bnum;
-  int length;
-  int expires;
-  int time = trap_RealTime( NULL );
-  char duration[ 32 ] = {""};
-  char *reason;
-  char bs[ 5 ];
-  char secs[ MAX_TOKEN_CHARS ];
-  char mode = '\0';
-
-  if( G_SayArgc() < 3 + skiparg )
-  {
-    ADMP( "^3!adjustban: ^7usage: !adjustban [ban#] [time] [reason]\n" );
-    return qfalse;
-  }
-  G_SayArgv( 1 + skiparg, bs, sizeof( bs ) );
-  bnum = atoi( bs );
-  if( bnum < 1 || bnum > MAX_ADMIN_BANS || !g_admin_bans[ bnum - 1] )
-  {
-    ADMP( "^3!adjustban: ^7invalid ban#\n" );
-    return qfalse;
-  }
-  if( g_admin_bans[ bnum - 1 ]->expires == 0 &&
-      !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) )
-  {
-    ADMP( "^3!adjustban: ^7you cannot modify permanent bans\n" );
-    return qfalse;
-  }
-  if( g_admin_bans[ bnum - 1 ]->bannerlevel > G_admin_level( ent ) )
-  {
-    ADMP( "^3!adjustban: ^7you cannot modify Bans made by admins higher than you\n" );
-    return qfalse;
-  }
-  if( g_adminMaxBan.integer &&
-      !G_admin_permission( ent, ADMF_CAN_PERM_BAN ) &&
-      g_admin_bans[ bnum - 1 ]->expires - time > G_admin_parse_time( g_adminMaxBan.string ) )
-  {
-    ADMP( va( "^3!adjustban: ^7your admin level cannot modify bans longer than %s\n",
-              g_adminMaxBan.string ) );
+    ADMP( va( "^3!adjustban: ^7your admin level cannot modify bans longer than %s\n",
+              g_adminMaxBan.string ) );
     return qfalse;
   }
   G_SayArgv( 2 + skiparg, secs, sizeof( secs ) );
@@ -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,125 +3375,311 @@ 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;
     }
   }
 }
 
-void G_admin_adminlog_cleanup( void )
+typedef struct
 {
-  int i;
+  g_admin_admin_t *admin;
+  int id;
+}
+g_admin_sort_t;
 
-  for( i = 0; i < MAX_ADMIN_ADMINLOGS && g_admin_adminlog[ i ]; i++ )
-  {
-    G_Free( g_admin_adminlog[ i ] );
-    g_admin_adminlog[ i ] = NULL;
-  }
+static int SortSeenTimes( const void *av, const void *bv)
+{
+  const g_admin_sort_t *a = av;
+  const g_admin_sort_t *b = bv;
 
-  admin_adminlog_index = 0;
+  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;
 }
 
-void G_admin_adminlog_log( gentity_t *ent, char *command, char *args, int skiparg, qboolean success )
+qboolean G_admin_expire( gentity_t *ent, int skiparg )
 {
-  g_admin_adminlog_t *adminlog;
-  int previous;
-  int count = 1;
+  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( !command )
-    return;
-
-  if( !Q_stricmp( command, "adminlog" ) ||
-      !Q_stricmp( command, "admintest" ) ||
-      !Q_stricmp( command, "help" ) ||
-      !Q_stricmp( command, "info" ) ||
-      !Q_stricmp( command, "listadmins" ) ||
-      !Q_stricmp( command, "listplayers" ) ||
-      !Q_stricmp( command, "namelog" ) ||
-      !Q_stricmp( command, "showbans" ) ||
-      !Q_stricmp( command, "seen" ) ||
-      !Q_stricmp( command, "time" ) )
-    return;
-
-  previous = admin_adminlog_index - 1;
-  if( previous < 0 )
-    previous = MAX_ADMIN_ADMINLOGS - 1;
+  if( g_adminExpireTime.integer < 1 )
+  {
+    ADMP( "^3!expire: ^7expire is disabled\n" );
+    return qfalse;
+  }
 
-  if( g_admin_adminlog[ previous ] )
-    count = g_admin_adminlog[ previous ]->id + 1;
+  if( G_SayArgc() > 1 + skiparg )
+  {
+    G_SayArgv( skiparg + 1, arg, sizeof( arg ) );
+    if( !Q_stricmp( arg, "confirm" ) )
+      confirm = qtrue;
+  }
 
-  if( g_admin_adminlog[ admin_adminlog_index ] )
-    adminlog = g_admin_adminlog[ admin_adminlog_index ];
-  else
-    adminlog = G_Alloc( sizeof( g_admin_adminlog_t ) );
+  t = trap_RealTime( &qt );
 
-  memset( adminlog, 0, sizeof( adminlog ) );
-  adminlog->id = count;
-  adminlog->time = level.time - level.startTime;
-  adminlog->success = success;
-  Q_strncpyz( adminlog->command, command, sizeof( adminlog->command ) );
+  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++;
+  }
 
-  if ( args )
-    Q_strncpyz( adminlog->args, args, sizeof( adminlog->args ) );
-  else
-    Q_strncpyz( adminlog->args, G_SayConcatArgs( 1 + skiparg ), sizeof( adminlog->args ) );
+  qsort( sort_list, count, sizeof( sort_list[ 0 ] ), SortSeenTimes );
 
-  if( ent )
+  ADMBP_begin();
+  for( i = 0; i < count && max > 0 ; i++ )
   {
-    qboolean found = qfalse;
-    // real admin name
-    for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; 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 )
     {
-      if( !Q_stricmp( g_admin_admins[ i ]->guid, ent->client->pers.guid ) )
+      char sduration[ 32 ];
+
+      max--;
+      G_admin_duration( t - sort_list[ i ].admin->seen, sduration, sizeof( sduration ) );
+
+      if( confirm )
       {
-        Q_strncpyz( adminlog->name, g_admin_admins[ i ]->name, sizeof( adminlog->name ) );
-        found = qtrue;
-        break;
+        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" ) );
       }
     }
-    if( !found )
-      Q_strncpyz( adminlog->name, ent->client->pers.netname, sizeof( adminlog->name ) );
-
-    adminlog->level = ent->client->pers.adminLevel;
-  }
-  else
-  {
-    Q_strncpyz( adminlog->name, "console", sizeof( adminlog->name ) );
-    adminlog->level = 10000;
   }
 
-  g_admin_adminlog[ admin_adminlog_index ] = adminlog;
-  admin_adminlog_index++;
-  if( admin_adminlog_index >= MAX_ADMIN_ADMINLOGS )
-    admin_adminlog_index = 0;
+  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_adminlog( gentity_t *ent, int skiparg )
+qboolean G_admin_putteam( gentity_t *ent, int skiparg )
 {
-  g_admin_adminlog_t *results[ 10 ];
-  int result_index = 0;
-  char *search_cmd = NULL;
-  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 ];
-  int name_length = 12;
-  int max_id = 0;
-  int i;
-  qboolean match;
-
-  memset( results, 0, sizeof( results ) );
+  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;
 
-  index = admin_adminlog_index;
-  for( i = 0; i < 10; i++ )
+  G_SayArgv( 1 + skiparg, name, sizeof( name ) );
+  G_SayArgv( 2 + skiparg, team, sizeof( team ) );
+  if( G_SayArgc() < 3 + skiparg )
   {
-    int prev;
-
-    prev = index - 1;
+    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;
+
+  for( i = 0; i < MAX_ADMIN_ADMINLOGS && g_admin_adminlog[ i ]; i++ )
+  {
+    G_Free( g_admin_adminlog[ i ] );
+    g_admin_adminlog[ i ] = NULL;
+  }
+
+  admin_adminlog_index = 0;
+}
+
+void G_admin_adminlog_log( gentity_t *ent, char *command, char *args, int skiparg, qboolean success )
+{
+  g_admin_adminlog_t *adminlog;
+  int previous;
+  int count = 1;
+  int i;
+
+  if( !command )
+    return;
+
+  if( !Q_stricmp( command, "adminlog" ) ||
+      !Q_stricmp( command, "admintest" ) ||
+      !Q_stricmp( command, "help" ) ||
+      !Q_stricmp( command, "info" ) ||
+      !Q_stricmp( command, "listadmins" ) ||
+      !Q_stricmp( command, "listplayers" ) ||
+      !Q_stricmp( command, "namelog" ) ||
+      !Q_stricmp( command, "showbans" ) ||
+      !Q_stricmp( command, "time" ) )
+    return;
+
+  previous = admin_adminlog_index - 1;
+  if( previous < 0 )
+    previous = MAX_ADMIN_ADMINLOGS - 1;
+
+  if( g_admin_adminlog[ previous ] )
+    count = g_admin_adminlog[ previous ]->id + 1;
+
+  if( g_admin_adminlog[ admin_adminlog_index ] )
+    adminlog = g_admin_adminlog[ admin_adminlog_index ];
+  else
+    adminlog = G_Alloc( sizeof( g_admin_adminlog_t ) );
+
+  memset( adminlog, 0, sizeof( adminlog ) );
+  adminlog->id = count;
+  adminlog->time = level.time - level.startTime;
+  adminlog->success = success;
+  Q_strncpyz( adminlog->command, command, sizeof( adminlog->command ) );
+
+  if ( args )
+    Q_strncpyz( adminlog->args, args, sizeof( adminlog->args ) );
+  else
+    Q_strncpyz( adminlog->args, G_SayConcatArgs( 1 + skiparg ), sizeof( adminlog->args ) );
+
+  if( ent )
+  {
+    qboolean found = qfalse;
+    // real admin name
+    for( i = 0; i < MAX_ADMIN_ADMINS && g_admin_admins[ i ]; i++ )
+    {
+      if( !Q_stricmp( g_admin_admins[ i ]->guid, ent->client->pers.guid ) )
+      {
+        Q_strncpyz( adminlog->name, g_admin_admins[ i ]->name, sizeof( adminlog->name ) );
+        found = qtrue;
+        break;
+      }
+    }
+    if( !found )
+      Q_strncpyz( adminlog->name, ent->client->pers.netname, sizeof( adminlog->name ) );
+
+    adminlog->level = ent->client->pers.adminLevel;
+  }
+  else
+  {
+    Q_strncpyz( adminlog->name, "console", sizeof( adminlog->name ) );
+    adminlog->level = 10000;
+  }
+
+  g_admin_adminlog[ admin_adminlog_index ] = adminlog;
+  admin_adminlog_index++;
+  if( admin_adminlog_index >= MAX_ADMIN_ADMINLOGS )
+    admin_adminlog_index = 0;
+}
+
+qboolean G_admin_adminlog( gentity_t *ent, int skiparg )
+{
+  g_admin_adminlog_t *results[ 10 ];
+  int result_index = 0;
+  char *search_cmd = NULL;
+  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 ];
+  int name_length = 12;
+  int max_id = 0;
+  int i;
+  qboolean match;
+
+  memset( results, 0, sizeof( results ) );
+
+  index = admin_adminlog_index;
+  for( i = 0; i < 10; i++ )
+  {
+    int prev;
+
+    prev = index - 1;
     if( prev < 0 )
       prev = MAX_ADMIN_ADMINLOGS - 1;
     if( !g_admin_adminlog[ prev ] )
@@ -3722,60 +3850,315 @@ qboolean G_admin_adminlog( gentity_t *ent, int skiparg )
   return qtrue;
 }
 
-qboolean G_admin_map( gentity_t *ent, int skiparg )
+void G_admin_tklog_cleanup( void )
 {
-  char map[ MAX_QPATH ];
-  char layout[ MAX_QPATH ] = { "" };
-  
-  if( G_SayArgc( ) < 2 + skiparg )
+  int i;
+
+  for( i = 0; i < MAX_ADMIN_TKLOGS && g_admin_tklog[ i ]; i++ )
   {
-    ADMP( "^3!map: ^7usage: !map [map] (layout)\n" );
-    return qfalse;
+    G_Free( g_admin_tklog[ i ] );
+    g_admin_tklog[ i ] = NULL;
   }
 
-  G_SayArgv( skiparg + 1, map, sizeof( map ) );
+  admin_tklog_index = 0;
+}
 
-  if( !trap_FS_FOpenFile( va( "maps/%s.bsp", map ), NULL, FS_READ ) )
+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 )
   {
-    ADMP( va( "^3!map: ^7invalid map name '%s'\n", map ) );
-    return qfalse;
+    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 ];
   }
-
-  if( G_SayArgc( ) > 2 + skiparg )
+  else
   {
-    G_SayArgv( skiparg + 2, layout, sizeof( layout ) );
-    if( !Q_stricmp( layout, "*BUILTIN*" ) ||
-      trap_FS_FOpenFile( va( "layouts/%s/%s.dat", map, layout ),
-        NULL, FS_READ ) > 0 )
-    {
-      trap_Cvar_Set( "g_layouts", layout );
-    }
-    else
-    {
-      ADMP( va( "^3!map: ^7invalid layout name '%s'\n", layout ) );
-      return qfalse;
-    }
+    Q_strncpyz( tklog->victim, "^3BLEEDING", sizeof( tklog->victim ) );
+    tklog->damage = attacker->client->pers.statscounters.spreebleeds;
+    tklog->value = g_bleedingSpree.integer * 100;
   }
 
-  trap_SendConsoleCommand( EXEC_APPEND, va( "map %s", map ) );
-  level.restarted = qtrue;
-  AP( va( "print \"^3!map: ^7map '%s' started by %s^7 %s\n\"", map,
-          ( ent ) ? G_admin_adminPrintName( ent ) : "console",
-          ( layout[ 0 ] ) ? va( "(forcing layout '%s')", layout ) : "" ) );
-  G_admin_maplog_result( "M" );
-  return qtrue;
+  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_devmap( gentity_t *ent, int skiparg )
+qboolean G_admin_tklog( gentity_t *ent, int skiparg )
 {
-  char map[ MAX_QPATH ];
-  char layout[ MAX_QPATH ] = { "" };
+  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;
 
-  if( G_SayArgc( ) < 2 + skiparg )
-  {
-    ADMP( "^3!devmap: ^7usage: !devmap [map] (layout)\n" );
-    return qfalse;
-  }
+  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 ];
+  char layout[ MAX_QPATH ] = { "" };
+  
+  if( G_SayArgc( ) < 2 + skiparg )
+  {
+    ADMP( "^3!map: ^7usage: !map [map] (layout)\n" );
+    return qfalse;
+  }
+
+  G_SayArgv( skiparg + 1, map, sizeof( map ) );
+
+  if( !trap_FS_FOpenFile( va( "maps/%s.bsp", map ), NULL, FS_READ ) )
+  {
+    ADMP( va( "^3!map: ^7invalid map name '%s'\n", map ) );
+    return qfalse;
+  }
+
+  if( G_SayArgc( ) > 2 + skiparg )
+  {
+    G_SayArgv( skiparg + 2, layout, sizeof( layout ) );
+    if( !Q_stricmp( layout, "*BUILTIN*" ) ||
+      trap_FS_FOpenFile( va( "layouts/%s/%s.dat", map, layout ),
+        NULL, FS_READ ) > 0 )
+    {
+      trap_Cvar_Set( "g_layouts", layout );
+    }
+    else
+    {
+      ADMP( va( "^3!map: ^7invalid layout name '%s'\n", layout ) );
+      return qfalse;
+    }
+  }
+
+  trap_SendConsoleCommand( EXEC_APPEND, va( "map %s", map ) );
+  level.restarted = qtrue;
+  AP( va( "print \"^3!map: ^7map '%s' started by %s^7 %s\n\"", map,
+          ( ent ) ? G_admin_adminPrintName( ent ) : "console",
+          ( layout[ 0 ] ) ? va( "(forcing layout '%s')", layout ) : "" ) );
+  G_admin_maplog_result( "M" );
+  return qtrue;
+}
+
+qboolean G_admin_devmap( gentity_t *ent, int skiparg )
+{
+  char map[ MAX_QPATH ];
+  char layout[ MAX_QPATH ] = { "" };
+
+  if( G_SayArgc( ) < 2 + skiparg )
+  {
+    ADMP( "^3!devmap: ^7usage: !devmap [map] (layout)\n" );
+    return qfalse;
+  }
 
   G_SayArgv( skiparg + 1, map, sizeof( map ) );
 
@@ -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( count )
+    
+    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,361 +6302,662 @@ qboolean G_admin_restart( gentity_t *ent, int skiparg )
   return qtrue;
 }
 
-qboolean G_admin_nobuild( gentity_t *ent, int skiparg )
+qboolean G_admin_nextmap( gentity_t *ent, int skiparg )
+{
+  AP( va( "print \"^3!nextmap: ^7%s^7 decided to load the next map\n\"",
+    ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
+  level.lastWin = PTE_NONE;
+  trap_SetConfigstring( CS_WINNER, "Evacuation" );
+  LogExit( va( "nextmap was run by %s",
+    ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
+  G_admin_maplog_result( "N" );
+  return qtrue;
+}
+
+qboolean G_admin_namelog( gentity_t *ent, int skiparg )
+{
+  int i, j;
+  char search[ MAX_NAME_LENGTH ] = {""};
+  char s2[ MAX_NAME_LENGTH ] = {""};
+  char n2[ MAX_NAME_LENGTH ] = {""};
+  char guid_stub[ 9 ];
+  qboolean found = qfalse;
+  int printed = 0;
+
+  if( G_SayArgc() > 1 + skiparg )
+  {
+    G_SayArgv( 1 + skiparg, search, sizeof( search ) );
+    G_SanitiseString( search, s2, sizeof( s2 ) );
+  }
+  ADMBP_begin();
+  for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ )
+  {
+    if( search[0] )
+    {
+      found = qfalse;
+      for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && 
+        g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ )
+      {
+        G_SanitiseString( g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) );
+        if( strstr( n2, s2 ) )
+        {
+          found = qtrue;
+          break;
+        }
+      }
+      if( !found )
+        continue;
+    }
+    printed++;
+    for( j = 0; j < 8; j++ )
+      guid_stub[ j ] = g_admin_namelog[ i ]->guid[ j + 24 ];
+    guid_stub[ j ] = '\0';
+    if( g_admin_namelog[ i ]->slot > -1 )
+       ADMBP( "^3" );
+    ADMBP( va( "%-2s (*%s) %15s^7", 
+      (g_admin_namelog[ i ]->slot > -1 ) ?
+        va( "%d", g_admin_namelog[ i ]->slot ) : "-",
+      guid_stub, g_admin_namelog[ i ]->ip ) );
+    for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && 
+      g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ )
+    {
+      ADMBP( va( " '%s^7'", g_admin_namelog[ i ]->name[ j ] ) );
+    }
+    ADMBP( "\n" ); 
+  } 
+  ADMBP( va( "^3!namelog:^7 %d recent clients found\n", printed ) );
+  ADMBP_end();
+  return qtrue;
+}
+
+qboolean G_admin_lock( gentity_t *ent, int skiparg )
+{
+  char teamName[2] = {""};
+  pTeam_t team;
+
+  if( G_SayArgc() < 2 + skiparg )
+  {
+    ADMP( "^3!lock: ^7usage: !lock [a|h]\n" );
+    return qfalse;
+  }
+  G_SayArgv( 1 + skiparg, teamName, sizeof( teamName ) );
+  if( teamName[ 0 ] == 'a' || teamName[ 0 ] == 'A' )
+    team = PTE_ALIENS;
+  else if( teamName[ 0 ] == 'h' || teamName[ 0 ] == 'H' )
+    team = PTE_HUMANS;
+  else
+  {
+    ADMP( va( "^3!lock: ^7invalid team\"%c\"\n", teamName[0] ) );
+    return qfalse;
+  }
+
+  if( team == PTE_ALIENS )
+  {
+    if( level.alienTeamLocked )
+    {
+      ADMP( "^3!lock: ^7Alien team is already locked\n" );
+      return qfalse;
+    }
+    else
+      level.alienTeamLocked = qtrue;
+  }
+  else if( team == PTE_HUMANS ) {
+    if( level.humanTeamLocked )
+    {
+      ADMP( "^3!lock: ^7Human team is already locked\n" );
+      return qfalse;
+    }
+    else
+      level.humanTeamLocked = qtrue;
+  }
+
+  AP( va( "print \"^3!lock: ^7%s team has been locked by %s\n\"",
+    ( team == PTE_ALIENS ) ? "Alien" : "Human",
+    ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
+  return qtrue;
+} 
+
+qboolean G_admin_unlock( gentity_t *ent, int skiparg )
+{
+  char teamName[2] = {""};
+  pTeam_t team;
+
+  if( G_SayArgc() < 2 + skiparg )
+  {
+    ADMP( "^3!unlock: ^7usage: !unlock [a|h]\n" );
+    return qfalse;
+  }
+  G_SayArgv( 1 + skiparg, teamName, sizeof( teamName ) );
+  if( teamName[ 0 ] == 'a' || teamName[ 0 ] == 'A' )
+    team = PTE_ALIENS;
+  else if( teamName[ 0 ] == 'h' || teamName[ 0 ] == 'H' )
+    team = PTE_HUMANS;
+  else
+  {
+    ADMP( va( "^3!unlock: ^7invalid team\"%c\"\n", teamName[0] ) );
+    return qfalse;
+  }
+  
+  if( team == PTE_ALIENS )
+  {
+    if( !level.alienTeamLocked )
+    {
+      ADMP( "^3!unlock: ^7Alien team is not currently locked\n" );
+      return qfalse;
+    }
+    else
+      level.alienTeamLocked = qfalse;
+  }
+  else if( team == PTE_HUMANS ) {
+    if( !level.humanTeamLocked )
+    {
+      ADMP( "^3!unlock: ^7Human team is not currently locked\n" );
+      return qfalse;
+    }
+    else
+      level.humanTeamLocked = qfalse;
+  }
+
+  AP( va( "print \"^3!unlock: ^7%s team has been unlocked by %s\n\"",
+    ( team == PTE_ALIENS ) ? "Alien" : "Human",
+    ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
+  return qtrue;
+} 
+
+qboolean G_admin_designate( gentity_t *ent, int skiparg )
+{
+  int pids[ MAX_CLIENTS ];
+  char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ];
+  char command[ MAX_ADMIN_CMD_LEN ], *cmd;
+  gentity_t *vic;
+
+  if( G_SayArgc() < 2 + skiparg )
+  {
+    ADMP( "^3!designate: ^7usage: designate [name|slot#]\n" );
+    return qfalse;
+  }
+  G_SayArgv( skiparg, command, sizeof( command ) );
+  cmd = command;
+  if( cmd && *cmd == '!' )
+    cmd++;
+  G_SayArgv( 1 + skiparg, name, sizeof( name ) );
+  if( G_ClientNumbersFromString( name, pids ) != 1 )
+  {
+    G_MatchOnePlayer( pids, err, sizeof( err ) );
+    ADMP( va( "^3!designate: ^7%s\n", err ) );
+    return qfalse;
+  }
+  if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) &&
+    !Q_stricmp( cmd, "undesignate" ) )
+  {
+    ADMP( "^3!designate: ^7sorry, but your intended victim has a higher admin"
+        " level than you\n" );
+    return qfalse;
+  }
+  vic = &g_entities[ pids[ 0 ] ];
+  if( vic->client->pers.designatedBuilder == qtrue )
+  {
+    if( !Q_stricmp( cmd, "designate" ) )
+    {
+      ADMP( "^3!designate: ^7player is already designated builder\n" );
+      return qtrue;
+    }
+    vic->client->pers.designatedBuilder = qfalse;
+    CPx( pids[ 0 ], "cp \"^1Your designation has been revoked\"" );
+    AP( va(
+      "print \"^3!designate: ^7%s^7's designation has been revoked by %s\n\"",
+       vic->client->pers.netname,
+       ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
+    G_CheckDBProtection( );
+  }
+  else
+  {
+    if( !Q_stricmp( cmd, "undesignate" ) )
+    {
+      ADMP( "^3!undesignate: ^7player is not currently designated builder\n" );
+      return qtrue;
+    }
+    vic->client->pers.designatedBuilder = qtrue;
+    CPx( pids[ 0 ], "cp \"^1You've been designated\"" );
+    AP( va( "print \"^3!designate: ^7%s^7 has been designated by ^7%s\n\"",
+      vic->client->pers.netname,
+      ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
+  }
+  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 buffer[ MAX_STRING_CHARS ];
-  int  i, tmp;
-  
+  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( "^3!nobuild: ^7usage: !nobuild (^5enable / disable / log / remove / save^7)\n" );
+    ADMP( va( "^3!%s: ^7usage: !%s slot# flag\n", cmd, cmd ) );
     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\"",
-    ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
-  level.lastWin = PTE_NONE;
-  trap_SetConfigstring( CS_WINNER, "Evacuation" );
-  LogExit( va( "nextmap was run by %s",
-    ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
-  G_admin_maplog_result( "N" );
-  return qtrue;
-}
-
-qboolean G_admin_namelog( gentity_t *ent, int skiparg )
-{
-  int i, j;
-  char search[ MAX_NAME_LENGTH ] = {""};
-  char s2[ MAX_NAME_LENGTH ] = {""};
-  char n2[ MAX_NAME_LENGTH ] = {""};
-  char guid_stub[ 9 ];
-  qboolean found = qfalse;
-  int printed = 0;
 
-  if( G_SayArgc() > 1 + skiparg )
+  G_SayArgv( 1 + skiparg, name, sizeof( name ) );
+  if( name[ 0 ] == '*' )
   {
-    G_SayArgv( 1 + skiparg, search, sizeof( search ) );
-    G_SanitiseString( search, s2, sizeof( s2 ) );
+    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 );
   }
-  ADMBP_begin();
-  for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ )
+  else
   {
-    if( search[0] )
+    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 ) )
     {
-      found = qfalse;
-      for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && 
-        g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ )
+      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++ )
       {
-        G_SanitiseString( g_admin_namelog[ i ]->name[ j ], n2, sizeof( n2 ) );
-        if( strstr( n2, s2 ) )
+        if( g_admin_admins[ id ]->level == g_admin_levels[ i ]->level )
         {
-          found = qtrue;
+          flag = g_admin_levels[ i ]->flags;
+          level = g_admin_admins[ id ]->level;
           break;
         }
       }
-      if( !found )
-        continue;
+      ADMP( va( "^3%s:^7 flags for %s^7 are '^3%s^7'\n",
+        cmd, adminname, g_admin_admins[ id ]->flags) );
     }
-    printed++;
-    for( j = 0; j < 8; j++ )
-      guid_stub[ j ] = g_admin_namelog[ i ]->guid[ j + 24 ];
-    guid_stub[ j ] = '\0';
-    if( g_admin_namelog[ i ]->slot > -1 )
-       ADMBP( "^3" );
-    ADMBP( va( "%-2s (*%s) %15s^7", 
-      (g_admin_namelog[ i ]->slot > -1 ) ?
-        va( "%d", g_admin_namelog[ i ]->slot ) : "-",
-      guid_stub, g_admin_namelog[ i ]->ip ) );
-    for( j = 0; j < MAX_ADMIN_NAMELOG_NAMES && 
-      g_admin_namelog[ i ]->name[ j ][ 0 ]; j++ )
+    else
     {
-      ADMBP( va( " '%s^7'", g_admin_namelog[ i ]->name[ j ] ) );
+      flag = g_admin_levels[ admin_level ]->flags;
+      level = g_admin_levels[ admin_level ]->level;
     }
-    ADMBP( "\n" ); 
-  } 
-  ADMBP( va( "^3!namelog:^7 %d recent clients found\n", printed ) );
-  ADMBP_end();
-  return qtrue;
-}
+    ADMP( va( "^3%s:^7  level %d flags are '%s'\n",
+      cmd, level, flag ) );
 
-qboolean G_admin_lock( gentity_t *ent, int skiparg )
-{
-  char teamName[2] = {""};
-  pTeam_t team;
+    return qtrue;
+  }
 
-  if( G_SayArgc() < 2 + skiparg )
+  G_SayArgv( 2 + skiparg, flagbuf, sizeof( flagbuf ) );
+  flag = flagbuf;
+  if( flag[ 0 ] == '-' || flag[ 0 ] == '+' )
   {
-    ADMP( "^3!lock: ^7usage: !lock [a|h]\n" );
+    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;
   }
-  G_SayArgv( 1 + skiparg, teamName, sizeof( teamName ) );
-  if( teamName[ 0 ] == 'a' || teamName[ 0 ] == 'A' )
-    team = PTE_ALIENS;
-  else if( teamName[ 0 ] == 'h' || teamName[ 0 ] == 'H' )
-    team = PTE_HUMANS;
-  else
+  if( flag[ 0 ] != '.' && !G_admin_permission( ent, flag ) )
   {
-    ADMP( va( "^3!lock: ^7invalid team\"%c\"\n", teamName[0] ) );
+    ADMP( va( "^3%s:^7 you can only change flags that you also have\n", cmd ) );
     return qfalse;
   }
 
-  if( team == PTE_ALIENS )
+  if( !Q_stricmp( cmd, "unflag" ) )
   {
-    if( level.alienTeamLocked )
-    {
-      ADMP( "^3!lock: ^7Alien team is already locked\n" );
-      return qfalse;
-    }
-    else
-      level.alienTeamLocked = qtrue;
-  }
-  else if( team == PTE_HUMANS ) {
-    if( level.humanTeamLocked )
-    {
-      ADMP( "^3!lock: ^7Human team is already locked\n" );
-      return qfalse;
-    }
-    else
-      level.humanTeamLocked = qtrue;
+    clear = qtrue;
   }
 
-  AP( va( "print \"^3!lock: ^7%s team has been locked by %s\n\"",
-    ( team == PTE_ALIENS ) ? "Alien" : "Human",
-    ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
-  return qtrue;
-} 
-
-qboolean G_admin_unlock( gentity_t *ent, int skiparg )
-{
-  char teamName[2] = {""};
-  pTeam_t team;
-
-  if( G_SayArgc() < 2 + skiparg )
+  if( admin_level < 0 )
   {
-    ADMP( "^3!unlock: ^7usage: !unlock [a|h]\n" );
-    return qfalse;
+    result = G_admin_user_flag( g_admin_admins[ id ]->flags, flag, add, clear,
+                                g_admin_admins[ id ]->flags, sizeof( g_admin_admins[ id ]->flags ) );
   }
-  G_SayArgv( 1 + skiparg, teamName, sizeof( teamName ) );
-  if( teamName[ 0 ] == 'a' || teamName[ 0 ] == 'A' )
-    team = PTE_ALIENS;
-  else if( teamName[ 0 ] == 'h' || teamName[ 0 ] == 'H' )
-    team = PTE_HUMANS;
   else
   {
-    ADMP( va( "^3!unlock: ^7invalid team\"%c\"\n", teamName[0] ) );
+    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( team == PTE_ALIENS )
+
+  if( !Q_stricmp( cmd, "flag" ) )
   {
-    if( !level.alienTeamLocked )
-    {
-      ADMP( "^3!unlock: ^7Alien team is not currently locked\n" );
-      return qfalse;
-    }
-    else
-      level.alienTeamLocked = qfalse;
+    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 if( team == PTE_HUMANS ) {
-    if( !level.humanTeamLocked )
-    {
-      ADMP( "^3!unlock: ^7Human team is not currently locked\n" );
-      return qfalse;
-    }
-    else
-      level.humanTeamLocked = qfalse;
+  else
+  {
+    G_AdminsPrintf( "^3!%s: ^7admin flag '%s' for %s^7 cleared by %s\n",
+      cmd, flag, adminname,
+      ( ent ) ? ent->client->pers.netname : "console" );
   }
 
-  AP( va( "print \"^3!unlock: ^7%s team has been unlocked by %s\n\"",
-    ( team == PTE_ALIENS ) ? "Alien" : "Human",
-    ( ent ) ? G_admin_adminPrintName( ent ) : "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_designate( gentity_t *ent, int skiparg )
+qboolean G_admin_immunity( gentity_t *ent, int skiparg )
 {
-  int pids[ MAX_CLIENTS ];
-  char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ];
-  char command[ MAX_ADMIN_CMD_LEN ], *cmd;
-  gentity_t *vic;
+  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!designate: ^7usage: designate [name|slot#]\n" );
+    ADMP( "^3!immunity: ^7usage: immunity [+|-]slot#\n" );
     return qfalse;
   }
-  G_SayArgv( skiparg, command, sizeof( command ) );
+  G_SayArgv( 1 + skiparg, command, sizeof( command ) );
   cmd = command;
-  if( cmd && *cmd == '!' )
-    cmd++;
-  G_SayArgv( 1 + skiparg, name, sizeof( name ) );
-  if( G_ClientNumbersFromString( name, pids ) != 1 )
+  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 != '-' )
   {
-    G_MatchOnePlayer( pids, err, sizeof( err ) );
-    ADMP( va( "^3!designate: ^7%s\n", err ) );
+    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;
   }
-  if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) &&
-    !Q_stricmp( cmd, "undesignate" ) )
+
+  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( "^3!designate: ^7sorry, but your intended victim has a higher admin"
-        " level than you\n" );
+    ADMP( va( "^3!immunity: ^7an error occured setting flag, %s\n", result ) );
     return qfalse;
   }
-  vic = &g_entities[ pids[ 0 ] ];
-  if( vic->client->pers.designatedBuilder == qtrue )
+
+  if( *action == '+' )
   {
-    if( !Q_stricmp( cmd, "designate" ) )
-    {
-      ADMP( "^3!designate: ^7player is already designated builder\n" );
-      return qtrue;
-    }
-    vic->client->pers.designatedBuilder = qfalse;
-    CPx( pids[ 0 ], "cp \"^1Your designation has been revoked\"" );
     AP( va(
-      "print \"^3!designate: ^7%s^7's designation has been revoked by %s\n\"",
-       vic->client->pers.netname,
-       ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
-    G_CheckDBProtection( );
+      "print \"^3!immunity: ^7%s^7 was given ban immunity by %s\n\"",
+      adminname, ( ent ) ? ent->client->pers.netname : "console" ) );
   }
   else
   {
-    if( !Q_stricmp( cmd, "undesignate" ) )
-    {
-      ADMP( "^3!undesignate: ^7player is not currently designated builder\n" );
-      return qtrue;
-    }
-    vic->client->pers.designatedBuilder = qtrue;
-    CPx( pids[ 0 ], "cp \"^1You've been designated\"" );
-    AP( va( "print \"^3!designate: ^7%s^7 has been designated by ^7%s\n\"",
-      vic->client->pers.netname,
-      ( ent ) ? G_admin_adminPrintName( ent ) : "console" ) );
+    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;
 }
 
@@ -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" ) )
   {
@@ -1816,17 +2110,54 @@ void Cmd_CallVote_f( gentity_t *ent )
     Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ),
         "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
@@ -2721,6 +2979,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
@@ -2772,6 +3084,179 @@ void CheckCvars( void )
   level.frameMsec = trap_Milliseconds( );
 }
 
+/*
+==================
+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:
@@ -938,6 +856,51 @@ qboolean G_MapRotationActive( void )
   return ( g_currentMapRotation.integer != NOT_ROTATING );
 }
 
+/*
+===============
+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
@@ -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
-- 
cgit