Index: src/game/g_active.c
===================================================================
--- src/game/g_active.c (revision 66:2e9e43724119)
+++ src/game/g_active.c (revision 68:9d65d8683e8d)
@@ -376,4 +376,5 @@
   pmove_t pm;
   gclient_t *client;
+  qboolean attack1, attack3;
   qboolean	doPmove = qtrue;
 
@@ -382,4 +383,9 @@
   client->oldbuttons = client->buttons;
   client->buttons = ucmd->buttons;
+
+  attack1 = ( ( client->buttons & BUTTON_ATTACK ) &&
+              !( client->oldbuttons & BUTTON_ATTACK ) );
+  attack3 = ( ( client->buttons & BUTTON_USE_HOLDABLE ) &&
+              !( client->oldbuttons & BUTTON_USE_HOLDABLE ) );
 
   if( client->sess.spectatorState == SPECTATOR_LOCKED || client->sess.spectatorState == SPECTATOR_FOLLOW )
@@ -401,4 +407,13 @@
   if (doPmove)
   {
+    // in case the client entered the queue while following a teammate
+    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 ) ) )
+    {
+      client->ps.pm_flags |= PMF_QUEUED;
+    }
+
     client->ps.speed = BG_FindSpeedForClass( client->ps.stats[ STAT_PCLASS ] );
 
@@ -426,26 +441,23 @@
     trap_UnlinkEntity( ent );
 
-    if( ( client->buttons & BUTTON_ATTACK ) && !( client->oldbuttons & BUTTON_ATTACK ) )
-    {
-      //if waiting in a queue remove from the queue
-      if( client->ps.pm_flags & PMF_QUEUED )
-      {
-        if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
-          G_RemoveFromSpawnQueue( &level.alienSpawnQueue, client->ps.clientNum );
-        else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
-          G_RemoveFromSpawnQueue( &level.humanSpawnQueue, client->ps.clientNum );
-
-        client->pers.classSelection = PCL_NONE;
-        client->ps.stats[ STAT_PCLASS ] = PCL_NONE;
-      }
-      else if( client->pers.classSelection == PCL_NONE )
-      {
-        if( client->pers.teamSelection == PTE_NONE )
-          G_TriggerMenu( client->ps.clientNum, MN_TEAM );
-        else if( client->pers.teamSelection == PTE_ALIENS )
-          G_TriggerMenu( client->ps.clientNum, MN_A_CLASS );
-        else if( client->pers.teamSelection == PTE_HUMANS )
-          G_TriggerMenu( client->ps.clientNum, MN_H_SPAWN );
-      }
+    if( ( attack1 || attack3 ) && ( client->ps.pm_flags & PMF_QUEUED ) )
+    {
+      if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
+        G_RemoveFromSpawnQueue( &level.alienSpawnQueue, client->ps.clientNum );
+      else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+        G_RemoveFromSpawnQueue( &level.humanSpawnQueue, client->ps.clientNum );
+
+      client->pers.classSelection = PCL_NONE;
+      client->ps.stats[ STAT_PCLASS ] = PCL_NONE;
+    }
+    
+    if( attack1 && client->pers.classSelection == PCL_NONE )
+    {
+      if( client->pers.teamSelection == PTE_NONE )
+        G_TriggerMenu( client->ps.clientNum, MN_TEAM );
+      else if( client->pers.teamSelection == PTE_ALIENS )
+        G_TriggerMenu( client->ps.clientNum, MN_A_CLASS );
+      else if( client->pers.teamSelection == PTE_HUMANS )
+        G_TriggerMenu( client->ps.clientNum, MN_H_SPAWN );
     }
 
@@ -466,6 +478,20 @@
   }
 
-  if( ( client->buttons & BUTTON_USE_HOLDABLE ) && !( client->oldbuttons & BUTTON_USE_HOLDABLE ) )
+  else if( attack1 && ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
+  {
+    G_StopFollowing( ent );
+    client->pers.classSelection = PCL_NONE;
+    if( client->pers.teamSelection == PTE_NONE )
+      G_TriggerMenu( ent-g_entities, MN_TEAM );
+    else if( client->pers.teamSelection == PTE_ALIENS )
+      G_TriggerMenu( ent-g_entities, MN_A_CLASS );
+    else if( client->pers.teamSelection == PTE_HUMANS )
+      G_TriggerMenu( ent-g_entities, MN_H_SPAWN );
+  }
+  
+  if( attack3 )
+  {
     G_ToggleFollow( ent );
+  }
 }
 
@@ -1905,4 +1931,8 @@
   pers = &ent->client->pers;
 
