/*
===============
trigger_ammo_touch
===============
*/
void trigger_ammo_touch( gentity_t *self, gentity_t *other, trace_t *trace )
{
  int ammo, clips, maxClips, maxAmmo;

  if( !other->client )
    return;

  if( other->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS )
    return;

  if( self->timestamp > level.time )
    return;

  if( other->client->ps.weaponstate != WEAPON_READY )
    return;

  if( BG_FindUsesEnergyForWeapon( other->client->ps.weapon ) && self->spawnflags & 2 )
    return;

  if( !BG_FindUsesEnergyForWeapon( other->client->ps.weapon ) && self->spawnflags & 4 )
    return;

  if( self->spawnflags & 1 )
    self->timestamp = level.time + 1000;
  else
    self->timestamp = level.time + FRAMETIME;

  BG_FindAmmoForWeapon( other->client->ps.weapon, &maxAmmo, &maxClips );
  BG_UnpackAmmoArray( other->client->ps.weapon, other->client->ps.ammo, other->client->ps.powerups,
                      &ammo, &clips );

  if( ( ammo + self->damage ) > maxAmmo )
  {
    if( clips < maxClips )
    {
      clips++;
      ammo = 1;
    }
    else
      ammo = maxAmmo;
  }
  else
    ammo += self->damage;

  BG_PackAmmoArray( other->client->ps.weapon, other->client->ps.ammo, other->client->ps.powerups,
                    ammo, clips );
}
Esempio n. 2
0
/*
=================
G_GiveClientMaxAmmo
=================
*/
void G_GiveClientMaxAmmo( gentity_t *ent, qboolean buyingEnergyAmmo )
{
  int       i;
  int       maxAmmo, maxClips;
  qboolean  weaponType, restoredAmmo = qfalse;

  for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
  {
    if( buyingEnergyAmmo )
      weaponType = BG_FindUsesEnergyForWeapon( i );
    else
      weaponType = !BG_FindUsesEnergyForWeapon( i );

    if( BG_InventoryContainsWeapon( i, ent->client->ps.stats ) &&
        weaponType && !BG_FindInfinteAmmoForWeapon( i ) &&
        !BG_WeaponIsFull( i, ent->client->ps.stats,
          ent->client->ps.ammo, ent->client->ps.powerups ) )
    {
      BG_FindAmmoForWeapon( i, &maxAmmo, &maxClips );

	if(ent->client->pers.trippleammo == 1)
    {
		maxAmmo*=3;
    }

      if( buyingEnergyAmmo )
      {
        G_AddEvent( ent, EV_RPTUSE_SOUND, 0 );

        if( BG_InventoryContainsUpgrade( UP_BATTPACK, ent->client->ps.stats ) )
          maxAmmo = (int)( (float)maxAmmo * BATTPACK_MODIFIER );
      }

      BG_PackAmmoArray( i, ent->client->ps.ammo, ent->client->ps.powerups,
                        maxAmmo, maxClips );

      restoredAmmo = qtrue;
    }
  }

  if( restoredAmmo )
    G_ForceWeaponChange( ent, ent->client->ps.weapon );
}
Esempio n. 3
0
/*
==================
ClientTimerActions

Actions that happen once a second
==================
*/
void ClientTimerActions( gentity_t *ent, int msec )
{
  gclient_t *client;
  usercmd_t *ucmd;
  int       aForward, aRight;

  ucmd = &ent->client->pers.cmd;

  aForward  = abs( ucmd->forwardmove );
  aRight    = abs( ucmd->rightmove );

  client = ent->client;
  client->time100 += msec;
  client->time1000 += msec;
  client->time10000 += msec;

  while ( client->time100 >= 100 )
  {
    client->time100 -= 100;

    //if not trying to run then not trying to sprint
    if( aForward <= 64 )
      client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST;

    if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) )
      client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST;

    if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) &&  ucmd->upmove >= 0 )
    {
      //subtract stamina
      if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) )
        client->ps.stats[ STAT_STAMINA ] -= STAMINA_LARMOUR_TAKE;
      else
        client->ps.stats[ STAT_STAMINA ] -= STAMINA_SPRINT_TAKE;

      if( client->ps.stats[ STAT_STAMINA ] < -MAX_STAMINA )
        client->ps.stats[ STAT_STAMINA ] = -MAX_STAMINA;
    }

    if( ( aForward <= 64 && aForward > 5 ) || ( aRight <= 64 && aRight > 5 ) )
    {
      //restore stamina
      client->ps.stats[ STAT_STAMINA ] += STAMINA_WALK_RESTORE;

      if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA )
        client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;
    }
    else if( aForward <= 5 && aRight <= 5 )
    {
      //restore stamina faster
      client->ps.stats[ STAT_STAMINA ] += STAMINA_STOP_RESTORE;

      if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA )
        client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;
    }

    //client is charging up for a pounce
    if( client->ps.weapon == WP_ALEVEL3 || client->ps.weapon == WP_ALEVEL3_UPG )
    {
      int pounceSpeed = 0;

      if( client->ps.weapon == WP_ALEVEL3 )
        pounceSpeed = LEVEL3_POUNCE_SPEED;
      else if( client->ps.weapon == WP_ALEVEL3_UPG )
        pounceSpeed = LEVEL3_POUNCE_UPG_SPEED;

      if( client->ps.stats[ STAT_MISC ] < pounceSpeed && ucmd->buttons & BUTTON_ATTACK2 )
        client->ps.stats[ STAT_MISC ] += ( 100.0f / (float)LEVEL3_POUNCE_CHARGE_TIME ) * pounceSpeed;

      if( !( ucmd->buttons & BUTTON_ATTACK2 ) )
      {
        if( client->ps.stats[ STAT_MISC ] > 0 )
        {
          client->allowedToPounce = qtrue;
          client->pouncePayload = client->ps.stats[ STAT_MISC ];
        }

        client->ps.stats[ STAT_MISC ] = 0;
      }

      if( client->ps.stats[ STAT_MISC ] > pounceSpeed )
        client->ps.stats[ STAT_MISC ] = pounceSpeed;
    }

    //client is charging up for a... charge
    if( client->ps.weapon == WP_ALEVEL4 )
    {
      if( client->ps.stats[ STAT_MISC ] < LEVEL4_CHARGE_TIME && ucmd->buttons & BUTTON_ATTACK2 &&
          !client->charging )
      {
        client->charging = qfalse; //should already be off, just making sure
        client->ps.stats[ STAT_STATE ] &= ~SS_CHARGING;

        if( ucmd->forwardmove > 0 )
        {
          //trigger charge sound...is quite annoying
          //if( client->ps.stats[ STAT_MISC ] <= 0 )
          //  G_AddEvent( ent, EV_LEV4_CHARGE_PREPARE, 0 );

          client->ps.stats[ STAT_MISC ] += (int)( 100 * (float)LEVEL4_CHARGE_CHARGE_RATIO );

          if( client->ps.stats[ STAT_MISC ] > LEVEL4_CHARGE_TIME )
            client->ps.stats[ STAT_MISC ] = LEVEL4_CHARGE_TIME;
        }
        else
          client->ps.stats[ STAT_MISC ] = 0;
      }

      if( !( ucmd->buttons & BUTTON_ATTACK2 ) || client->charging ||
          client->ps.stats[ STAT_MISC ] == LEVEL4_CHARGE_TIME )
      {
        if( client->ps.stats[ STAT_MISC ] > LEVEL4_MIN_CHARGE_TIME )
        {
          client->ps.stats[ STAT_MISC ] -= 100;

          if( client->charging == qfalse )
            G_AddEvent( ent, EV_LEV4_CHARGE_START, 0 );

          client->charging = qtrue;
          client->ps.stats[ STAT_STATE ] |= SS_CHARGING;

          //if the charger has stopped moving take a chunk of charge away
          if( VectorLength( client->ps.velocity ) < 64.0f || aRight )
            client->ps.stats[ STAT_MISC ] = client->ps.stats[ STAT_MISC ] / 2;

          //can't charge backwards
          if( ucmd->forwardmove < 0 )
            client->ps.stats[ STAT_MISC ] = 0;
        }
        else
          client->ps.stats[ STAT_MISC ] = 0;


        if( client->ps.stats[ STAT_MISC ] <= 0 )
        {
          client->ps.stats[ STAT_MISC ] = 0;
          client->charging = qfalse;
          client->ps.stats[ STAT_STATE ] &= ~SS_CHARGING;
        }
      }
    }

    //client is charging up an lcannon
    if( client->ps.weapon == WP_LUCIFER_CANNON )
    {
      int ammo;

      BG_UnpackAmmoArray( WP_LUCIFER_CANNON, client->ps.ammo, client->ps.powerups, &ammo, NULL );

      if( client->ps.stats[ STAT_MISC ] < LCANNON_TOTAL_CHARGE && ucmd->buttons & BUTTON_ATTACK )
        client->ps.stats[ STAT_MISC ] += ( 100.0f / LCANNON_CHARGE_TIME ) * LCANNON_TOTAL_CHARGE;

      if( client->ps.stats[ STAT_MISC ] > LCANNON_TOTAL_CHARGE )
        client->ps.stats[ STAT_MISC ] = LCANNON_TOTAL_CHARGE;

      if( client->ps.stats[ STAT_MISC ] > ( ammo * LCANNON_TOTAL_CHARGE ) / 10 )
        client->ps.stats[ STAT_MISC ] = ammo * LCANNON_TOTAL_CHARGE / 10;
    }

    switch( client->ps.weapon )
    {
      case WP_ABUILD:
      case WP_ABUILD2:
      case WP_HBUILD:
      case WP_HBUILD2:
        //set validity bit on buildable
        if( ( client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) > BA_NONE )
        {
          int     dist = BG_FindBuildDistForClass( ent->client->ps.stats[ STAT_PCLASS ] );
          vec3_t  dummy;

          if( G_itemFits( ent, (buildable_t)(client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT),
                          dist, dummy ) == IBE_NONE )
            client->ps.stats[ STAT_BUILDABLE ] |= SB_VALID_TOGGLEBIT;
          else
            client->ps.stats[ STAT_BUILDABLE ] &= ~SB_VALID_TOGGLEBIT;
        }

        //update build timer
        if( client->ps.stats[ STAT_MISC ] > 0 )
          client->ps.stats[ STAT_MISC ] -= 100;

        if( client->ps.stats[ STAT_MISC ] < 0 )
          client->ps.stats[ STAT_MISC ] = 0;
        break;

      default:
        break;
    }

    if( client->ps.stats[ STAT_STATE ] & SS_MEDKIT_ACTIVE )
    {
      int remainingStartupTime = MEDKIT_STARTUP_TIME - ( level.time - client->lastMedKitTime );

      if( remainingStartupTime < 0 )
      {
        if( ent->health < ent->client->ps.stats[ STAT_MAX_HEALTH ] &&
            ent->client->medKitHealthToRestore &&
            ent->client->ps.pm_type != PM_DEAD )
        {
          ent->client->medKitHealthToRestore--;
          ent->health++;
        }
        else
          ent->client->ps.stats[ STAT_STATE ] &= ~SS_MEDKIT_ACTIVE;
      }
      else
      {
        if( ent->health < ent->client->ps.stats[ STAT_MAX_HEALTH ] &&
            ent->client->medKitHealthToRestore &&
            ent->client->ps.pm_type != PM_DEAD )
        {
          //partial increase
          if( level.time > client->medKitIncrementTime )
          {
            ent->client->medKitHealthToRestore--;
            ent->health++;

            client->medKitIncrementTime = level.time +
              ( remainingStartupTime / MEDKIT_STARTUP_SPEED );
          }
        }
        else
          ent->client->ps.stats[ STAT_STATE ] &= ~SS_MEDKIT_ACTIVE;
      }
    }
  }

  while( client->time1000 >= 1000 )
  {
    client->time1000 -= 1000;

    //client is poison clouded
    if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED )
      G_Damage( ent, client->lastPoisonCloudedClient, client->lastPoisonCloudedClient, NULL, NULL,
                LEVEL1_PCLOUD_DMG, 0, MOD_LEVEL1_PCLOUD );

    //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;

      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 );
    }

    //replenish alien health
    if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
    {
      int       entityList[ MAX_GENTITIES ];
      vec3_t    range = { LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE };
      vec3_t    mins, maxs;
      int       i, num;
      gentity_t *boostEntity;
      float     modifier = 1.0f;

      VectorAdd( client->ps.origin, range, maxs );
      VectorSubtract( client->ps.origin, range, mins );

      num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
      for( i = 0; i < num; i++ )
      {
        boostEntity = &g_entities[ entityList[ i ] ];

        if( boostEntity->client && boostEntity->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS &&
            boostEntity->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL4 )
        {
          modifier = LEVEL4_REGEN_MOD;
          break;
        }
        else if( boostEntity->s.eType == ET_BUILDABLE &&
            boostEntity->s.modelindex == BA_A_BOOSTER &&
            boostEntity->spawned )
        {
          modifier = BOOSTER_REGEN_MOD;
          break;
        }
      }

      if( ent->health > 0 && ent->health < client->ps.stats[ STAT_MAX_HEALTH ] &&
          ( ent->lastDamageTime + ALIEN_REGEN_DAMAGE_TIME ) < level.time )
        ent->health += BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] ) * modifier;

      if( ent->health > client->ps.stats[ STAT_MAX_HEALTH ] )
        ent->health = client->ps.stats[ STAT_MAX_HEALTH ];
    }
  }

  while( client->time10000 >= 10000 )
  {
    client->time10000 -= 10000;

    if( client->ps.weapon == WP_ALEVEL3_UPG )
    {
      int ammo, maxAmmo;

      BG_FindAmmoForWeapon( WP_ALEVEL3_UPG, &maxAmmo, NULL );
      BG_UnpackAmmoArray( WP_ALEVEL3_UPG, client->ps.ammo, client->ps.powerups, &ammo, NULL );

      if( ammo < maxAmmo )
      {
        ammo++;
        BG_PackAmmoArray( WP_ALEVEL3_UPG, client->ps.ammo, client->ps.powerups, ammo, 0 );
      }
    }
  }
}
Esempio n. 4
0
/*
===========
ClientSpawn

Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles )
{
  int                 index;
  vec3_t              spawn_origin, spawn_angles;
  gclient_t           *client;
  int                 i;
  clientPersistant_t  saved;
  clientSession_t     savedSess;
  int                 persistant[ MAX_PERSISTANT ];
  gentity_t           *spawnPoint = NULL;
  int                 flags;
  int                 savedPing;
  int                 teamLocal;
  int                 eventSequence;
  char                userinfo[ MAX_INFO_STRING ];
  vec3_t              up = { 0.0f, 0.0f, 1.0f };
  int                 maxAmmo, maxClips;
  weapon_t            weapon;


  index = ent - g_entities;
  client = ent->client;

  teamLocal = client->pers.teamSelection;

  //TA: only start client if chosen a class and joined a team
  if( client->pers.classSelection == PCL_NONE && teamLocal == PTE_NONE )
  {
    client->sess.sessionTeam = TEAM_SPECTATOR;
    client->sess.spectatorState = SPECTATOR_FREE;
  }
  else if( client->pers.classSelection == PCL_NONE )
  {
    client->sess.sessionTeam = TEAM_SPECTATOR;
    client->sess.spectatorState = SPECTATOR_LOCKED;
  }
  
  //if client is dead and following teammate, stop following before spawning
  if(ent->client->sess.spectatorClient!=-1)
  {
    ent->client->sess.spectatorClient = -1;
    ent->client->sess.spectatorState = SPECTATOR_FREE;
  }

  if( origin != NULL )
    VectorCopy( origin, spawn_origin );

  if( angles != NULL )
    VectorCopy( angles, spawn_angles );

  // find a spawn point
  // do it before setting health back up, so farthest
  // ranging doesn't count this client
  if( client->sess.sessionTeam == TEAM_SPECTATOR )
  {
    if( teamLocal == PTE_NONE )
      spawnPoint = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
    else if( teamLocal == PTE_ALIENS )
      spawnPoint = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
    else if( teamLocal == PTE_HUMANS )
      spawnPoint = G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
  }
  else
  {
    if( spawn == NULL )
    {
      G_Error( "ClientSpawn: spawn is NULL\n" );
      return;
    }

    spawnPoint = spawn;

    if( ent != spawn )
    {
      //start spawn animation on spawnPoint
      G_SetBuildableAnim( spawnPoint, BANIM_SPAWN1, qtrue );

      if( spawnPoint->biteam == PTE_ALIENS )
        spawnPoint->clientSpawnTime = ALIEN_SPAWN_REPEAT_TIME;
      else if( spawnPoint->biteam == PTE_HUMANS )
        spawnPoint->clientSpawnTime = HUMAN_SPAWN_REPEAT_TIME;
    }
  }
  client->pers.teamState.state = TEAM_ACTIVE;

  // 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

  saved = client->pers;
  savedSess = client->sess;
  savedPing = client->ps.ping;

  for( i = 0; i < MAX_PERSISTANT; i++ )
    persistant[ i ] = client->ps.persistant[ i ];

  eventSequence = client->ps.eventSequence;
  memset( client, 0, sizeof( *client ) );

  client->pers = saved;
  client->sess = savedSess;
  client->ps.ping = savedPing;
  client->lastkilled_client = -1;

  for( i = 0; i < MAX_PERSISTANT; i++ )
    client->ps.persistant[ i ] = persistant[ i ];

  client->ps.eventSequence = eventSequence;

  // increment the spawncount so the client will detect the respawn
  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;

  trap_GetUserinfo( index, userinfo, sizeof( userinfo ) );
  client->ps.eFlags = flags;

  //Com_Printf( "ent->client->pers->pclass = %i\n", ent->client->pers.classSelection );

  ent->s.groundEntityNum = ENTITYNUM_NONE;
  ent->client = &level.clients[ index ];
  ent->takedamage = qtrue;
  ent->inuse = qtrue;
  ent->classname = "player";
  ent->r.contents = CONTENTS_BODY;
  ent->clipmask = MASK_PLAYERSOLID;
  ent->die = player_die;
  ent->waterlevel = 0;
  ent->watertype = 0;
  ent->flags = 0;

  //TA: calculate each client's acceleration
  ent->evaluateAcceleration = qtrue;

  client->ps.stats[ STAT_WEAPONS ] = 0;
  client->ps.stats[ STAT_WEAPONS2 ] = 0;
  client->ps.stats[ STAT_SLOTS ] = 0;

  client->ps.eFlags = flags;
  client->ps.clientNum = index;

  BG_FindBBoxForClass( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, NULL, NULL, NULL );

  if( client->sess.sessionTeam != TEAM_SPECTATOR )
    client->pers.maxHealth = client->ps.stats[ STAT_MAX_HEALTH ] =
      BG_FindHealthForClass( ent->client->pers.classSelection );
  else
    client->pers.maxHealth = client->ps.stats[ STAT_MAX_HEALTH ] = 100;

  // clear entity values
  if( ent->client->pers.classSelection == PCL_HUMAN )
  {
    BG_AddWeaponToInventory( WP_BLASTER, client->ps.stats );
    BG_AddUpgradeToInventory( UP_MEDKIT, client->ps.stats );
    weapon = client->pers.humanItemSelection;
  }
  else if( client->sess.sessionTeam != TEAM_SPECTATOR )
    weapon = BG_FindStartWeaponForClass( ent->client->pers.classSelection );
  else
    weapon = WP_NONE;

  BG_FindAmmoForWeapon( weapon, &maxAmmo, &maxClips );
  BG_AddWeaponToInventory( weapon, client->ps.stats );
  BG_PackAmmoArray( weapon, client->ps.ammo, client->ps.powerups, maxAmmo, maxClips );

  ent->client->ps.stats[ STAT_PCLASS ] = ent->client->pers.classSelection;
  ent->client->ps.stats[ STAT_PTEAM ] = ent->client->pers.teamSelection;

  ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
  ent->client->ps.stats[ STAT_STATE ] = 0;
  VectorSet( ent->client->ps.grapplePoint, 0.0f, 0.0f, 1.0f );

  // health will count down towards max_health
  ent->health = client->ps.stats[ STAT_HEALTH ] = client->ps.stats[ STAT_MAX_HEALTH ]; //* 1.25;

  //if evolving scale health
  if( ent == spawn )
  {
    ent->health *= ent->client->pers.evolveHealthFraction;
    client->ps.stats[ STAT_HEALTH ] *= ent->client->pers.evolveHealthFraction;
  }

  //clear the credits array
  for( i = 0; i < MAX_CLIENTS; i++ )
    ent->credits[ i ] = 0;

  client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;

  G_SetOrigin( ent, spawn_origin );
  VectorCopy( spawn_origin, client->ps.origin );

#define UP_VEL  150.0f
#define F_VEL   50.0f

  //give aliens some spawn velocity
  if( client->sess.sessionTeam != TEAM_SPECTATOR &&
      client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
  {
    if( ent == spawn )
    {
      //evolution particle system
      G_AddPredictableEvent( ent, EV_ALIEN_EVOLVE, DirToByte( up ) );
    }
    else
    {
      spawn_angles[ YAW ] += 180.0f;
      AngleNormalize360( spawn_angles[ YAW ] );

      if( spawnPoint->s.origin2[ 2 ] > 0.0f )
      {
        vec3_t  forward, dir;

        AngleVectors( spawn_angles, forward, NULL, NULL );
        VectorScale( forward, F_VEL, forward );
        VectorAdd( spawnPoint->s.origin2, forward, dir );
        VectorNormalize( dir );

        VectorScale( dir, UP_VEL, client->ps.velocity );
      }

      G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 );
    }
  }
  else if( client->sess.sessionTeam != TEAM_SPECTATOR &&
           client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
  {
    spawn_angles[ YAW ] += 180.0f;
    AngleNormalize360( spawn_angles[ YAW ] );
  }

  // the respawned flag will be cleared after the attack and jump keys come up
  client->ps.pm_flags |= PMF_RESPAWNED;

  trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
  G_SetClientViewAngle( ent, spawn_angles );

  if( !( client->sess.sessionTeam == TEAM_SPECTATOR ) )
  {
    /*G_KillBox( ent );*/ //blame this if a newly spawned client gets stuck in another
    trap_LinkEntity( ent );

    // force the base weapon up
    client->ps.weapon = WP_NONE;
    client->ps.weaponstate = WEAPON_READY;
  }

  // don't allow full run speed for a bit
  client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
  client->ps.pm_time = 100;

  client->respawnTime = level.time;
  client->lastKillTime = level.time;

  client->inactivityTime = level.time + g_inactivity.integer * 1000;
  client->latched_buttons = 0;

  // set default animations
  client->ps.torsoAnim = TORSO_STAND;
  client->ps.legsAnim = LEGS_IDLE;

  if( level.intermissiontime )
    MoveClientToIntermission( ent );
  else
  {
    // fire the targets of the spawn point
    if( !spawn )
      G_UseTargets( spawnPoint, ent );

    // select the highest weapon number available, after any
    // spawn given items have fired
    client->ps.weapon = 1;

    for( i = WP_NUM_WEAPONS - 1; i > 0 ; i-- )
    {
      if( BG_InventoryContainsWeapon( i, client->ps.stats ) )
      {
        client->ps.weapon = i;
        break;
      }
    }
  }

  // run a client frame to drop exactly to the floor,
  // initialize animations and other things
  client->ps.commandTime = level.time - 100;
  ent->client->pers.cmd.serverTime = level.time;
  ClientThink( ent-g_entities );

  // positively link the client, even if the command times are weird
  if( client->sess.sessionTeam != TEAM_SPECTATOR )
  {
    BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
    VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
    trap_LinkEntity( ent );
  }

  //TA: must do this here so the number of active clients is calculated
  CalculateRanks( );

  // run the presend to set anything else
  ClientEndFrame( ent );

  // clear entity state values
  BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
}