]> git.pld-linux.org Git - packages/tremulous.git/commitdiff
- very popupar patch by TJW: http://tjw.org/tremulous/tremulous-svn823.patch
authorsparky <sparky@pld-linux.org>
Tue, 2 Sep 2008 23:06:50 +0000 (23:06 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Changed files:
    tremulous-backport.patch -> 1.1

tremulous-backport.patch [new file with mode: 0644]

diff --git a/tremulous-backport.patch b/tremulous-backport.patch
new file mode 100644 (file)
index 0000000..9f7ab34
--- /dev/null
@@ -0,0 +1,2047 @@
+Index: src/server/sv_client.c
+===================================================================
+--- src/server/sv_client.c     (revision 823)
++++ src/server/sv_client.c     (working copy)
+@@ -460,7 +460,11 @@
+       sharedEntity_t *ent;
+       Com_DPrintf( "Going from CS_PRIMED to CS_ACTIVE for %s\n", client->name );
++
+       client->state = CS_ACTIVE;
++      // resend all configstrings using the cs commands since these are
++      // no longer sent when the client is CS_PRIMED
++      SV_UpdateConfigstrings( client );
+       // set up the entity for the client
+       clientNum = client - svs.clients;
+Index: src/server/server.h
+===================================================================
+--- src/server/server.h        (revision 823)
++++ src/server/server.h        (working copy)
+@@ -169,6 +169,7 @@
+       netchan_buffer_t **netchan_end_queue;
+       int                             oldServerTime;
++      qboolean                        csupdated[MAX_CONFIGSTRINGS+1]; 
+ } client_t;
+ //=============================================================================
+@@ -227,6 +228,8 @@
+ extern        cvar_t  *sv_rconPassword;
+ extern        cvar_t  *sv_privatePassword;
+ extern        cvar_t  *sv_allowDownload;
++extern        cvar_t  *sv_wwwDownload;
++extern        cvar_t  *sv_wwwBaseURL;
+ extern        cvar_t  *sv_maxclients;
+ extern        cvar_t  *sv_privateClients;
+@@ -272,6 +275,7 @@
+ //
+ void SV_SetConfigstring( int index, const char *val );
+ void SV_GetConfigstring( int index, char *buffer, int bufferSize );
++void SV_UpdateConfigstrings( client_t *client );
+ void SV_SetUserinfo( int index, const char *val );
+ void SV_GetUserinfo( int index, char *buffer, int bufferSize );
+Index: src/server/sv_init.c
+===================================================================
+--- src/server/sv_init.c       (revision 823)
++++ src/server/sv_init.c       (working copy)
+@@ -23,15 +23,62 @@
+ #include "server.h"
++
+ /*
+ ===============
++SV_SendConfigstring
++===============
++*/
++static void SV_SendConfigString(client_t *client, int index)
++{
++      int maxChunkSize = MAX_STRING_CHARS - 24;
++      int len;
++
++      if(!sv.configstrings[index][0])
++              return;
++
++      len = strlen(sv.configstrings[index]);
++
++      if( len >= maxChunkSize ) {
++              int             sent = 0;
++              int             remaining = len;
++              char    *cmd;
++              char    buf[MAX_STRING_CHARS];
++
++              while (remaining > 0 ) {
++                      if ( sent == 0 ) {
++                              cmd = "bcs0";
++                      }
++                      else if( remaining < maxChunkSize ) {
++                              cmd = "bcs2";
++                      }
++                      else {
++                              cmd = "bcs1";
++                      }
++                      Q_strncpyz( buf, &sv.configstrings[index][sent],
++                              maxChunkSize );
++
++                      SV_SendServerCommand( client, "%s %i \"%s\"\n", cmd,
++                              index, buf );
++
++                      sent += (maxChunkSize - 1);
++                      remaining -= (maxChunkSize - 1);
++              }
++      } else {
++              // standard cs, just send it
++              SV_SendServerCommand( client, "cs %i \"%s\"\n", index,
++                      sv.configstrings[index] );
++      }
++}
++
++/*
++===============
+ SV_SetConfigstring
+ ===============
+ */
+ void SV_SetConfigstring (int index, const char *val) {
+       int             len, i;
+-      int             maxChunkSize = MAX_STRING_CHARS - 24;
+       client_t        *client;
+       if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
+@@ -57,48 +104,41 @@
+               // send the data to all relevent clients
+               for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) {
+-                      if ( client->state < CS_PRIMED ) {
++                      if ( client->state < CS_ACTIVE ) {
++                              client->csupdated[index] = qtrue;
+                               continue;
+                       }
+                       // do not always send server info to all clients
+                       if ( index == CS_SERVERINFO && client->gentity && (client->gentity->r.svFlags & SVF_NOSERVERINFO) ) {
+                               continue;
+                       }
++              
+                       len = strlen( val );
+-                      if( len >= maxChunkSize ) {
+-                              int             sent = 0;
+-                              int             remaining = len;
+-                              char    *cmd;
+-                              char    buf[MAX_STRING_CHARS];
++                      SV_SendConfigString(client, index);
++              }
++      }
++}
+-                              while (remaining > 0 ) {
+-                                      if ( sent == 0 ) {
+-                                              cmd = "bcs0";
+-                                      }
+-                                      else if( remaining < maxChunkSize ) {
+-                                              cmd = "bcs2";
+-                                      }
+-                                      else {
+-                                              cmd = "bcs1";
+-                                      }
+-                                      Q_strncpyz( buf, &val[sent], maxChunkSize );
++void SV_UpdateConfigstrings(client_t *client)
++{
++      int index;
+-                                      SV_SendServerCommand( client, "%s %i \"%s\"\n", cmd, index, buf );
++      for( index = 0; index <= MAX_CONFIGSTRINGS; index++ ) {
++              // if the CS hasn't changed since we went to CS_PRIMED, ignore
++              if(!client->csupdated[index])
++                      continue;
+-                                      sent += (maxChunkSize - 1);
+-                                      remaining -= (maxChunkSize - 1);
+-                              }
+-                      } else {
+-                              // standard cs, just send it
+-                              SV_SendServerCommand( client, "cs %i \"%s\"\n", index, val );
+-                      }
++              // do not always send server info to all clients
++              if ( index == CS_SERVERINFO && client->gentity &&
++                      (client->gentity->r.svFlags & SVF_NOSERVERINFO) ) {
++                      continue;
+               }
++              SV_SendConfigString(client, index);
+       }
+ }
+-
+ /*
+ ===============
+ SV_GetConfigstring
+@@ -561,6 +601,10 @@
+       sv_zombietime = Cvar_Get ("sv_zombietime", "2", CVAR_TEMP );
+       sv_allowDownload = Cvar_Get ("sv_allowDownload", "0", CVAR_SERVERINFO);
++      sv_wwwDownload = Cvar_Get ("sv_wwwDownload", "1",
++              CVAR_SYSTEMINFO|CVAR_ARCHIVE);
++      sv_wwwBaseURL = Cvar_Get ("sv_wwwBaseURL", "",
++              CVAR_SYSTEMINFO|CVAR_ARCHIVE);
+       sv_master[0] = Cvar_Get ("sv_master1", MASTER_SERVER_NAME, 0 );
+       sv_master[1] = Cvar_Get ("sv_master2", "", CVAR_ARCHIVE );
+       sv_master[2] = Cvar_Get ("sv_master3", "", CVAR_ARCHIVE );
+Index: src/server/sv_main.c
+===================================================================
+--- src/server/sv_main.c       (revision 823)
++++ src/server/sv_main.c       (working copy)
+@@ -33,6 +33,8 @@
+ cvar_t        *sv_rconPassword;               // password for remote server commands
+ cvar_t        *sv_privatePassword;    // password for the privateClient slots
+ cvar_t        *sv_allowDownload;
++cvar_t        *sv_wwwBaseURL;
++cvar_t        *sv_wwwDownload;
+ cvar_t        *sv_maxclients;
+ cvar_t        *sv_privateClients;             // number of clients reserved for password
+@@ -129,6 +131,9 @@
+ //            return;
+ //    }
++      if( client->state < CS_ACTIVE )
++              return;
++
+       client->reliableSequence++;
+       // if we would be losing an old command that hasn't been acknowledged,
+       // we must drop the connection
+@@ -186,9 +191,6 @@
+       // send the data to all relevent clients
+       for (j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++) {
+-              if ( client->state < CS_PRIMED ) {
+-                      continue;
+-              }
+               SV_AddServerCommand( client, (char *)message );
+       }
+ }
+Index: src/game/bg_misc.c
+===================================================================
+--- src/game/bg_misc.c (revision 823)
++++ src/game/bg_misc.c (working copy)
+@@ -67,7 +67,8 @@
+     qfalse,                //qboolean  creepTest;
+     ASPAWN_CREEPSIZE,      //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qfalse,                //qboolean  replacable;
+   },
+   {
+     BA_A_BARRICADE,        //int       buildNum;
+@@ -102,7 +103,8 @@
+     qtrue,                 //qboolean  creepTest;
+     BARRICADE_CREEPSIZE,   //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qfalse,                //qboolean  replaceable;
+   },
+   {
+     BA_A_BOOSTER,          //int       buildNum;
+@@ -137,7 +139,8 @@
+     qtrue,                 //qboolean  creepTest;
+     BOOSTER_CREEPSIZE,     //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qtrue,                 //qboolean  replacable;
+   },
+   {
+     BA_A_ACIDTUBE,         //int       buildNum;
+@@ -172,7 +175,8 @@
+     qtrue,                 //qboolean  creepTest;
+     ACIDTUBE_CREEPSIZE,    //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qfalse,                //qboolean  replacable;
+   },
+   {
+     BA_A_HIVE,             //int       buildNum;
+@@ -207,7 +211,8 @@
+     qtrue,                 //qboolean  creepTest;
+     HIVE_CREEPSIZE,        //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qfalse,                //qboolean  replacable;
+   },
+   {
+     BA_A_TRAPPER,          //int       buildNum;
+@@ -242,7 +247,8 @@
+     qtrue,                 //qboolean  creepTest;
+     TRAPPER_CREEPSIZE,     //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qfalse,                //qboolean  replacable;
+   },
+   {
+     BA_A_OVERMIND,         //int       buildNum;
+@@ -277,7 +283,8 @@
+     qfalse,                //qboolean  creepTest;
+     OVERMIND_CREEPSIZE,    //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qtrue                  //qboolean  reactorTest;
++    qtrue,                 //qboolean  reactorTest;
++    qtrue,                 //qboolean  replacable;
+   },
+   {
+     BA_A_HOVEL,            //int       buildNum;
+@@ -312,7 +319,8 @@
+     qtrue,                 //qboolean  creepTest;
+     HOVEL_CREEPSIZE,       //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qtrue                  //qboolean  reactorTest;
++    qtrue,                 //qboolean  reactorTest;
++    qfalse,                //qboolean  replacable;
+   },
+   {
+     BA_H_SPAWN,            //int       buildNum;
+@@ -347,7 +355,8 @@
+     qfalse,                //qboolean  creepTest;
+     0,                     //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qfalse,                //qboolean  replacable;
+   },
+   {
+     BA_H_MEDISTAT,         //int       buildNum;
+@@ -382,7 +391,8 @@
+     qfalse,                //qboolean  creepTest;
+     0,                     //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qtrue,                 //qboolean  replacable;
+   },
+   {
+     BA_H_MGTURRET,         //int       buildNum;
+@@ -419,7 +429,8 @@
+     qfalse,                //qboolean  creepTest;
+     0,                     //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qfalse,                //qboolean  replacable;
+   },
+   {
+     BA_H_TESLAGEN,         //int       buildNum;
+@@ -454,7 +465,8 @@
+     qfalse,                //qboolean  creepTest;
+     0,                     //int       creepSize;
+     qtrue,                 //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qfalse,                //qboolean  replacable;
+   },
+   {
+     BA_H_DCC,              //int       buildNum;
+@@ -489,7 +501,8 @@
+     qfalse,                //qboolean  creepTest;
+     0,                     //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qtrue,                 //qboolean  replacable;
+   },
+   {
+     BA_H_ARMOURY,          //int       buildNum;
+@@ -524,7 +537,8 @@
+     qfalse,                //qboolean  creepTest;
+     0,                     //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qtrue,                 //qboolean  replacable;
+   },
+   {
+     BA_H_REACTOR,          //int       buildNum;
+@@ -559,7 +573,8 @@
+     qfalse,                //qboolean  creepTest;
+     0,                     //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qtrue                  //qboolean  reactorTest;
++    qtrue,                 //qboolean  reactorTest;
++    qtrue,                 //qboolean  replacable;
+   },
+   {
+     BA_H_REPEATER,         //int       buildNum;
+@@ -594,7 +609,8 @@
+     qfalse,                //qboolean  creepTest;
+     0,                     //int       creepSize;
+     qfalse,                //qboolean  dccTest;
+-    qfalse                 //qboolean  reactorTest;
++    qfalse,                //qboolean  reactorTest;
++    qtrue,                 //qboolean  replacable;
+   }
+ };
+@@ -1290,6 +1306,25 @@
+ /*
+ ==============
++BG_FindReplaceableTestForBuildable
++==============
++*/
++qboolean BG_FindReplaceableTestForBuildable( int bclass )
++{
++  int i;
++
++  for( i = 0; i < bg_numBuildables; i++ )
++  {
++    if( bg_buildableList[ i ].buildNum == bclass )
++    {
++      return bg_buildableList[ i ].replaceable;
++    }
++  }
++  return qfalse;
++}
++
++/*
++==============
+ BG_FindOverrideForBuildable
+ ==============
+ */
+Index: src/game/tremulous.h
+===================================================================
+--- src/game/tremulous.h       (revision 823)
++++ src/game/tremulous.h       (working copy)
+@@ -306,8 +306,8 @@
+ #define ALIENSENSE_RANGE            1000.0f
+-#define ALIEN_POISON_TIME           10000
+-#define ALIEN_POISON_DMG            30
++#define ALIEN_POISON_TIME           5000
++#define ALIEN_POISON_DMG            5
+ #define ALIEN_POISON_DIVIDER        (1.0f/1.32f) //about 1.0/(time`th root of damage)
+ #define ALIEN_SPAWN_REPEAT_TIME     10000
+@@ -427,9 +427,11 @@
+  */
+ #define LIGHTARMOUR_PRICE           70
++#define LIGHTARMOUR_POISON_PROTECTION 1
+ #define HELMET_PRICE                90
+ #define HELMET_RANGE                1000.0f
++#define HELMET_POISON_PROTECTION    2 
+ #define MEDKIT_PRICE                0
+@@ -443,6 +445,7 @@
+ #define JETPACK_DISABLE_CHANCE      0.3f
+ #define BSUIT_PRICE                 400
++#define BSUIT_POISON_PROTECTION     4
+ #define MGCLIP_PRICE                0
+@@ -450,7 +453,7 @@
+ #define GAS_PRICE                   0
+-#define MEDKIT_POISON_IMMUNITY_TIME 30000
++#define MEDKIT_POISON_IMMUNITY_TIME 0
+ #define MEDKIT_STARTUP_TIME         4000
+ #define MEDKIT_STARTUP_SPEED        5
+@@ -588,3 +591,8 @@
+ #define DAMAGE_FRACTION_FOR_KILL    0.5f //how much damage players (versus structures) need to
+                                          //do to increment the stage kill counters
++
++// g_suddenDeathMode settings
++#define SDMODE_BP                   0 
++#define SDMODE_NO_BUILD             1
++#define SDMODE_SELECTIVE            2
+Index: src/game/g_svcmds.c
+===================================================================
+--- src/game/g_svcmds.c        (revision 823)
++++ src/game/g_svcmds.c        (working copy)
+@@ -600,6 +600,11 @@
+       G_Printf( "cp: %s\n", ConcatArgs( 1 ) );
+       return qtrue;
+     }
++    else if( !Q_stricmp( cmd, "m" ) )
++    {
++      G_PrivateMessage( NULL );
++      return qtrue;
++    }
+     G_Printf( "unknown command: %s\n", cmd );
+     return qtrue;
+Index: src/game/g_local.h
+===================================================================
+--- src/game/g_local.h (revision 823)
++++ src/game/g_local.h (working copy)
+@@ -347,6 +347,14 @@
+   int                 adminLevel;
+ } clientPersistant_t;
++#define MAX_UNLAGGED_MARKERS 10
++typedef struct unlagged_s {
++  vec3_t      origin;
++  vec3_t      mins;
++  vec3_t      maxs;
++  qboolean    used;
++} unlagged_t;
++
+ // this structure is cleared on each ClientSpawn(),
+ // except for 'client->pers' and 'client->sess'
+ struct gclient_s
+@@ -438,6 +446,12 @@
+ #define RAM_FRAMES  1                       // number of frames to wait before retriggering
+   int                 retriggerArmouryMenu; // frame number to retrigger the armoury menu
++
++  unlagged_t          unlaggedHist[ MAX_UNLAGGED_MARKERS ];
++  unlagged_t          unlaggedBackup;
++  unlagged_t          unlaggedCalc;
++  int                 unlaggedTime;
++
+ };
+@@ -609,6 +623,9 @@
+   pTeam_t           lastWin;
++  int               suddenDeathABuildPoints;
++  int               suddenDeathHBuildPoints;
++  qboolean          suddenDeath;
+   timeWarning_t     suddenDeathWarning;
+   timeWarning_t     timelimitWarning;
+@@ -624,6 +641,9 @@
+   qboolean          uncondHumanWin;
+   qboolean          alienTeamLocked;
+   qboolean          humanTeamLocked;
++
++  int unlaggedIndex;
++  int unlaggedTimes[ MAX_UNLAGGED_MARKERS ];
+ } level_locals_t;
+ //
+@@ -652,6 +672,7 @@
+ void      G_DecolorString( char *in, char *out );
+ void      G_ChangeTeam( gentity_t *ent, pTeam_t newTeam );
+ void      G_SanitiseName( char *in, char *out );
++void      G_PrivateMessage( gentity_t *ent );
+ //
+ // g_physics.c
+@@ -908,6 +929,11 @@
+ //
+ // g_active.c
+ //
++void G_UnlaggedStore( void );
++void G_UnlaggedClear( gentity_t *ent );
++void G_UnlaggedCalc( int time, gentity_t *skipEnt );
++void G_UnlaggedOn( vec3_t muzzle, float range );
++void G_UnlaggedOff( void );
+ void ClientThink( int clientNum );
+ void ClientEndFrame( gentity_t *ent );
+ void G_RunClient( gentity_t *ent );
+@@ -1045,6 +1071,7 @@
+ extern  vmCvar_t  g_timelimit;
+ extern  vmCvar_t  g_suddenDeathTime;
++extern  vmCvar_t  g_suddenDeathMode;
+ extern  vmCvar_t  g_friendlyFire;
+ extern  vmCvar_t  g_friendlyFireHumans;
+ extern  vmCvar_t  g_friendlyFireAliens;
+@@ -1094,6 +1121,8 @@
+ extern  vmCvar_t  g_alienStage2Threshold;
+ extern  vmCvar_t  g_alienStage3Threshold;
++extern  vmCvar_t  g_unlagged;
++
+ extern  vmCvar_t  g_disabledEquipment;
+ extern  vmCvar_t  g_disabledClasses;
+ extern  vmCvar_t  g_disabledBuildables;
+@@ -1112,6 +1141,9 @@
+ extern  vmCvar_t  g_adminNameProtect;
+ extern  vmCvar_t  g_adminTempBan;
++extern  vmCvar_t  g_privateMessages;
++extern  vmCvar_t  g_dretchPunt;
++
+ void      trap_Printf( const char *fmt );
+ void      trap_Error( const char *fmt );
+ int       trap_Milliseconds( void );
+Index: src/game/g_combat.c
+===================================================================
+--- src/game/g_combat.c        (revision 823)
++++ src/game/g_combat.c        (working copy)
+@@ -41,9 +41,6 @@
+   if( !ent->client )
+     return;
+-  if( ent->client->sess.sessionTeam == TEAM_SPECTATOR )
+-    return;
+-
+   // no scoring during pre-match warmup
+   if( level.warmupTime )
+     return;
+@@ -141,6 +138,7 @@
+   char      *killerName, *obit;
+   float     totalDamage = 0.0f;
+   gentity_t *player;
++  qboolean  tk = qfalse;
+   if( self->client->ps.pm_type == PM_DEAD )
+@@ -169,7 +167,11 @@
+     killer = attacker->s.number;
+     if( attacker->client )
++    {
+       killerName = attacker->client->pers.netname;
++      tk = ( attacker != self && attacker->client->ps.stats[ STAT_PTEAM ] 
++        == self->client->ps.stats[ STAT_PTEAM ] );
++    }
+     else
+       killerName = "<non-client>";
+   }
+@@ -202,11 +204,24 @@
+     BG_DeactivateUpgrade( i, self->client->ps.stats );
+   // broadcast the death event to everyone
+-  ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
+-  ent->s.eventParm = meansOfDeath;
+-  ent->s.otherEntityNum = self->s.number;
+-  ent->s.otherEntityNum2 = killer;
+-  ent->r.svFlags = SVF_BROADCAST; // send to everyone
++  if( !tk )
++  {
++    ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
++    ent->s.eventParm = meansOfDeath;
++    ent->s.otherEntityNum = self->s.number;
++    ent->s.otherEntityNum2 = killer;
++    ent->r.svFlags = SVF_BROADCAST; // send to everyone
++  }
++  else 
++  {
++    // tjw: obviously this is a hack and belongs in the client, but
++    //      this works as a temporary fix.
++    trap_SendServerCommand( -1,
++      va( "print \"%s^7 was killed by ^1TEAMMATE^7 %s\n\"",
++      self->client->pers.netname, attacker->client->pers.netname ) );
++    trap_SendServerCommand( attacker - g_entities,
++      va( "cp \"You killed ^1TEAMMATE^7 %s\"", self->client->pers.netname ) );
++  }
+   self->enemy = attacker;
+@@ -1021,8 +1036,20 @@
+     // if the attacker was on the same team
+     if( targ != attacker && OnSameTeam( targ, attacker ) )
+     {
+-      if( !g_friendlyFire.integer )
++      if( g_dretchPunt.integer &&
++        targ->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL0 )
+       {
++        vec3_t dir, push;
++
++        VectorSubtract( targ->r.currentOrigin, attacker->r.currentOrigin, dir );
++        VectorNormalizeFast( dir );
++        VectorScale( dir, ( damage * 10.0f ), push );
++        push[2] = 64.0f;
++        VectorAdd( targ->client->ps.velocity, push, targ->client->ps.velocity );
++        return;
++      }
++      else if( !g_friendlyFire.integer )
++      {
+         if( !g_friendlyFireHumans.integer 
+           && targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+         {
+@@ -1097,8 +1124,8 @@
+     //if boosted poison every attack
+     if( attacker->client && attacker->client->ps.stats[ STAT_STATE ] & SS_BOOSTED )
+     {
+-      if( !( targ->client->ps.stats[ STAT_STATE ] & SS_POISONED ) &&
+-          !BG_InventoryContainsUpgrade( UP_BATTLESUIT, targ->client->ps.stats ) &&
++      if( targ->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS &&
++          !( targ->client->ps.stats[ STAT_STATE ] & SS_POISONED ) &&
+           mod != MOD_LEVEL2_ZAP &&
+           targ->client->poisonImmunityTime < level.time )
+       {
+Index: src/game/g_active.c
+===================================================================
+--- src/game/g_active.c        (revision 823)
++++ src/game/g_active.c        (working copy)
+@@ -217,6 +217,13 @@
+     other = &g_entities[ pm->touchents[ i ] ];
++    // see G_UnlaggedDetectCollisions(), this is the inverse of that.
++    // if our movement is blocked by another player's real position,
++    // don't use the unlagged position for them because they are 
++    // blocking or server-side Pmove() from reaching it
++    if( other->client && other->client->unlaggedCalc.used )
++      other->client->unlaggedCalc.used = qfalse;
++
+     //charge attack
+     if( ent->client->ps.weapon == WP_ALEVEL4 &&
+         ent->client->ps.stats[ STAT_MISC ] > 0 &&
+@@ -683,22 +690,17 @@
+     //client is poisoned
+     if( client->ps.stats[ STAT_STATE ] & SS_POISONED )
+     {
+-      int i;
+-      int seconds = ( ( level.time - client->lastPoisonTime ) / 1000 ) + 1;
+-      int damage = ALIEN_POISON_DMG, damage2 = 0;
++      int damage = ALIEN_POISON_DMG; 
++   
++      if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, client->ps.stats ) )
++        damage -= BSUIT_POISON_PROTECTION;
++      if( BG_InventoryContainsUpgrade( UP_HELMET, client->ps.stats ) )
++        damage -= HELMET_POISON_PROTECTION;
++      if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) )
++        damage -= LIGHTARMOUR_POISON_PROTECTION;
+-      for( i = 0; i < seconds; i++ )
+-      {
+-        if( i == seconds - 1 )
+-          damage2 = damage;
+-
+-        damage *= ALIEN_POISON_DIVIDER;
+-      }
+-
+-      damage = damage2 - damage;
+-
+-      G_Damage( ent, client->lastPoisonClient, client->lastPoisonClient, NULL, NULL,
+-                damage, 0, MOD_POISON );
++      G_Damage( ent, client->lastPoisonClient, client->lastPoisonClient, NULL,
++        0, damage, 0, MOD_POISON );
+     }
+     //replenish alien health
+@@ -901,6 +903,288 @@
+ /*
+ ==============
++ G_UnlaggedStore
++
++ Called on every server frame.  Stores position data for the client at that 
++ into client->unlaggedHist[] and the time into level.unlaggedTimes[].  
++ This data is used by G_UnlaggedCalc()
++==============
++*/
++void G_UnlaggedStore( void )
++{
++  int i = 0;
++  gentity_t *ent;
++  unlagged_t *save;
++  
++  if( !g_unlagged.integer )
++    return;
++  level.unlaggedIndex++; 
++  if( level.unlaggedIndex >= MAX_UNLAGGED_MARKERS )
++    level.unlaggedIndex = 0;
++
++  level.unlaggedTimes[ level.unlaggedIndex ] = level.time;
++ 
++  for( i = 0; i < level.maxclients; i++ )
++  {
++    ent = &g_entities[ i ];
++    save = &ent->client->unlaggedHist[ level.unlaggedIndex ];
++    save->used = qfalse; 
++    if( !ent->r.linked || !( ent->r.contents & CONTENTS_BODY ) )
++      continue;
++    if( ent->client->pers.connected != CON_CONNECTED )
++      continue; 
++    VectorCopy( ent->r.mins, save->mins );
++    VectorCopy( ent->r.maxs, save->maxs );
++    VectorCopy( ent->s.pos.trBase, save->origin );
++    save->used = qtrue;
++  }
++}
++
++/*
++==============
++ G_UnlaggedClear
++ 
++ Mark all unlaggedHist[] markers for this client invalid.  Useful for
++ preventing teleporting and death.
++==============
++*/
++void G_UnlaggedClear( gentity_t *ent )
++{
++  int i;
++
++  for( i = 0; i < MAX_UNLAGGED_MARKERS; i++ )
++    ent->client->unlaggedHist[ i ].used = qfalse;
++}
++
++/*
++==============
++ G_UnlaggedCalc
++
++ Loops through all active clients and calculates their predicted position
++ for time then stores it in client->unlaggedCalc
++==============
++*/
++void G_UnlaggedCalc( int time, gentity_t *rewindEnt )
++{
++  int i = 0;
++  gentity_t *ent;
++  int startIndex = level.unlaggedIndex;
++  int stopIndex = -1;
++  int frameMsec = 0;
++  float lerp = 0.5f;
++
++  if( !g_unlagged.integer )
++    return;
++ 
++  // clear any calculated values from a previous run
++  for( i = 0; i < level.maxclients; i++ )
++  {
++    ent = &g_entities[ i ];
++    ent->client->unlaggedCalc.used = qfalse;
++  }
++
++  for( i = 0; i < MAX_UNLAGGED_MARKERS; i++ )
++  {
++    if( level.unlaggedTimes[ startIndex ] <= time )
++      break;
++    stopIndex = startIndex;
++    if( --startIndex < 0 )
++      startIndex = MAX_UNLAGGED_MARKERS - 1;
++  }
++  if( i == MAX_UNLAGGED_MARKERS )
++  {
++    // if we searched all markers and the oldest one still isn't old enough
++    // just use the oldest marker with no lerping
++    lerp = 0.0f;
++  }
++
++  // client is on the current frame, no need for unlagged
++  if( stopIndex == -1 )
++    return;
++
++  // lerp between two markers
++  frameMsec = level.unlaggedTimes[ stopIndex ] -
++    level.unlaggedTimes[ startIndex ];
++  if( frameMsec > 0 )
++  {
++    lerp = ( float )( time - level.unlaggedTimes[ startIndex ] )
++      / ( float )frameMsec; 
++  }
++  
++  for( i = 0; i < level.maxclients; i++ )
++  {
++    ent = &g_entities[ i ];
++    if( ent->s.number == rewindEnt->s.number )
++      continue;
++    if( !ent->r.linked || !( ent->r.contents & CONTENTS_BODY ) )
++      continue;
++    if( ent->client->pers.connected != CON_CONNECTED )
++      continue;
++    if( !ent->client->unlaggedHist[ startIndex ].used )
++      continue;
++    if( !ent->client->unlaggedHist[ stopIndex ].used )
++      continue;
++
++    // between two unlagged markers
++    VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].mins,
++      ent->client->unlaggedHist[ stopIndex ].mins,
++      ent->client->unlaggedCalc.mins );
++    VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].maxs,
++      ent->client->unlaggedHist[ stopIndex ].maxs,
++      ent->client->unlaggedCalc.maxs );
++    VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].origin,
++      ent->client->unlaggedHist[ stopIndex ].origin,
++      ent->client->unlaggedCalc.origin );
++
++    ent->client->unlaggedCalc.used = qtrue;
++  }
++}
++
++/*
++==============
++ G_UnlaggedOff
++
++ Reverses the changes made to all active clients by G_UnlaggedOn()
++==============
++*/
++void G_UnlaggedOff( void )
++{
++  int i = 0;
++  gentity_t *ent;
++  
++  if( !g_unlagged.integer )
++    return;
++  
++  for( i = 0; i < level.maxclients; i++ )
++  {
++    ent = &g_entities[ i ];
++    if( !ent->client->unlaggedBackup.used )
++      continue;
++    VectorCopy( ent->client->unlaggedBackup.mins, ent->r.mins );
++    VectorCopy( ent->client->unlaggedBackup.maxs, ent->r.maxs );
++    VectorCopy( ent->client->unlaggedBackup.origin, ent->r.currentOrigin );
++    ent->client->unlaggedBackup.used = qfalse;
++    trap_LinkEntity( ent );
++  }
++}
++
++/*
++==============
++ G_UnlaggedOn
++
++ Called after G_UnlaggedCalc() to apply the calculated values to all active
++ clients.  Once finished tracing, G_UnlaggedOff() must be called to restore
++ the clients' position data
++
++ As an optimization, all clients that have an unlagged position that is
++ not touchable at "range" from "muzzle" will be ignored.  This is required
++ to prevent a huge amount of trap_LinkEntity() calls per user cmd.
++==============
++*/
++
++void G_UnlaggedOn( vec3_t muzzle, float range )
++{
++  int i = 0;
++  gentity_t *ent;
++  unlagged_t *calc;
++  
++  if( !g_unlagged.integer )
++    return;
++  
++  for( i = 0; i < level.maxclients; i++ )
++  {
++    ent = &g_entities[ i ];
++    calc = &ent->client->unlaggedCalc;
++
++    if( !calc->used )
++      continue;
++    if( ent->client->unlaggedBackup.used )
++      continue;
++    if( !ent->r.linked || !( ent->r.contents & CONTENTS_BODY ) )
++      continue;
++    if( VectorCompare( ent->r.currentOrigin, calc->origin ) )
++      continue;
++    if( muzzle )
++    {
++      float r1 = Distance( calc->origin, calc->maxs );
++      float r2 = Distance( calc->origin, calc->mins );
++      float maxRadius = ( r1 > r2 ) ? r1 : r2;
++
++      if( Distance( muzzle, calc->origin ) > range + maxRadius )
++        continue; 
++    }
++
++    // create a backup of the real positions
++    VectorCopy( ent->r.mins, ent->client->unlaggedBackup.mins );
++    VectorCopy( ent->r.maxs, ent->client->unlaggedBackup.maxs );
++    VectorCopy( ent->r.currentOrigin, ent->client->unlaggedBackup.origin );
++    ent->client->unlaggedBackup.used = qtrue;
++
++    // move the client to the calculated unlagged position
++    VectorCopy( calc->mins, ent->r.mins );
++    VectorCopy( calc->maxs, ent->r.maxs );
++    VectorCopy( calc->origin, ent->r.currentOrigin );
++    trap_LinkEntity( ent );
++  }
++}
++/*
++==============
++ G_UnlaggedDetectCollisions
++
++ cgame prediction will predict a client's own position all the way up to
++ the current time, but only updates other player's positions up to the
++ postition sent in the most recent snapshot.
++
++ This allows player X to essentially "move through" the position of player Y 
++ when player X's cmd is processed with Pmove() on the server.  This is because
++ player Y was clipping player X's Pmove() on his client, but when the same
++ cmd is processed with Pmove on the server it is not clipped.
++
++ Long story short (too late): don't use unlagged positions for players who
++ were blocking this player X's client-side Pmove().  This makes the assumption
++ that if player X's movement was blocked in the client he's going to still
++ be up against player Y when the Pmove() is run on the server with the
++ same cmd.
++
++ NOTE: this must be called after Pmove() and G_UnlaggedCalc()
++==============
++*/
++static void G_UnlaggedDetectCollisions( gentity_t *ent )
++{
++  unlagged_t *calc;
++  trace_t tr;
++  float r1, r2;
++  float range;
++
++  if( !g_unlagged.integer )
++    return;
++
++  calc = &ent->client->unlaggedCalc;
++
++  // if the client isn't moving, this is not necessary
++  if( VectorCompare( ent->client->oldOrigin, ent->client->ps.origin ) )
++    return;
++
++  range = Distance( ent->client->oldOrigin, ent->client->ps.origin );
++
++  // increase the range by the player's largest possible radius since it's
++  // the players bounding box that collides, not their origin
++  r1 = Distance( calc->origin, calc->mins );
++  r2 = Distance( calc->origin, calc->maxs );
++  range += ( r1 > r2 ) ? r1 : r2;
++
++  G_UnlaggedOn( ent->client->oldOrigin, range );
++
++  trap_Trace(&tr, ent->client->oldOrigin, ent->r.mins, ent->r.maxs,
++    ent->client->ps.origin, ent->s.number,  MASK_PLAYERSOLID );
++  if( tr.entityNum >= 0 && tr.entityNum < MAX_CLIENTS )
++    g_entities[ tr.entityNum ].client->unlaggedCalc.used = qfalse; 
++
++  G_UnlaggedOff( );
++}
++
++/*
++==============
+ ClientThink
+ This will be called once for each client frame, which will
+@@ -949,6 +1233,9 @@
+   if( msec > 200 )
+     msec = 200;
++  client->unlaggedTime = ucmd->serverTime;
++  client->unlaggedTime += ( level.time - level.previousTime );
++
+   if( pmove_msec.integer < 8 )
+     trap_Cvar_Set( "pmove_msec", "8" );
+   else if( pmove_msec.integer > 33 )
+@@ -986,6 +1273,9 @@
+   if( !ClientInactivityTimer( client ) )
+     return;
++  // calculate where ent is currently seeing all the other active clients 
++  G_UnlaggedCalc( ent->client->unlaggedTime, ent );
++
+   if( client->noclip )
+     client->ps.pm_type = PM_NOCLIP;
+   else if( client->ps.stats[ STAT_HEALTH ] <= 0 )
+@@ -1161,6 +1451,8 @@
+   Pmove( &pm );
++  G_UnlaggedDetectCollisions( ent );
++
+   // save results of pmove
+   if( ent->client->ps.eventSequence != oldEventSequence )
+     ent->eventTime = level.time;
+@@ -1186,6 +1478,9 @@
+   ent->waterlevel = pm.waterlevel;
+   ent->watertype = pm.watertype;
++  // touch other objects
++  ClientImpacts( ent, &pm );
++  
+   // execute client events
+   ClientEvents( ent, oldEventSequence );
+@@ -1196,9 +1491,6 @@
+   VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
+   VectorCopy( ent->client->ps.origin, ent->s.origin );
+-  // touch other objects
+-  ClientImpacts( ent, &pm );
+-
+   // save results of triggers and client events
+   if( ent->client->ps.eventSequence != oldEventSequence )
+     ent->eventTime = level.time;
+@@ -1225,6 +1517,7 @@
+         //prevent lerping
+         client->ps.eFlags ^= EF_TELEPORT_BIT;
+         client->ps.eFlags &= ~EF_NODRAW;
++        G_UnlaggedClear( ent );
+         //client leaves hovel
+         client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING;
+Index: src/game/g_buildable.c
+===================================================================
+--- src/game/g_buildable.c     (revision 823)
++++ src/game/g_buildable.c     (working copy)
+@@ -23,6 +23,9 @@
+ #include "g_local.h"
++// from g_combat.c
++extern char *modNames[ ];
++
+ /*
+ ================
+ G_setBuildableAnim
+@@ -628,12 +631,27 @@
+   self->s.eFlags &= ~EF_FIRING; //prevent any firing effects
+-  if( attacker && attacker->client && attacker->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
++  if( attacker && attacker->client )
+   {
+-    if( self->s.modelindex == BA_A_OVERMIND )
+-      G_AddCreditToClient( attacker->client, OVERMIND_VALUE, qtrue );
+-    else if( self->s.modelindex == BA_A_SPAWN )
+-      G_AddCreditToClient( attacker->client, ASPAWN_VALUE, qtrue );
++    if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
++    {
++      if( self->s.modelindex == BA_A_OVERMIND )
++        G_AddCreditToClient( attacker->client, OVERMIND_VALUE, qtrue );
++      else if( self->s.modelindex == BA_A_SPAWN )
++        G_AddCreditToClient( attacker->client, ASPAWN_VALUE, qtrue );
++    }
++    else
++    {
++      G_TeamCommand( PTE_ALIENS,
++        va( "print \"%s ^3DESTROYED^7 by teammate %s^7\n\"",
++          BG_FindHumanNameForBuildable( self->s.modelindex ), 
++          attacker->client->pers.netname ) );
++    }
++    G_LogPrintf("Decon: %i %i %i: %s^7 destroyed %s by %s\n",
++      attacker->client->ps.clientNum, self->s.modelindex, mod,
++      attacker->client->pers.netname, 
++      BG_FindNameForBuildable( self->s.modelindex ),
++      modNames[ mod ] );
+   }
+ }
+@@ -840,6 +858,22 @@
+     self->nextthink = level.time + 5000;
+   else
+     self->nextthink = level.time; //blast immediately
++
++  if( attacker && attacker->client )
++  {
++    if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
++    {
++      G_TeamCommand( PTE_ALIENS,
++        va( "print \"%s ^3DESTROYED^7 by teammate %s^7\n\"",
++          BG_FindHumanNameForBuildable( self->s.modelindex ), 
++          attacker->client->pers.netname ) );
++    }
++    G_LogPrintf("Decon: %i %i %i: %s^7 destroyed %s by %s\n",
++      attacker->client->ps.clientNum, self->s.modelindex, mod,
++      attacker->client->pers.netname, 
++      BG_FindNameForBuildable( self->s.modelindex ),
++      modNames[ mod ] );
++  }
+ }
+ /*
+@@ -1156,6 +1190,7 @@
+       //prevent lerping
+       activator->client->ps.eFlags ^= EF_TELEPORT_BIT;
+       activator->client->ps.eFlags |= EF_NODRAW;
++      G_UnlaggedClear( activator );
+       activator->client->ps.stats[ STAT_STATE ] |= SS_HOVELING;
+       activator->client->hovel = self;
+@@ -1241,6 +1276,7 @@
+     //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 );
+@@ -1252,6 +1288,22 @@
+   self->r.contents = 0;    //stop collisions...
+   trap_LinkEntity( self ); //...requires a relink
++
++  if( attacker && attacker->client )
++  {
++    if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
++    {
++      G_TeamCommand( PTE_ALIENS,
++        va( "print \"%s ^3DESTROYED^7 by teammate %s^7\n\"",
++          BG_FindHumanNameForBuildable( self->s.modelindex ), 
++          attacker->client->pers.netname ) );
++    }
++    G_LogPrintf("Decon: %i %i %i: %s^7 destroyed %s by %s\n",
++      attacker->client->ps.clientNum, self->s.modelindex, mod,
++      attacker->client->pers.netname, 
++      BG_FindNameForBuildable( self->s.modelindex ),
++      modNames[ mod ] );
++  }
+ }
+@@ -2193,12 +2245,27 @@
+     self->nextthink = level.time; //blast immediately
+   }
+-  if( attacker && attacker->client && attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
++  if( attacker && attacker->client )
+   {
+-    if( self->s.modelindex == BA_H_REACTOR )
+-      G_AddCreditToClient( attacker->client, REACTOR_VALUE, qtrue );
+-    else if( self->s.modelindex == BA_H_SPAWN )
+-      G_AddCreditToClient( attacker->client, HSPAWN_VALUE, qtrue );
++    if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
++    {
++      if( self->s.modelindex == BA_H_REACTOR )
++        G_AddCreditToClient( attacker->client, REACTOR_VALUE, qtrue );
++      else if( self->s.modelindex == BA_H_SPAWN )
++        G_AddCreditToClient( attacker->client, HSPAWN_VALUE, qtrue );
++    }
++    else
++    {
++      G_TeamCommand( PTE_HUMANS,
++        va( "print \"%s ^3DESTROYED^7 by teammate %s^7\n\"",
++          BG_FindHumanNameForBuildable( self->s.modelindex ), 
++          attacker->client->pers.netname ) );
++    }
++    G_LogPrintf("Decon: %i %i %i: %s^7 destroyed %s by %s\n",
++      attacker->client->ps.clientNum, self->s.modelindex, mod,
++      attacker->client->pers.netname, 
++      BG_FindNameForBuildable( self->s.modelindex ),
++      modNames[ mod ] );
+   }
+ }
+Index: src/game/g_main.c
+===================================================================
+--- src/game/g_main.c  (revision 823)
++++ src/game/g_main.c  (working copy)
+@@ -42,6 +42,7 @@
+ vmCvar_t  g_fraglimit;
+ vmCvar_t  g_timelimit;
+ vmCvar_t  g_suddenDeathTime;
++vmCvar_t  g_suddenDeathMode;
+ vmCvar_t  g_capturelimit;
+ vmCvar_t  g_friendlyFire;
+ vmCvar_t  g_friendlyFireAliens;
+@@ -103,6 +104,8 @@
+ vmCvar_t  g_alienStage2Threshold;
+ vmCvar_t  g_alienStage3Threshold;
++vmCvar_t  g_unlagged;
++
+ vmCvar_t  g_disabledEquipment;
+ vmCvar_t  g_disabledClasses;
+ vmCvar_t  g_disabledBuildables;
+@@ -121,6 +124,10 @@
+ vmCvar_t  g_adminNameProtect;
+ vmCvar_t  g_adminTempBan;
++vmCvar_t  g_privateMessages;
++
++vmCvar_t  g_dretchPunt;
++
+ static cvarTable_t   gameCvarTable[ ] =
+ {
+   // don't override the cheat state set by the system
+@@ -141,6 +148,7 @@
+   // change anytime vars
+   { &g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
+   { &g_suddenDeathTime, "g_suddenDeathTime", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
++  { &g_suddenDeathMode, "g_suddenDeathMode", "0", CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
+   { &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse  },
+@@ -206,6 +214,8 @@
+   { &g_alienMaxStage, "g_alienMaxStage", DEFAULT_ALIEN_MAX_STAGE, 0, 0, qfalse  },
+   { &g_alienStage2Threshold, "g_alienStage2Threshold", DEFAULT_ALIEN_STAGE2_THRESH, 0, 0, qfalse  },
+   { &g_alienStage3Threshold, "g_alienStage3Threshold", DEFAULT_ALIEN_STAGE3_THRESH, 0, 0, qfalse  },
++  
++  { &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  },
+@@ -226,6 +236,10 @@
+   { &g_adminNameProtect, "g_adminNameProtect", "1", CVAR_ARCHIVE, 0, qfalse  },
+   { &g_adminTempBan, "g_adminTempBan", "120", CVAR_ARCHIVE, 0, qfalse  },
+   
++  { &g_privateMessages, "g_privateMessages", "1", CVAR_ARCHIVE, 0, qfalse  },
++  
++  { &g_dretchPunt, "g_dretchPunt", "0", CVAR_ARCHIVE, 0, qfalse  },
++  
+   { &g_rankings, "g_rankings", "0", 0, 0, qfalse}
+ };
+@@ -985,14 +999,32 @@
+     if( !level.warmupTime &&
+       ( level.time - level.startTime >= g_suddenDeathTime.integer * 60000 ) )
+     {
+-      localHTP = 0;
+-      localATP = 0;
+-
+-      //warn about sudden death
+-      if( level.time - level.startTime >= g_suddenDeathTime.integer * 60000 &&
+-          level.suddenDeathWarning < TW_PASSED )
++      // begin sudden death
++      if( level.suddenDeathWarning < TW_PASSED )
+       {
+         trap_SendServerCommand( -1, "cp \"Sudden Death!\"" );
++        localHTP = 0;
++        localATP = 0;
++        if( g_suddenDeathMode.integer == SDMODE_SELECTIVE ) {
++          for( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ )
++          {
++            if( ent->s.eType != ET_BUILDABLE )
++              continue;
++    
++            if( BG_FindReplaceableTestForBuildable( ent->s.modelindex ) )
++            {
++              int t = BG_FindTeamForBuildable( ent->s.modelindex );
++    
++              if( t == BIT_HUMANS )
++                localHTP += BG_FindBuildPointsForBuildable( ent->s.modelindex );
++              else if( t == BIT_ALIENS )
++                localATP += BG_FindBuildPointsForBuildable( ent->s.modelindex );
++            }
++          }
++        }
++        level.suddenDeathHBuildPoints = localHTP;
++        level.suddenDeathABuildPoints = localATP;
++        level.suddenDeath = qtrue;
+         level.suddenDeathWarning = TW_PASSED;
+       }
+     }
+@@ -1007,6 +1039,12 @@
+       }
+     }
+   }
++
++  if( level.suddenDeath )
++  {
++    localHTP = level.suddenDeathHBuildPoints;
++    localATP = level.suddenDeathABuildPoints;
++  }
+   else
+   {
+     localHTP = g_humanBuildPoints.integer;
+@@ -1037,17 +1075,19 @@
+       if( buildable == BA_A_OVERMIND && ent->spawned && ent->health > 0 )
+         level.overmindPresent = qtrue;
+-      if( BG_FindTeamForBuildable( buildable ) == BIT_HUMANS )
++      if( !level.suddenDeath || BG_FindReplaceableTestForBuildable( buildable ) )
+       {
+-        level.humanBuildPoints -= BG_FindBuildPointsForBuildable( buildable );
+-
+-        if( ent->powered )
+-          level.humanBuildPointsPowered -= BG_FindBuildPointsForBuildable( buildable );
++        if( BG_FindTeamForBuildable( buildable ) == BIT_HUMANS )
++        {
++          level.humanBuildPoints -= BG_FindBuildPointsForBuildable( buildable );
++          if( ent->powered )
++            level.humanBuildPointsPowered -= BG_FindBuildPointsForBuildable( buildable );
++        }
++        else
++        {
++          level.alienBuildPoints -= BG_FindBuildPointsForBuildable( buildable );
++        }
+       }
+-      else
+-      {
+-        level.alienBuildPoints -= BG_FindBuildPointsForBuildable( buildable );
+-      }
+     }
+   }
+@@ -2110,12 +2150,6 @@
+     if( !ent->r.linked && ent->neverFree )
+       continue;
+-    if( ent->s.eType == ET_MISSILE )
+-    {
+-      G_RunMissile( ent );
+-      continue;
+-    }
+-
+     if( ent->s.eType == ET_BUILDABLE )
+     {
+       G_BuildableThink( ent, msec );
+@@ -2155,6 +2189,25 @@
+       ClientEndFrame( ent );
+   }
++  // save position information for all active clients 
++  G_UnlaggedStore( );
++
++  // for missle impacts, move every active client one server frame time back
++  // to compensate for built-in 50ms lag
++  G_UnlaggedCalc( level.previousTime, NULL );
++  G_UnlaggedOn( NULL, 0.0f );
++  for( i = MAX_CLIENTS; i < level.num_entities ; i++)
++  {
++    ent = &g_entities[ i ];
++    if( !ent->inuse )
++      continue;
++    if( ent->freeAfterEvent )
++      continue;
++    if( ent->s.eType == ET_MISSILE )
++      G_RunMissile( ent );
++  }
++  G_UnlaggedOff( );
++
+   end = trap_Milliseconds();
+   //TA:
+Index: src/game/bg_pmove.c
+===================================================================
+--- src/game/bg_pmove.c        (revision 823)
++++ src/game/bg_pmove.c        (working copy)
+@@ -510,12 +510,19 @@
+       pm->ps->weapon != WP_ALEVEL3_UPG )
+     return qfalse;
+-  if( pm->cmd.buttons & BUTTON_ATTACK2 )
++  // we were pouncing, but we've landed  
++  if( pm->ps->groundEntityNum != ENTITYNUM_NONE
++    && ( pm->ps->pm_flags & PMF_CHARGE ) )
+   {
++    pm->ps->weaponTime += LEVEL3_POUNCE_TIME;
+     pm->ps->pm_flags &= ~PMF_CHARGE;
+-    return qfalse;
+   }
++  // we're building up for a pounce
++  if( pm->cmd.buttons & BUTTON_ATTACK2 )
++    return qfalse;
++
++  // already a pounce in progress
+   if( pm->ps->pm_flags & PMF_CHARGE )
+     return qfalse;
+@@ -2705,7 +2712,15 @@
+     return;
+   }
+-  // make weapon function
++  
++  // no bite during pounce
++  if( ( pm->ps->weapon == WP_ALEVEL3 || pm->ps->weapon == WP_ALEVEL3_UPG ) 
++    && ( pm->cmd.buttons & BUTTON_ATTACK )
++    && ( pm->ps->pm_flags & PMF_CHARGE ) )
++  {
++    return;
++  }
++
+   if( pm->ps->weaponTime > 0 )
+     pm->ps->weaponTime -= pml.msec;
+Index: src/game/g_weapon.c
+===================================================================
+--- src/game/g_weapon.c        (revision 823)
++++ src/game/g_weapon.c        (working copy)
+@@ -179,7 +179,10 @@
+   VectorMA( muzzle, range, forward, end );
++  G_UnlaggedOn( muzzle, range );
+   trap_Trace( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT );
++  G_UnlaggedOff( );
++
+   if( tr.surfaceFlags & SURF_NOIMPACT )
+     return;
+@@ -223,7 +226,16 @@
+   VectorMA( end, r, right, end );
+   VectorMA( end, u, up, end );
+-  trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
++  // don't use unlagged if this is not a client (e.g. turret)
++  if( ent->client )
++  {
++    G_UnlaggedOn( muzzle, 8192 * 16 );
++    trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
++    G_UnlaggedOff( );
++  }
++  else
++    trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
++
+   if( tr.surfaceFlags & SURF_NOIMPACT )
+     return;
+@@ -308,8 +320,9 @@
+   SnapVector( tent->s.origin2 );
+   tent->s.eventParm = rand() & 255;    // seed for spread pattern
+   tent->s.otherEntityNum = ent->s.number;
+-
++  G_UnlaggedOn( muzzle, 8192 * 16 );
+   ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent );
++  G_UnlaggedOff();
+ }
+ /*
+@@ -329,7 +342,10 @@
+   VectorMA( muzzle, 8192 * 16, forward, end );
++  G_UnlaggedOn( muzzle, 8192 * 16 );
+   trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
++  G_UnlaggedOff( );
++
+   if( tr.surfaceFlags & SURF_NOIMPACT )
+     return;
+@@ -482,7 +498,10 @@
+   VectorMA( muzzle, 8192 * 16, forward, end );
++  G_UnlaggedOn( muzzle, 8192 * 16 );
+   trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
++  G_UnlaggedOff( );
++
+   if( tr.surfaceFlags & SURF_NOIMPACT )
+     return;
+@@ -534,7 +553,10 @@
+   VectorMA( muzzle, PAINSAW_RANGE, forward, end );
++  G_UnlaggedOn( muzzle, PAINSAW_RANGE );
+   trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
++  G_UnlaggedOff( );
++
+   if( tr.surfaceFlags & SURF_NOIMPACT )
+     return;
+@@ -805,7 +827,9 @@
+   VectorMA( muzzle, LEVEL0_BITE_RANGE, forward, end );
++  G_UnlaggedOn( muzzle, LEVEL0_BITE_RANGE );
+   trap_Trace( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT );
++  G_UnlaggedOff( );
+   if( tr.surfaceFlags & SURF_NOIMPACT )
+     return qfalse;
+@@ -939,6 +963,7 @@
+   VectorAdd( ent->client->ps.origin, range, maxs );
+   VectorSubtract( ent->client->ps.origin, range, mins );
++  G_UnlaggedOn( ent->client->ps.origin, LEVEL1_PCLOUD_RANGE );
+   num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
+   for( i = 0; i < num; i++ )
+   {
+@@ -967,6 +992,7 @@
+       }
+     }
+   }
++  G_UnlaggedOff( );
+ }
+@@ -1231,7 +1257,9 @@
+   VectorMA( muzzle, LEVEL2_AREAZAP_RANGE, forward, end );
++  G_UnlaggedOn( muzzle, LEVEL2_AREAZAP_RANGE );
+   trap_Trace( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT );
++  G_UnlaggedOff( );
+   if( tr.surfaceFlags & SURF_NOIMPACT )
+     return;
+@@ -1291,7 +1319,9 @@
+   VectorMA( muzzle, LEVEL3_POUNCE_RANGE, forward, end );
++  G_UnlaggedOn( muzzle, LEVEL3_POUNCE_RANGE );
+   trap_Trace( &tr, ent->s.origin, mins, maxs, end, ent->s.number, MASK_SHOT );
++  G_UnlaggedOff( );
+   //miss
+   if( tr.fraction >= 1.0 )
+@@ -1323,7 +1353,6 @@
+   G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage,
+       DAMAGE_NO_KNOCKBACK|DAMAGE_NO_LOCDAMAGE, MOD_LEVEL3_POUNCE );
+-  ent->client->ps.weaponTime += LEVEL3_POUNCE_TIME;
+   ent->client->allowedToPounce = qfalse;
+   return qtrue;
+Index: src/game/g_misc.c
+===================================================================
+--- src/game/g_misc.c  (revision 823)
++++ src/game/g_misc.c  (working copy)
+@@ -86,6 +86,7 @@
+   // toggle the teleport bit so the client knows to not lerp
+   player->client->ps.eFlags ^= EF_TELEPORT_BIT;
++  G_UnlaggedClear( player );
+   // set angles
+   SetClientViewAngle( player, angles );
+Index: src/game/g_client.c
+===================================================================
+--- src/game/g_client.c        (revision 823)
++++ src/game/g_client.c        (working copy)
+@@ -89,9 +89,6 @@
+   if( !client )
+     return;
+-  if( client->sess.sessionTeam == TEAM_SPECTATOR )
+-    return;
+-
+   //if we're already at the max and trying to add credit then stop
+   if( cap )
+   {
+@@ -952,8 +949,8 @@
+   char      model[ MAX_QPATH ];
+   char      buffer[ MAX_QPATH ];
+   char      filename[ MAX_QPATH ];
+-  char      oldname[ MAX_STRING_CHARS ];
+-  char      newname[ MAX_STRING_CHARS ];
++  char      oldname[ MAX_NAME_LENGTH ];
++  char      newname[ MAX_NAME_LENGTH ];
+   char      err[ MAX_STRING_CHARS ];
+   qboolean  revertName = qfalse;
+   gclient_t *client;
+@@ -1415,6 +1412,7 @@
+   // toggle the teleport bit so the client knows to not lerp
+   flags = ent->client->ps.eFlags & ( EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED );
+   flags ^= EF_TELEPORT_BIT;
++  G_UnlaggedClear( ent );
+   // clear everything but the persistant data
+Index: src/game/bg_public.h
+===================================================================
+--- src/game/bg_public.h       (revision 823)
++++ src/game/bg_public.h       (working copy)
+@@ -1017,6 +1017,7 @@
+   qboolean  dccTest;
+   qboolean  reactorTest;
++  qboolean  replaceable;
+ } buildableAttributes_t;
+ typedef struct
+@@ -1142,6 +1143,7 @@
+ int       BG_FindCreepSizeForBuildable( int bclass );
+ int       BG_FindDCCTestForBuildable( int bclass );
+ int       BG_FindUniqueTestForBuildable( int bclass );
++qboolean  BG_FindReplaceableTestForBuildable( int bclass );
+ void      BG_InitBuildableOverrides( void );
+ int       BG_FindClassNumForName( char *name );
+Index: src/game/g_cmds.c
+===================================================================
+--- src/game/g_cmds.c  (revision 823)
++++ src/game/g_cmds.c  (working copy)
+@@ -587,57 +587,50 @@
+ {
+   pTeam_t oldTeam = ent->client->pers.teamSelection;
++  if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
++  {
++      G_Printf("stop following me!!!!!\n");
++      G_Printf("score before %d\n", ent->client->ps.persistant[ PERS_SCORE ]);
++      G_StopFollowing( ent );
++      G_Printf("score after %d\n", ent->client->ps.persistant[ PERS_SCORE ]);
++  }
++  else G_Printf("you're not following anybody\n");
++
+   ent->client->pers.teamSelection = newTeam;
+-  if( oldTeam != newTeam )
++  if( oldTeam == newTeam )
++    return;
++
++  if( oldTeam == PTE_ALIENS )
++    G_RemoveFromSpawnQueue( &level.alienSpawnQueue, ent->client->ps.clientNum );
++  else if( oldTeam == PTE_HUMANS )
++    G_RemoveFromSpawnQueue( &level.humanSpawnQueue, ent->client->ps.clientNum );
++
++  if( G_admin_permission( ent, ADMF_TEAMCHANGEFREE ) ||
++    ( ( oldTeam == PTE_HUMANS || oldTeam == PTE_ALIENS )
++    && ( level.time - ent->client->pers.teamChangeTime ) > 60000 ) ) 
+   {
+-    //if the client is in a queue make sure they are removed from it before changing
++    // always save in human credtis
+     if( oldTeam == PTE_ALIENS )
+-      G_RemoveFromSpawnQueue( &level.alienSpawnQueue, ent->client->ps.clientNum );
+-    else if( oldTeam == PTE_HUMANS )
+-      G_RemoveFromSpawnQueue( &level.humanSpawnQueue, ent->client->ps.clientNum );
+-
+-    if( G_admin_permission( ent, ADMF_TEAMCHANGEFREE ) )
+     {
+-      // always save in human credtis
+-      if( oldTeam == PTE_ALIENS )
+-      {
+-        ent->client->ps.persistant[ PERS_CREDIT ] *=
+-          (float)FREEKILL_HUMAN / FREEKILL_ALIEN;
+-      }
+-      if( newTeam == PTE_ALIENS )
+-      {
+-        ent->client->ps.persistant[ PERS_CREDIT ] *=
+-          (float)FREEKILL_ALIEN / FREEKILL_HUMAN;
+-      }
++      ent->client->ps.persistant[ PERS_CREDIT ] *=
++        (float)FREEKILL_HUMAN / FREEKILL_ALIEN;
+     }
+-    else if( ( oldTeam == PTE_HUMANS || oldTeam == PTE_ALIENS )
+-      && ( level.time - ent->client->pers.teamChangeTime ) > 60000 ) 
++    if( newTeam == PTE_ALIENS )
+     {
+-      // Tranfer credits and kills as long as this player has been on the
+-      // same team for at least 1 minute. This is done to provide
+-      // a penalty for switching teams for reconnaissance.
+-      if( oldTeam == PTE_HUMANS )
+-      {
+-        ent->client->ps.persistant[ PERS_CREDIT ] *=
+-          (float)FREEKILL_ALIEN / FREEKILL_HUMAN;
+-      }
+-      else if( oldTeam == PTE_ALIENS )
+-      {
+-        ent->client->ps.persistant[ PERS_CREDIT ] *=
+-          (float)FREEKILL_HUMAN / FREEKILL_ALIEN;
+-      }
++      ent->client->ps.persistant[ PERS_CREDIT ] *=
++        (float)FREEKILL_ALIEN / FREEKILL_HUMAN;
+     }
+-    else
+-    {
+-      ent->client->ps.persistant[ PERS_CREDIT ] = 0;
+-      ent->client->ps.persistant[ PERS_SCORE ] = 0;
+-    }
+-
+-    ent->client->pers.classSelection = PCL_NONE;
+-    ClientSpawn( ent, NULL, NULL, NULL );
+   }
++  else
++  {
++    ent->client->ps.persistant[ PERS_CREDIT ] = 0;
++    ent->client->ps.persistant[ PERS_SCORE ] = 0;
++  }
++  ent->client->pers.classSelection = PCL_NONE;
++  ClientSpawn( ent, NULL, NULL, NULL );
++
+   ent->client->pers.joinedATeam = qtrue;
+   ent->client->pers.teamChangeTime = level.time;
+@@ -819,14 +812,14 @@
+   {
+     default:
+     case SAY_ALL:
+-      G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText );
++      G_LogPrintf( "say: %s^7: %s\n", ent->client->pers.netname, chatText );
+       Com_sprintf( name, sizeof( name ), "%s%s%c%c"EC": ", prefix,
+                    ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
+       color = COLOR_GREEN;
+       break;
+     case SAY_TEAM:
+-      G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText );
++      G_LogPrintf( "sayteam: %s^7: %s\n", ent->client->pers.netname, chatText );
+       if( Team_GetLocationMsg( ent, location, sizeof( location ) ) )
+         Com_sprintf( name, sizeof( name ), EC"(%s%c%c"EC") (%s)"EC": ",
+           ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location );
+@@ -857,10 +850,6 @@
+     return;
+   }
+-  // echo the text to the console
+-  if( g_dedicated.integer )
+-    G_Printf( "%s%s\n", name, text);
+-
+   // send it to all the apropriate clients
+   for( j = 0; j < level.maxclients; j++ )
+   {
+@@ -883,12 +872,28 @@
+ static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 )
+ {
+   char    *p;
++  char    *args;
+   if( ent->client->pers.muted )
+   {
+     return;
+   }
++  // support parsing /m out of say text since some people have a hard
++  // time figuring out what the console is.
++  if( g_privateMessages.integer )
++  {
++    args = G_SayConcatArgs(0);
++    if( !Q_stricmpn( args, "say /m ", 7 ) ||
++      !Q_stricmpn( args, "say_team /m ", 12 ) || 
++      !Q_stricmpn( args, "say /mt ", 8 ) || 
++      !Q_stricmpn( args, "say_team /mt ", 13 ) )
++    {
++      G_PrivateMessage( ent );
++      return;
++    }
++  }
++
+   if( trap_Argc( ) < 2 && !arg0 )
+     return;
+@@ -927,7 +932,8 @@
+   p = ConcatArgs( 2 );
+-  G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p );
++  G_LogPrintf( "tell: %s^7 to %s^7: %s\n", ent->client->pers.netname,
++    target->client->pers.netname, p );
+   G_Say( ent, target, SAY_TELL, p );
+   // don't tell to the player self if it was already directed to this player
+   // also don't send the chat back to a bot
+@@ -978,7 +984,7 @@
+     return;
+   }
+-  if( ent->client->sess.sessionTeam == TEAM_SPECTATOR )
++  if( ent->client->pers.teamSelection == PTE_NONE )
+   {
+     trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator\n\"" );
+     return;
+@@ -1104,6 +1110,8 @@
+   trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE
+         " called a vote\n\"", ent->client->pers.netname ) );
++  G_Printf( "'%s' called a vote for '%s'\n", ent->client->pers.netname, 
++    level.voteString ) ;
+   ent->client->pers.voteCount++;
+@@ -1342,6 +1350,8 @@
+       trap_SendServerCommand( i, va("print \"%s " S_COLOR_WHITE
+             "called a team vote\n\"", ent->client->pers.netname ) );
+   }
++  G_Printf( "'%s' called a teamvote for '%s'\n", ent->client->pers.netname, 
++    level.teamVoteString[ cs_offset ] ) ;
+   // start the voting, the caller autoamtically votes yes
+   level.teamVoteTime[ cs_offset ] = level.time;
+@@ -1714,10 +1724,16 @@
+         return;
+       // Don't allow destruction of buildables that cannot be rebuilt
+-      if( g_suddenDeathTime.integer && ( level.time - level.startTime >=
+-          g_suddenDeathTime.integer * 60000 ) &&
+-          BG_FindBuildPointsForBuildable( traceEnt->s.modelindex ) )
++      if( level.suddenDeath && traceEnt->health > 0 &&
++        ( ( g_suddenDeathMode.integer == SDMODE_SELECTIVE &&
++          !BG_FindReplaceableTestForBuildable( traceEnt->s.modelindex ) ) ||
++        ( g_suddenDeathMode.integer == SDMODE_BP &&
++          BG_FindBuildPointsForBuildable( traceEnt->s.modelindex ) ) ||
++        g_suddenDeathMode.integer == SDMODE_NO_BUILD ) ) 
+       {
++        trap_SendServerCommand( ent-g_entities,
++          "print \"During Sudden Death you can only decon buildings that "
++          "can be rebuilt\n\"" );
+         return;
+       }
+@@ -1726,6 +1742,18 @@
+         G_AddEvent( ent, EV_BUILD_DELAY, ent->client->ps.clientNum );
+         return;
+       }
++      if( traceEnt->health > 0 )
++      {
++        G_TeamCommand( ent->client->pers.teamSelection,
++          va( "print \"%s ^3DECONSTRUCTED^7 by %s^7\n\"",
++            BG_FindHumanNameForBuildable( traceEnt->s.modelindex ), 
++            ent->client->pers.netname ) );
++      }
++      G_LogPrintf("Decon: %i %i 0: %s^7 deconstructed %s\n",
++        ent->client->ps.clientNum,
++        traceEnt->s.modelindex,
++        ent->client->pers.netname, 
++        BG_FindNameForBuildable( traceEnt->s.modelindex ) );
+       if( !deconstruct && CheatsOk( ent ) )
+         G_Damage( traceEnt, ent, ent, forward, tr.endpos, 10000, 0, MOD_SUICIDE );
+@@ -1895,10 +1923,11 @@
+   if( buyingEnergyAmmo )
+   {
+     //no armoury nearby
+-    if( ( !G_BuildableRange( ent->client->ps.origin, 100, BA_H_REACTOR ) &&
+-        !G_BuildableRange( ent->client->ps.origin, 100, BA_H_REPEATER ) ) )
++    if( !G_BuildableRange( ent->client->ps.origin, 100, BA_H_REACTOR ) &&
++        !G_BuildableRange( ent->client->ps.origin, 100, BA_H_REPEATER ) &&
++        !G_BuildableRange( ent->client->ps.origin, 100, BA_H_ARMOURY ) )
+     {
+-      trap_SendServerCommand( ent-g_entities, va( "print \"You must be near a reactor or repeater\n\"" ) );
++      trap_SendServerCommand( ent-g_entities, va( "print \"You must be near a reactor, repeater, or armoury\n\"" ) );
+       return;
+     }
+   }
+@@ -2219,6 +2248,23 @@
+   trap_Argv( 1, s, sizeof( s ) );
+   buildable = BG_FindBuildNumForName( s );
++
++  if( level.suddenDeath )
++  {
++    if( g_suddenDeathMode.integer == SDMODE_SELECTIVE &&
++      !BG_FindReplaceableTestForBuildable( buildable ) )
++    {
++      trap_SendServerCommand( ent-g_entities,
++        "print \"Only essential buildings can be rebuilt in Sudden Death\n\"" );
++      return;
++    }
++    else if( g_suddenDeathMode.integer == SDMODE_NO_BUILD )
++    {
++      trap_SendServerCommand( ent-g_entities,
++        "print \"Building is not allowed during Sudden Death\n\"" );
++    }
++  }
++
+   team = ent->client->ps.stats[ STAT_PTEAM ];
+   if( buildable != BA_NONE &&
+@@ -2326,6 +2372,7 @@
+ */
+ void G_StopFollowing( gentity_t *ent )
+ {
++  ent->client->ps = level.clients[ ent - g_entities ].ps;
+   ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;
+   ent->client->sess.sessionTeam = TEAM_SPECTATOR;
+   ent->client->sess.spectatorState = SPECTATOR_FREE;
+@@ -2616,6 +2663,12 @@
+     Cmd_Tell_f( ent );
+     return;
+   }
++  
++  if( !Q_stricmp( cmd, "m" ) || !Q_stricmp( cmd, "mt" ) )
++  {
++    G_PrivateMessage( ent );
++    return;
++  }
+   if( Q_stricmp( cmd, "score" ) == 0 )
+   {
+@@ -2808,3 +2861,105 @@
+   }
+   *out = '\0';
+ }
++
++
++void G_PrivateMessage( gentity_t *ent )
++{
++  int pids[ MAX_CLIENTS ];
++  char name[ MAX_NAME_LENGTH ];
++  char netname[ MAX_NAME_LENGTH ];
++  char cmd[ 12 ];
++  char str[ MAX_STRING_CHARS ];
++  char *msg;
++  char color;
++  int pcount, count = 0;
++  int i;
++  int skipargs = 0;
++  qboolean teamonly = qfalse;
++  gentity_t *tmpent;
++
++  if( !g_privateMessages.integer && ent )
++    return;
++
++  G_SayArgv( 0, cmd, sizeof( cmd ) );
++  if( !Q_stricmp( cmd, "say" ) || !Q_stricmp( cmd, "say_team" ) )
++  {
++    skipargs = 1;
++    G_SayArgv( 1, cmd, sizeof( cmd ) );
++  }
++  if( G_SayArgc( ) < 3+skipargs )
++  {
++    ADMP( va( "usage: %s [name|slot#] [message]\n", cmd ) );
++    return;
++  }
++
++  if( !Q_stricmp( cmd, "mt" ) || !Q_stricmp( cmd, "/mt" ) )
++    teamonly = qtrue;
++
++  G_SayArgv( 1+skipargs, name, sizeof( name ) );
++  msg = G_SayConcatArgs( 2+skipargs );
++  pcount = G_ClientNumbersFromString( name, pids );
++
++  if( ent )
++  {
++    Q_strncpyz( netname, ent->client->pers.netname, sizeof( netname ) );
++    if( teamonly )
++    {
++      for( i=0; i < pcount; i++ )
++      {
++        if( !OnSameTeam( ent, &g_entities[ pids[ i ] ] ) )
++          continue;
++        pids[ count ] = pids[ i ];
++        count++;
++      }
++      pcount = count;
++    }
++  }
++  else
++  {
++    Q_strncpyz( netname, "console", sizeof( name ) );
++  }
++
++  color = teamonly ? COLOR_CYAN : COLOR_YELLOW;
++
++  Q_strncpyz( str,
++    va( "^%csent to %i player%s: ^7", color, pcount,
++      ( pcount == 1 ) ? "" : "s" ),
++    sizeof( str ) );
++
++  for( i=0; i < pcount; i++ )
++  {
++    tmpent = &g_entities[ pids[ i ] ];
++
++    if( i > 0 )
++      Q_strcat( str, sizeof( str ), "^7, " );
++    Q_strcat( str, sizeof( str ), tmpent->client->pers.netname );
++    CPx( pids[ i ], va(
++      "chat \"%s^%c -> ^7%s^7: (%d recipients): ^%c%s^7\" %i",
++      netname,
++      color,
++      name,
++      pcount,
++      color,
++      msg,
++      ent ? ent-g_entities : -1 ) );
++    CPx( pids[ i ], va("cp \"^%cprivate message from ^7%s^7\"",
++      color,
++      netname ) );
++  }
++
++  if( !pcount )
++    ADMP( va( "^3No player matching ^7\'%s^7\' ^3to send message to.\n",
++      name ) );
++  else
++  {
++    ADMP( va( "^%cPrivate message: ^7%s\n", color, msg ) );
++    ADMP(va("%s\n", str));
++
++    if( teamonly )
++      G_LogPrintf( "tprivmsg: %s^7: %s^7: %s\n", netname, name, msg );
++    else
++      G_LogPrintf( "privmsg: %s^7: %s^7: %s\n", netname, name, msg );
++  }
++}
++
+Index: src/qcommon/q_shared.h
+===================================================================
+--- src/qcommon/q_shared.h     (revision 823)
++++ src/qcommon/q_shared.h     (working copy)
+@@ -423,6 +423,9 @@
+ #define VectorCopy(a,b)                       ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2])
+ #define       VectorScale(v, s, o)    ((o)[0]=(v)[0]*(s),(o)[1]=(v)[1]*(s),(o)[2]=(v)[2]*(s))
+ #define       VectorMA(v, s, b, o)    ((o)[0]=(v)[0]+(b)[0]*(s),(o)[1]=(v)[1]+(b)[1]*(s),(o)[2]=(v)[2]+(b)[2]*(s))
++#define VectorLerp( f, s, e, r ) ((r)[0]=(s)[0]+(f)*((e)[0]-(s)[0]),\
++  (r)[1]=(s)[1]+(f)*((e)[1]-(s)[1]),\
++  (r)[2]=(s)[2]+(f)*((e)[2]-(s)[2])) 
+ #else
This page took 0.178506 seconds and 4 git commands to generate.