+  // save a copy of things from playerState in case of SPECTATOR_FOLLOW 
+  pers->score = ent->client->ps.persistant[ PERS_SCORE ];
+  pers->credit = ent->client->ps.persistant[ PERS_CREDIT ];
+
   //
   // If the end of unit layout is displayed, don't give
Index: src/game/g_buildable.c
===================================================================
--- src/game/g_buildable.c (revision 65:9e928bb0b01b)
+++ src/game/g_buildable.c (revision 68:9d65d8683e8d)
@@ -1240,5 +1240,5 @@
     VectorCopy( origin, player->client->ps.origin );
     VectorCopy( vec3_origin, player->client->ps.velocity );
-    SetClientViewAngle( player, angles );
+    G_SetClientViewAngle( player, angles );
   }
 
@@ -1320,5 +1320,5 @@
       G_SetOrigin( activator, hovelOrigin );
       VectorCopy( hovelOrigin, activator->client->ps.origin );
-      SetClientViewAngle( activator, hovelAngles );
+      G_SetClientViewAngle( activator, hovelAngles );
     }
   }
@@ -1395,5 +1395,5 @@
     G_SetOrigin( builder, newOrigin );
     VectorCopy( newOrigin, builder->client->ps.origin );
-    SetClientViewAngle( builder, newAngles );
+    G_SetClientViewAngle( builder, newAngles );
 
     //client leaves hovel
@@ -1837,5 +1837,5 @@
 				G_SetOrigin(activator,spawn_origin);
 				VectorCopy(spawn_origin,activator->client->ps.origin);
-				SetClientViewAngle(activator,spawn_angles);
+				G_SetClientViewAngle(activator,spawn_angles);
 
 				// Play the spawn sound effect
Index: src/game/g_client.c
===================================================================
--- src/game/g_client.c (revision 65:9e928bb0b01b)
+++ src/game/g_client.c (revision 68:9d65d8683e8d)
@@ -140,4 +140,7 @@
   if( client->ps.persistant[ PERS_CREDIT ] < 0 )
     client->ps.persistant[ PERS_CREDIT ] = 0;
+  // keep PERS_CREDIT in sync if not following 
+  if( client->sess.spectatorState != SPECTATOR_FOLLOW )
+    client->ps.persistant[ PERS_CREDIT ] = client->pers.credit;
 }
 
@@ -146,5 +149,5 @@
 =======================================================================
 
-  SelectSpawnPoint
+  G_SelectSpawnPoint
 
 =======================================================================
@@ -181,5 +184,5 @@
 /*
 ================
-SelectNearestDeathmatchSpawnPoint
+G_SelectNearestDeathmatchSpawnPoint
 
 Find the spot that we DON'T want to use
@@ -187,5 +190,5 @@
 */
 #define MAX_SPAWN_POINTS  128
-gentity_t *SelectNearestDeathmatchSpawnPoint( vec3_t from )
+gentity_t *G_SelectNearestDeathmatchSpawnPoint( vec3_t from )
 {
   gentity_t *spot;
@@ -216,5 +219,5 @@
 /*
 ================
-SelectRandomDeathmatchSpawnPoint
+G_SelectRandomDeathmatchSpawnPoint
 
 go to a random point that doesn't telefrag
@@ -222,5 +225,5 @@
 */
 #define MAX_SPAWN_POINTS  128
-gentity_t *SelectRandomDeathmatchSpawnPoint( void )
+gentity_t *G_SelectRandomDeathmatchSpawnPoint( void )
 {
   gentity_t *spot;
@@ -251,10 +254,10 @@
 /*
 ===========
-SelectRandomFurthestSpawnPoint
+G_SelectRandomFurthestSpawnPoint
 
 Chooses a player start, deathmatch start, etc
 ============
 */
-gentity_t *SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
+gentity_t *G_SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
 {
   gentity_t *spot;
@@ -334,10 +337,10 @@
 /*
 ================
-SelectAlienSpawnPoint
+G_SelectAlienSpawnPoint
 
 go to a random point that doesn't telefrag
 ================
 */
-gentity_t *SelectAlienSpawnPoint( vec3_t preference )
+gentity_t *G_SelectAlienSpawnPoint( vec3_t preference )
 {
   gentity_t *spot;
@@ -383,10 +386,10 @@
 /*
 ================
-SelectHumanSpawnPoint
+G_SelectHumanSpawnPoint
 
 go to a random point that doesn't telefrag
 ================
 */
-gentity_t *SelectHumanSpawnPoint( vec3_t preference )
+gentity_t *G_SelectHumanSpawnPoint( vec3_t preference )
 {
   gentity_t *spot;
@@ -432,12 +435,12 @@
 /*
 ===========
-SelectSpawnPoint
+G_SelectSpawnPoint
 
 Chooses a player start, deathmatch start, etc
 ============
 */
-gentity_t *SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
-{
-  return SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles );
+gentity_t *G_SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
+{
+  return G_SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles );
 }
 
@@ -445,17 +448,17 @@
 /*
 ===========
-SelectTremulousSpawnPoint
+G_SelectTremulousSpawnPoint
 
 Chooses a player start, deathmatch start, etc
 ============
 */
-gentity_t *SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t origin, vec3_t angles )
+gentity_t *G_SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t origin, vec3_t angles )
 {
   gentity_t *spot = NULL;
 
   if( team == PTE_ALIENS )
-    spot = SelectAlienSpawnPoint( preference );
+    spot = G_SelectAlienSpawnPoint( preference );
   else if( team == PTE_HUMANS )
-    spot = SelectHumanSpawnPoint( preference );
+    spot = G_SelectHumanSpawnPoint( preference );
 
   //no available spots
@@ -478,5 +481,5 @@
 /*
 ===========
-SelectInitialSpawnPoint
+G_SelectInitialSpawnPoint
 
 Try to find a spawn point marked 'initial', otherwise
@@ -484,5 +487,5 @@
 ============
 */
-gentity_t *SelectInitialSpawnPoint( vec3_t origin, vec3_t angles )
+gentity_t *G_SelectInitialSpawnPoint( vec3_t origin, vec3_t angles )
 {
   gentity_t *spot;
@@ -497,5 +500,5 @@
   if( !spot || SpotWouldTelefrag( spot ) )
   {
-    return SelectSpawnPoint( vec3_origin, origin, angles );
+    return G_SelectSpawnPoint( vec3_origin, origin, angles );
   }
 
@@ -509,9 +512,9 @@
 /*
 ===========
-SelectSpectatorSpawnPoint
+G_SelectSpectatorSpawnPoint
 
 ============
 */
-gentity_t *SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles )
+gentity_t *G_SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles )
 {
   FindIntermissionPoint( );
@@ -526,5 +529,5 @@
 /*
 ===========
-SelectAlienLockSpawnPoint
+G_SelectAlienLockSpawnPoint
 
 Try to find a spawn point for alien intermission otherwise
@@ -532,5 +535,5 @@
 ============
 */
-gentity_t *SelectAlienLockSpawnPoint( vec3_t origin, vec3_t angles )
+gentity_t *G_SelectAlienLockSpawnPoint( vec3_t origin, vec3_t angles )
 {
   gentity_t *spot;
@@ -540,5 +543,5 @@
 
   if( !spot )
-    return SelectSpectatorSpawnPoint( origin, angles );
+    return G_SelectSpectatorSpawnPoint( origin, angles );
 
   VectorCopy( spot->s.origin, origin );
@@ -551,5 +554,5 @@
 /*
 ===========
-SelectHumanLockSpawnPoint
+G_SelectHumanLockSpawnPoint
 
 Try to find a spawn point for human intermission otherwise
@@ -557,5 +560,5 @@
 ============
 */
-gentity_t *SelectHumanLockSpawnPoint( vec3_t origin, vec3_t angles )
+gentity_t *G_SelectHumanLockSpawnPoint( vec3_t origin, vec3_t angles )
 {
   gentity_t *spot;
@@ -565,5 +568,5 @@
 
   if( !spot )
-    return SelectSpectatorSpawnPoint( origin, angles );
+    return G_SelectSpectatorSpawnPoint( origin, angles );
 
   VectorCopy( spot->s.origin, origin );
@@ -747,9 +750,9 @@
 /*
 ==================
-SetClientViewAngle
+G_SetClientViewAngle
 
 ==================
 */
-void SetClientViewAngle( gentity_t *ent, vec3_t angle )
+void G_SetClientViewAngle( gentity_t *ent, vec3_t angle )
 {
   int     i;
@@ -1156,8 +1159,5 @@
   strcpy( c2, Info_ValueForKey( userinfo, "color2" ) );
 
-  if( client->ps.pm_flags & PMF_FOLLOW )
-    team = PTE_NONE;
-  else
-    team = client->ps.stats[ STAT_PTEAM ];
+  team = client->pers.teamSelection;
 
   // send over a subset of the userinfo keys so other clients can
@@ -1341,4 +1341,5 @@
   client->pers.enterTime = level.time;
   client->pers.teamState.state = TEAM_BEGIN;
+  client->pers.classSelection = PCL_NONE;
 
   // save eflags around this, because changing teams will
@@ -1463,9 +1464,9 @@
   {
     if( teamLocal == PTE_NONE )
-      spawnPoint = SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
+      spawnPoint = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
     else if( teamLocal == PTE_ALIENS )
-      spawnPoint = SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
+      spawnPoint = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
     else if( teamLocal == PTE_HUMANS )
-      spawnPoint = SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
+      spawnPoint = G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
   }
   else
@@ -1522,4 +1523,8 @@
   client->ps.persistant[ PERS_SPAWN_COUNT ]++;
   client->ps.persistant[ PERS_TEAM ] = client->sess.sessionTeam;
+
+  // restore really persistant things
+  client->ps.persistant[ PERS_SCORE ] = client->pers.score;
+  client->ps.persistant[ PERS_CREDIT ] = client->pers.credit;
 
   client->airOutTime = level.time + 12000;
@@ -1648,5 +1653,5 @@
 
   trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
-  SetClientViewAngle( ent, spawn_angles );
+  G_SetClientViewAngle( ent, spawn_angles );
 
   if( !( client->sess.sessionTeam == TEAM_SPECTATOR ) )
Index: src/game/g_cmds.c
===================================================================
--- src/game/g_cmds.c (revision 67:e9b4b1a50e30)
+++ src/game/g_cmds.c (revision 68:9d65d8683e8d)
@@ -264,8 +264,10 @@
     if( cl->pers.connected == CON_CONNECTING )
       ping = -1;
+    else if( cl->sess.spectatorState == SPECTATOR_FOLLOW )
+      ping = cl->pers.ping < 999 ? cl->pers.ping : 999;
     else
       ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
 
-    if( cl->ps.stats[ STAT_HEALTH ] > 0 )
+    if( cl->sess.sessionTeam != TEAM_SPECTATOR )
     {
       weapon = cl->ps.weapon;
@@ -556,12 +558,4 @@
     if( ent->client && ent->client->pers.connected == CON_CONNECTED )
     {
-      // stop following clients
-      if( ent->client->sess.sessionTeam == TEAM_SPECTATOR &&
-          ent->client->sess.spectatorState == SPECTATOR_FOLLOW &&
-          ent->client->sess.spectatorClient == self->client->ps.clientNum )
-      {
-        if( !G_FollowNewClient( ent, 1 ) )
-          G_StopFollowing( ent );
-      }
       // cure poison
       if( ent->client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED &&
@@ -596,37 +590,13 @@
     && ( level.time - ent->client->pers.teamChangeTime ) > 60000 ) )
   {
-    if( oldTeam == PTE_NONE )
-    {
-      // ps.persistant[] from a spectator cannot be trusted
-      ent->client->ps.persistant[ PERS_SCORE ] = ent->client->pers.savedScore;
-      ent->client->ps.persistant[ PERS_CREDIT ] = ent->client->pers.savedCredit;
-    }
-    else if( oldTeam == PTE_ALIENS )
-    {
-      // always save in human credtis
-      ent->client->ps.persistant[ PERS_CREDIT ] *=
-        (float)FREEKILL_HUMAN / FREEKILL_ALIEN;
-    }
-
-    if( newTeam == PTE_NONE )
-    {
-      // save values before the client enters the spectator team and their
-      // ps.persistant[] values become trashed
-      ent->client->pers.savedScore = ent->client->ps.persistant[ PERS_SCORE ];
-      ent->client->pers.savedCredit = ent->client->ps.persistant[ PERS_CREDIT ];
-    }
+    if( oldTeam == PTE_ALIENS )
+      ent->client->pers.credit *= (float)FREEKILL_HUMAN / FREEKILL_ALIEN;
     else if( newTeam == PTE_ALIENS )
-    {
-      // convert to alien currency
-      ent->client->ps.persistant[ PERS_CREDIT ] *=
-        (float)FREEKILL_ALIEN / FREEKILL_HUMAN;
-    }
+      ent->client->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.savedScore = 0;
-    ent->client->pers.savedCredit = 0;
+    ent->client->pers.credit = 0;
+    ent->client->pers.score = 0;
   }
 
@@ -698,4 +668,5 @@
   //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 ||
       ent->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_BUILDER0_UPG ||
@@ -1622,6 +1593,4 @@
   trace_t   tr, tr2;
   vec3_t    infestOrigin;
-  int       allowedClasses[ PCL_NUM_CLASSES ];
-  int       numClasses = 0;
   pClass_t  currentClass = ent->client->ps.stats[ STAT_PCLASS ];
   pClass_t  newClass;
@@ -1638,19 +1607,81 @@
   qboolean  humanNear = qfalse;
 
-  if( ent->client->ps.stats[ STAT_HEALTH ] <= 0 )
-    return;
-
   clientNum = ent->client - level.clients;
   trap_Argv( 1, s, sizeof( s ) );
-
-  if( BG_ClassIsAllowed( PCL_ALIEN_BUILDER0 ) )
-    allowedClasses[ numClasses++ ] = PCL_ALIEN_BUILDER0;
-
-  if( BG_ClassIsAllowed( PCL_ALIEN_BUILDER0_UPG ) &&
-      BG_FindStagesForClass( PCL_ALIEN_BUILDER0_UPG, g_alienStage.integer ) )
-    allowedClasses[ numClasses++ ] = PCL_ALIEN_BUILDER0_UPG;
-
-  if( BG_ClassIsAllowed( PCL_ALIEN_LEVEL0 ) )
-    allowedClasses[ numClasses++ ] = PCL_ALIEN_LEVEL0;
+  newClass = BG_FindClassNumForName( s );
+
+  if( ent->client->sess.sessionTeam == TEAM_SPECTATOR )
+  {
+    if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
+      G_StopFollowing( ent );
+    if( ent->client->pers.teamSelection == PTE_ALIENS )
+    {
+      if( newClass != PCL_ALIEN_BUILDER0 &&
+          newClass != PCL_ALIEN_BUILDER0_UPG &&
+          newClass != PCL_ALIEN_LEVEL0 )
+      {
+        trap_SendServerCommand( ent-g_entities,
+          va( "print \"You cannot spawn with class %s\n\"", s ) );
+        return;
+      }
+ 
+      if( !BG_ClassIsAllowed( newClass ) )
+      {
+        trap_SendServerCommand( ent-g_entities,
+          va( "print \"Class %s is not allowed\n\"", s ) );
+        return;
+      }
+      if( !BG_FindStagesForClass( newClass, g_alienStage.integer ) )
+      {
+        trap_SendServerCommand( ent-g_entities,
+          va( "print \"Class %s not allowed at stage %d\n\"",
+              s, g_alienStage.integer ) );
+        return;
+      }
+ 
+      // spawn from an egg
+      if( G_PushSpawnQueue( &level.alienSpawnQueue, clientNum ) )
+      {
+        ent->client->pers.classSelection = newClass;
+        ent->client->ps.stats[ STAT_PCLASS ] = newClass;
+      }
+    }
+    else if( ent->client->pers.teamSelection == PTE_HUMANS )
+    {
+      //set the item to spawn with
+      if( !Q_stricmp( s, BG_FindNameForWeapon( WP_MACHINEGUN ) ) &&
+          BG_WeaponIsAllowed( WP_MACHINEGUN ) )
+      {
+        ent->client->pers.humanItemSelection = WP_MACHINEGUN;
+      }
+      else if( !Q_stricmp( s, BG_FindNameForWeapon( WP_HBUILD ) ) &&
+               BG_WeaponIsAllowed( WP_HBUILD ) )
+      {
+        ent->client->pers.humanItemSelection = WP_HBUILD;
+      }
+      else if( !Q_stricmp( s, BG_FindNameForWeapon( WP_HBUILD2 ) ) &&
+               BG_WeaponIsAllowed( WP_HBUILD2 ) &&
+               BG_FindStagesForWeapon( WP_HBUILD2, g_humanStage.integer ) )
+      {
+        ent->client->pers.humanItemSelection = WP_HBUILD2;
+      }
+      else
+      {
+        trap_SendServerCommand( ent-g_entities,
+          "print \"Unknown starting item\n\"" );
+        return;
+      }
+      // spawn from a telenode
+      if( G_PushSpawnQueue( &level.humanSpawnQueue, clientNum ) )
+      {
+        ent->client->pers.classSelection = PCL_HUMAN;
+        ent->client->ps.stats[ STAT_PCLASS ] = PCL_HUMAN;
+      }
+    }
+    return;
+  }
+
+  if( ent->health <= 0 )
+    return;
 
   if( ent->client->pers.teamSelection == PTE_ALIENS &&
@@ -1658,8 +1689,7 @@
       !( ent->client->ps.stats[ STAT_STATE ] & SS_HOVELING ) )
   {
-    newClass = BG_FindClassNumForName( s );
     if( newClass == PCL_NONE )
     {
-      trap_SendServerCommand( ent-g_entities, va( "print \"Unknown class\n\"" ) );
+      trap_SendServerCommand( ent-g_entities, "print \"Unknown class\n\"" );
       return;
     }
@@ -1716,5 +1746,6 @@
 
       //guard against selling the HBUILD weapons exploit
-      if( ( currentClass == PCL_ALIEN_BUILDER0 ||
+      if( ent->client->sess.sessionTeam != TEAM_SPECTATOR &&
+          ( currentClass == PCL_ALIEN_BUILDER0 ||
             currentClass == PCL_ALIEN_BUILDER0_UPG ) &&
           ent->client->ps.stats[ STAT_MISC ] > 0 )
@@ -1792,55 +1823,11 @@
       }
     }
-    else
-    {
-      //spawning from an egg
-      for( i = 0; i < numClasses; i++ )
-      {
-        if( allowedClasses[ i ] == newClass &&
-            BG_FindStagesForClass( newClass, g_alienStage.integer ) &&
-            BG_ClassIsAllowed( newClass ) )
-        {
-          G_LogOnlyPrintf("ClientTeamClass: %i alien %s\n", clientNum, s);
-
-          ent->client->pers.classSelection =
-            ent->client->ps.stats[ STAT_PCLASS ] = newClass;
-          G_PushSpawnQueue( &level.alienSpawnQueue, clientNum );
-          return;
-        }
-      }
-      trap_SendServerCommand( ent-g_entities, va( "print \"You cannot spawn as this class\n\"" ) );
-      return;
-    }
   }
   else if( ent->client->pers.teamSelection == PTE_HUMANS )
   {
     //humans cannot use this command whilst alive
-    if( ent->client->pers.classSelection != PCL_NONE )
-    {
-      trap_SendServerCommand( ent-g_entities, va( "print \"You must be dead to use the class command\n\"" ) );
-      return;
-    }
-
-    ent->client->pers.classSelection =
-      ent->client->ps.stats[ STAT_PCLASS ] = PCL_HUMAN;
-
-    //set the item to spawn with
-    if( !Q_stricmp( s, BG_FindNameForWeapon( WP_MACHINEGUN ) ) && BG_WeaponIsAllowed( WP_MACHINEGUN ) )
-      ent->client->pers.humanItemSelection = WP_MACHINEGUN;
-    else if( !Q_stricmp( s, BG_FindNameForWeapon( WP_HBUILD ) ) && BG_WeaponIsAllowed( WP_HBUILD ) )
-      ent->client->pers.humanItemSelection = WP_HBUILD;
-    else if( !Q_stricmp( s, BG_FindNameForWeapon( WP_HBUILD2 ) ) && BG_WeaponIsAllowed( WP_HBUILD2 ) &&
-        BG_FindStagesForWeapon( WP_HBUILD2, g_humanStage.integer ) )
-      ent->client->pers.humanItemSelection = WP_HBUILD2;
-    else
-    {
-      ent->client->pers.classSelection = PCL_NONE;
-      trap_SendServerCommand( ent-g_entities, va( "print \"Unknown starting item\n\"" ) );
-      return;
-    }
-
-    G_LogOnlyPrintf("ClientTeamClass: %i human %s\n", clientNum, s);
-
-    G_PushSpawnQueue( &level.humanSpawnQueue, clientNum );
+    trap_SendServerCommand( ent-g_entities,
+      "print \"You must be dead to use the class command\n\"" );
+    return;
   }
 }
@@ -2786,4 +2773,28 @@
 /*
 =================
+G_StopFromFollowing
+
+stops any other clients from following this one
+called when a player leaves a team or dies
+=================
+*/
+void G_StopFromFollowing( gentity_t *ent )
+{
+  int i;
+
+  for( i = 0; i < level.maxclients; i++ )
+  {
+    if( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR &&
+        level.clients[ i ].sess.spectatorState == SPECTATOR_FOLLOW &&
+        level.clients[ i ].sess.spectatorClient == ent->client->ps.clientNum )
+    {
+      if( !G_FollowNewClient( &g_entities[ i ], 1 ) )
+        G_StopFollowing( &g_entities[ i ] );
+    }
+  }
+}
+
+/*
+=================
 G_StopFollowing
 
@@ -2796,8 +2807,25 @@
   ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;
   ent->client->sess.sessionTeam = TEAM_SPECTATOR;
-  ent->client->sess.spectatorState = SPECTATOR_FREE;
+  ent->client->ps.stats[ STAT_PTEAM ] = ent->client->pers.teamSelection;
+
+  if( ent->client->pers.teamSelection == PTE_NONE )
+  {
+    ent->client->sess.spectatorState = SPECTATOR_FREE;
+  }
+  else
+  {
+    vec3_t   spawn_origin, spawn_angles;
+
+    ent->client->sess.spectatorState = SPECTATOR_LOCKED;
+    if( ent->client->pers.teamSelection == PTE_ALIENS )
+      G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
+    else if( ent->client->pers.teamSelection == PTE_HUMANS )
+      G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
+    G_SetOrigin( ent, spawn_origin );
+    VectorCopy( spawn_origin, ent->client->ps.origin );
+    G_SetClientViewAngle( ent, spawn_angles );
+  }
   ent->client->sess.spectatorClient = -1;
   ent->client->ps.pm_flags &= ~PMF_FOLLOW;
-  ent->client->ps.stats[ STAT_PTEAM ] = PTE_NONE;
 
   ent->client->ps.stats[ STAT_STATE ] &= ~SS_WALLCLIMBING;
@@ -2864,5 +2892,11 @@
 
     // can't follow another spectator
-    if( level.clients[ clientnum ].pers.teamSelection == PTE_NONE )
+    if( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR )
+      continue;
+
+    // can only follow teammates when dead and on a team
+    if( ent->client->pers.teamSelection != PTE_NONE && 
+        ( level.clients[ clientnum ].pers.teamSelection != 
+          ent->client->pers.teamSelection ) )
       continue;
 
@@ -3007,4 +3041,6 @@
 
   // won't work unless spectating
+  if( ent->client->sess.sessionTeam != TEAM_SPECTATOR )
+    return;
   if( ent->client->sess.spectatorState == SPECTATOR_NOT )
     return;
@@ -3211,7 +3247,7 @@
   { "donate", CMD_TEAM, Cmd_Donate_f },
 
-  { "follow", CMD_NOTEAM, Cmd_Follow_f },
-  { "follownext", CMD_NOTEAM, Cmd_FollowCycle_f },
-  { "followprev", CMD_NOTEAM, Cmd_FollowCycle_f },
+  { "follow", CMD_SPEC, Cmd_Follow_f },
+  { "follownext", CMD_SPEC, Cmd_FollowCycle_f },
+  { "followprev", CMD_SPEC, Cmd_FollowCycle_f },
 
   { "where", CMD_TEAM, Cmd_Where_f },
@@ -3285,9 +3321,9 @@
   }
 
-  if( cmds[ i ].cmdFlags & CMD_NOTEAM &&
-      ent->client->pers.teamSelection != PTE_NONE )
+  if( cmds[ i ].cmdFlags & CMD_SPEC &&
+      ent->client->sess.sessionTeam != TEAM_SPECTATOR )
   {
     trap_SendServerCommand( clientNum,
-      "print \"Cannot use this command when on a team\n\"" );
+      "print \"You can only use this command when spectating\n\"" );
     return;
   }
Index: src/game/g_combat.c
===================================================================
--- src/game/g_combat.c (revision 1:99f993ea82fe)
+++ src/game/g_combat.c (revision 68:9d65d8683e8d)
@@ -148,4 +148,5 @@
   // stop any following clients
   // r1: removed, annoying.
+  // G_StopFromFollowing( self );
 
   self->client->ps.pm_type = PM_DEAD;
Index: src/game/g_local.h
===================================================================
--- src/game/g_local.h (revision 66:2e9e43724119)
+++ src/game/g_local.h (revision 68:9d65d8683e8d)
@@ -346,7 +346,7 @@
   int                 nameChanges;
 
-  // used to save persistant[] values while in SPECTATOR_FOLLOW mode
-  int                 savedScore;
-  int                 savedCredit;
+  // used to save playerState_t values while in SPECTATOR_FOLLOW mode
+  int                 score;
+  int                 credit;
   int                 ping;
 
@@ -484,5 +484,6 @@
 int       G_PopSpawnQueue( spawnQueue_t *sq );
 int       G_PeekSpawnQueue( spawnQueue_t *sq );
-void      G_PushSpawnQueue( spawnQueue_t *sq, int clientNum );
+qboolean  G_SearchSpawnQueue( spawnQueue_t *sq, int clientNum );
+qboolean  G_PushSpawnQueue( spawnQueue_t *sq, int clientNum );
 qboolean  G_RemoveFromSpawnQueue( spawnQueue_t *sq, int clientNum );
 int       G_GetPosInSpawnQueue( spawnQueue_t *sq, int clientNum );
@@ -670,5 +671,5 @@
 #define CMD_MESSAGE       0x02 // sends message to others (skip when muted)
 #define CMD_TEAM          0x04 // must be on a team
-#define CMD_NOTEAM        0x08 // must not be on a team
+#define CMD_SPEC          0x08 // must be in spectator mode
 #define CMD_ALIEN         0x10
 #define CMD_HUMAN         0x20
@@ -698,4 +699,5 @@
 //
 void      Cmd_Score_f( gentity_t *ent );
+void      G_StopFromFollowing( gentity_t *ent );
 void      G_StopFollowing( gentity_t *ent );
 qboolean  G_FollowNewClient( gentity_t *ent, int dir );
@@ -917,7 +919,9 @@
 void      G_AddCreditToClient( gclient_t *client, short credit, qboolean cap );
 team_t    TeamCount( int ignoreClientNum, int team );
-void      SetClientViewAngle( gentity_t *ent, vec3_t angle );
-gentity_t *SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t origin, vec3_t angles );
-gentity_t *SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles );
+void      G_SetClientViewAngle( gentity_t *ent, vec3_t angle );
+gentity_t *G_SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t origin, vec3_t angles );
+gentity_t *G_SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles );
+gentity_t *G_SelectAlienLockSpawnPoint( vec3_t origin, vec3_t angles );
+gentity_t *G_SelectHumanLockSpawnPoint( vec3_t origin, vec3_t angles );
 void      SpawnCorpse( gentity_t *ent );
 void      respawn( gentity_t *ent );
Index: src/game/g_main.c
===================================================================
--- src/game/g_main.c (revision 66:2e9e43724119)
+++ src/game/g_main.c (revision 68:9d65d8683e8d)
@@ -821,7 +821,7 @@
 
   // then sort by score
-  if( ca->ps.persistant[ PERS_SCORE ] > cb->ps.persistant[ PERS_SCORE ] )
+  if( ca->pers.score > cb->pers.score )
     return -1;
-  else if( ca->ps.persistant[ PERS_SCORE ] < cb->ps.persistant[ PERS_SCORE ] )
+  else if( ca->pers.score < cb->pers.score )
     return 1;
   else
@@ -905,4 +905,21 @@
 /*
 ============
+G_SearchSpawnQueue
+
+Look to see if clientNum is already in the spawnQueue
+============
+*/
+qboolean G_SearchSpawnQueue( spawnQueue_t *sq, int clientNum )
+{
+  int i;
+
+  for( i = 0; i < MAX_CLIENTS; i++ )
+    if( sq->clients[ i ] == clientNum )
+      return qtrue;
+  return qfalse;
+}
+
+/*
+============
 G_PushSpawnQueue
 
@@ -910,10 +927,15 @@
 ============
 */
-void G_PushSpawnQueue( spawnQueue_t *sq, int clientNum )
-{
+qboolean G_PushSpawnQueue( spawnQueue_t *sq, int clientNum )
+{
+  // don't add the same client more than once
+  if( G_SearchSpawnQueue( sq, clientNum ) )
+    return qfalse;
+
   sq->back = QUEUE_PLUS1( sq->back );
   sq->clients[ sq->back ] = clientNum;
 
   g_entities[ clientNum ].client->ps.pm_flags |= PMF_QUEUED;
+  return qtrue;
 }
 
@@ -1049,5 +1071,5 @@
     ent = &g_entities[ clientNum ];
 
-    if( ( spawn = SelectTremulousSpawnPoint( team,
+    if( ( spawn = G_SelectTremulousSpawnPoint( team,
             ent->client->pers.lastDeathLocation,
             spawn_origin, spawn_angles ) ) )
@@ -1585,5 +1607,5 @@
   if( !ent )
   { // the map creator forgot to put in an intermission point...
-    SelectSpawnPoint( vec3_origin, level.intermission_origin, level.intermission_angle );
+    G_SelectSpawnPoint( vec3_origin, level.intermission_origin, level.intermission_angle );
   }
   else
Index: src/game/g_misc.c
===================================================================
--- src/game/g_misc.c (revision 0:abd33ab9075a)
+++ src/game/g_misc.c (revision 68:9d65d8683e8d)
@@ -90,5 +90,5 @@
 
   // set angles
-  SetClientViewAngle( player, angles );
+  G_SetClientViewAngle( player, angles );
 
   // kill anything at the destination
Index: src/game/g_ptr.c
===================================================================
--- src/game/g_ptr.c (revision 0:abd33ab9075a)
+++ src/game/g_ptr.c (revision 68:9d65d8683e8d)
@@ -64,5 +64,5 @@
     client->pers.connection->clientTeam = client->pers.teamSelection;
     if( client->pers.teamSelection == PTE_NONE )
-      client->pers.connection->clientCredit = client->pers.savedCredit;
+      client->pers.connection->clientCredit = client->pers.credit;
     else
       client->pers.connection->clientCredit = client->ps.persistant[ PERS_CREDIT ];
