示例#1
0
/*
=============
CG_AlienSense
=============
*/
void CG_AlienSense( rectDef_t *rect )
{
  int     i;
  vec3_t  origin;
  vec3_t  relOrigin;
  vec4_t  buildable = { 1.0f, 0.0f, 0.0f, 0.7f };
  vec4_t  client    = { 0.0f, 0.0f, 1.0f, 0.7f };

  VectorCopy( entityPositions.origin, origin );

  if( BG_UpgradeIsActive( UP_NIGHTVISION, cg.predictedPlayerState.stats ) )
  {
    for( i = 0; i < entityPositions.numHumanBuildables; i++ )
      CG_BasivisionBlip( entityPositions.humanBuildablePos[ i ],
                         0, 0,
                         150, cgs.media.basivisionBlipShader );

    for( i = 0; i < entityPositions.numHumanClients; i++ )
      CG_BasivisionBlip( entityPositions.humanClientPos[ i ],
                         60, cgs.media.basivisionBlipShader,
                         80, cgs.media.basivisionFlareShader );
  }
  else
  {
    //draw human buildables
    for( i = 0; i < entityPositions.numHumanBuildables; i++ )
    {
      VectorClear( relOrigin );
      VectorSubtract( entityPositions.humanBuildablePos[ i ], origin, relOrigin );

      if( VectorLength( relOrigin ) < ALIENSENSE_RANGE )
        CG_DrawDir( rect, relOrigin, buildable );
    }

    //draw human clients
    for( i = 0; i < entityPositions.numHumanClients; i++ )
    {
      VectorClear( relOrigin );
      VectorSubtract( entityPositions.humanClientPos[ i ], origin, relOrigin );

      if( VectorLength( relOrigin ) < ALIENSENSE_RANGE )
        CG_DrawDir( rect, relOrigin, client );
    }
  }
}
示例#2
0
/*
==================
CG_CheckLocalSounds
==================
*/
void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops )
{
    int reward, delta;

    // don't play the sounds if the player just spawned
    if( ps->persistant[ PERS_SPECSTATE ] != ops->persistant[ PERS_SPECSTATE ] )
        return;

    //hitsound
    delta = ps->persistant[PERS_HITS] - ops->persistant[PERS_HITS];

    if (cg_hitsound.integer == 3)
    {   if (delta > 165) //125
            trap_S_StartLocalSound( cgs.media.hitSound[8], CHAN_LOCAL_SOUND );
        else if (delta > 120) //100
            trap_S_StartLocalSound( cgs.media.hitSound[7], CHAN_LOCAL_SOUND );
        else if (delta > 85) //75
            trap_S_StartLocalSound( cgs.media.hitSound[6], CHAN_LOCAL_SOUND );
        else if (delta > 50)
            trap_S_StartLocalSound( cgs.media.hitSound[5], CHAN_LOCAL_SOUND );
        else if (delta > 25)
            trap_S_StartLocalSound( cgs.media.hitSound[4], CHAN_LOCAL_SOUND );
        else if (delta > 12)
            trap_S_StartLocalSound( cgs.media.hitSound[3], CHAN_LOCAL_SOUND );
        else if (delta > 8)
            trap_S_StartLocalSound( cgs.media.hitSound[2], CHAN_LOCAL_SOUND );
        else if (delta > 4)
            trap_S_StartLocalSound( cgs.media.hitSound[1], CHAN_LOCAL_SOUND );
        else if (delta > 0) //0 = no events
            trap_S_StartLocalSound( cgs.media.hitSound[0], CHAN_LOCAL_SOUND );
    }
    else if (cg_hitsound.integer == 2)
    {   if (delta > 8)
            trap_S_StartLocalSound( cgs.media.hitSound[8], CHAN_LOCAL_SOUND );
        else if (delta > 7)
            trap_S_StartLocalSound( cgs.media.hitSound[7], CHAN_LOCAL_SOUND );
        else if (delta > 6)
            trap_S_StartLocalSound( cgs.media.hitSound[6], CHAN_LOCAL_SOUND );
        else if (delta > 5)
            trap_S_StartLocalSound( cgs.media.hitSound[5], CHAN_LOCAL_SOUND );
        else if (delta > 4)
            trap_S_StartLocalSound( cgs.media.hitSound[4], CHAN_LOCAL_SOUND );
        else if (delta > 3)
            trap_S_StartLocalSound( cgs.media.hitSound[3], CHAN_LOCAL_SOUND );
        else if (delta > 2)
            trap_S_StartLocalSound( cgs.media.hitSound[2], CHAN_LOCAL_SOUND );
        else if (delta > 1)
            trap_S_StartLocalSound( cgs.media.hitSound[1], CHAN_LOCAL_SOUND );
        else if (delta > 0) //0 = no events
            trap_S_StartLocalSound( cgs.media.hitSound[0], CHAN_LOCAL_SOUND );
    }
    else if (cg_hitsound.integer && (delta > 0)) //no tonal change
        trap_S_StartLocalSound( cgs.media.hitSound[4], CHAN_LOCAL_SOUND );

    //health changes of more than -1 should make pain sounds
    //and don't play hurt sound if evolving
    if( ps->stats[ STAT_HEALTH ] < ops->stats[ STAT_HEALTH ] - 1
            && ps->stats[ STAT_CLASS ] == ops->stats[ STAT_CLASS ] )
    {
        float healthlost = ((ops->stats[ STAT_HEALTH ] - ps->stats[ STAT_HEALTH ])/ps->stats[ STAT_MAX_HEALTH ]);
        if( ps->stats[ STAT_HEALTH ] > 0 ) {
            CG_PainEvent( &cg.predictedPlayerEntity, ps->stats[ STAT_HEALTH ] );
            //play critical hit! sound if lost > 30% hp
            if (healthlost > 0.3)
                trap_S_StartLocalSound( cgs.media.hitSound[9], CHAN_LOCAL_SOUND );
        }
    }
    //Don't spam it if the user is spamming spacebar up a stair (Urasai! Annoying!)
    if(cg_doublejumpsound.integer &&
            ps->persistant[PERS_DOUBLEJUMPED] > ops->persistant[PERS_DOUBLEJUMPED])
    {
        trap_S_StartLocalSound( cgs.media.doublejumpsound, CHAN_LOCAL_SOUND );
    }

    // health changes of more than -1 should make pain sounds
    if( ps->stats[ STAT_HEALTH ] < ops->stats[ STAT_HEALTH ] - 1 )
    {
        if( ps->stats[ STAT_HEALTH ] > 0 )
            CG_PainEvent( &cg.predictedPlayerEntity, ps->stats[ STAT_HEALTH ] );
    }

    if( ( BG_UpgradeIsActive( UP_JETPACK, ps->stats ) ||
            ( cg.predictedPlayerEntity.jetPackJumpTime + 1000 > cg.time &&
              cg.predictedPlayerEntity.jetPackJumpTime + 250 < cg.time ) ) &&
            ps->stats[ STAT_FUEL ] <= JETPACK_FUEL_LOW )
    {
        static int last = 0;

        if( last + 740 < cg.time )
        {
            trap_S_StartSound( NULL, cg.predictedPlayerState.clientNum, CHAN_AUTO, cgs.media.jetpackLowFuelSound );
            last = cg.time;
        }
    }

    // if we are going into the intermission, don't start any voices
    if( cg.intermissionStarted )
        return;

    // reward sounds
    reward = qfalse;
}
示例#3
0
static AIValue_t haveUpgrade( gentity_t *self, const AIValue_t *params )
{
	int upgrade = AIUnBoxInt( params[ 0 ] );
	return AIBoxInt( !BG_UpgradeIsActive( upgrade, self->client->ps.stats ) && BG_InventoryContainsUpgrade( upgrade, self->client->ps.stats ) );
}
示例#4
0
/*
==============
ClientThink

This will be called once for each client frame, which will
usually be a couple times for each server frame on fast clients.

If "g_synchronousClients 1" is set, this will be called exactly
once for each server frame, which makes for smooth demo recording.
==============
*/
void ClientThink_real( gentity_t *ent )
{
  gclient_t *client;
  pmove_t   pm;
  int       oldEventSequence;
  int       msec;
  usercmd_t *ucmd;

  client = ent->client;

  // don't think if the client is not yet connected (and thus not yet spawned in)
  if( client->pers.connected != CON_CONNECTED )
    return;

  // mark the time, so the connection sprite can be removed
  ucmd = &ent->client->pers.cmd;

  // sanity check the command time to prevent speedup cheating
  if( ucmd->serverTime > level.time + 200 )
  {
    ucmd->serverTime = level.time + 200;
//    G_Printf("serverTime <<<<<\n" );
  }

  if( ucmd->serverTime < level.time - 1000 )
  {
    ucmd->serverTime = level.time - 1000;
//    G_Printf("serverTime >>>>>\n" );
  }

  msec = ucmd->serverTime - client->ps.commandTime;
  // following others may result in bad times, but we still want
  // to check for follow toggles
  if( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW )
    return;

  if( msec > 200 )
    msec = 200;

  if( pmove_msec.integer < 8 )
    trap_Cvar_Set( "pmove_msec", "8" );
  else if( pmove_msec.integer > 33 )
    trap_Cvar_Set( "pmove_msec", "33" );

  if( pmove_fixed.integer || client->pers.pmoveFixed )
  {
    ucmd->serverTime = ( ( ucmd->serverTime + pmove_msec.integer - 1 ) / pmove_msec.integer ) * pmove_msec.integer;
    //if (ucmd->serverTime - client->ps.commandTime <= 0)
    //  return;
  }

  //
  // check for exiting intermission
  //
  if( level.intermissiontime )
  {
    ClientIntermissionThink( client );
    return;
  }

  // spectators don't do much
  if( client->sess.sessionTeam == TEAM_SPECTATOR )
  {
    if( client->sess.spectatorState == SPECTATOR_SCOREBOARD )
      return;

    SpectatorThink( ent, ucmd );
    return;
  }

  G_UpdatePTRConnection( client );

  // check for inactivity timer, but never drop the local client of a non-dedicated server
  if( !ClientInactivityTimer( client ) )
    return;

  if( client->noclip )
    client->ps.pm_type = PM_NOCLIP;
  else if( client->ps.stats[ STAT_HEALTH ] <= 0 )
    client->ps.pm_type = PM_DEAD;
  else if( client->ps.stats[ STAT_STATE ] & SS_INFESTING ||
           client->ps.stats[ STAT_STATE ] & SS_HOVELING )
    client->ps.pm_type = PM_FREEZE;
  else if( client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED ||
           client->ps.stats[ STAT_STATE ] & SS_GRABBED )
    client->ps.pm_type = PM_GRABBED;
  else if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) )
    client->ps.pm_type = PM_JETPACK;
  else
    client->ps.pm_type = PM_NORMAL;

  if( client->ps.stats[ STAT_STATE ] & SS_GRABBED &&
      client->grabExpiryTime < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_GRABBED;

  if( client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED &&
      client->lastLockTime + 5000 < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_BLOBLOCKED;

  if( client->ps.stats[ STAT_STATE ] & SS_SLOWLOCKED &&
      client->lastLockTime + ABUILDER_BLOB_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_SLOWLOCKED;

  client->ps.stats[ STAT_BOOSTTIME ] = level.time - client->lastBoostedTime;

  if( client->ps.stats[ STAT_STATE ] & SS_BOOSTED &&
      client->lastBoostedTime + BOOST_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_BOOSTED;

  if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED &&
      client->lastPoisonCloudedTime + LEVEL1_PCLOUD_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_POISONCLOUDED;

  if( client->ps.stats[ STAT_STATE ] & SS_POISONED &&
      client->lastPoisonTime + ALIEN_POISON_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_POISONED;

  client->ps.gravity = g_gravity.value;

  if( BG_InventoryContainsUpgrade( UP_MEDKIT, client->ps.stats ) &&
      BG_UpgradeIsActive( UP_MEDKIT, client->ps.stats ) )
  {
    //if currently using a medkit or have no need for a medkit now
    if( client->ps.stats[ STAT_STATE ] & SS_MEDKIT_ACTIVE ||
        ( client->ps.stats[ STAT_HEALTH ] == client->ps.stats[ STAT_MAX_HEALTH ] &&
          !( client->ps.stats[ STAT_STATE ] & SS_POISONED ) ) )
    {
      BG_DeactivateUpgrade( UP_MEDKIT, client->ps.stats );
    }
    else if( client->ps.stats[ STAT_HEALTH ] > 0 )
    {
      //remove anti toxin
      BG_DeactivateUpgrade( UP_MEDKIT, client->ps.stats );
      BG_RemoveUpgradeFromInventory( UP_MEDKIT, client->ps.stats );

      client->ps.stats[ STAT_STATE ] &= ~SS_POISONED;
      client->poisonImmunityTime = level.time + MEDKIT_POISON_IMMUNITY_TIME;

      client->ps.stats[ STAT_STATE ] |= SS_MEDKIT_ACTIVE;
      client->lastMedKitTime = level.time;
      client->medKitHealthToRestore =
        client->ps.stats[ STAT_MAX_HEALTH ] - client->ps.stats[ STAT_HEALTH ];
      client->medKitIncrementTime = level.time +
        ( MEDKIT_STARTUP_TIME / MEDKIT_STARTUP_SPEED );

      G_AddEvent( ent, EV_MEDKIT_USED, 0 );
    }
  }

  if( BG_InventoryContainsUpgrade( UP_GRENADE, client->ps.stats ) &&
      BG_UpgradeIsActive( UP_GRENADE, client->ps.stats ) )
  {
    int lastWeapon = ent->s.weapon;

    //remove grenade
    BG_DeactivateUpgrade( UP_GRENADE, client->ps.stats );
    BG_RemoveUpgradeFromInventory( UP_GRENADE, client->ps.stats );

    //M-M-M-M-MONSTER HACK
    ent->s.weapon = WP_GRENADE;
    FireWeapon( ent );
    ent->s.weapon = lastWeapon;
  }

  // set speed
  client->ps.speed = g_speed.value * BG_FindSpeedForClass( client->ps.stats[ STAT_PCLASS ] );

  if( client->lastCreepSlowTime + CREEP_TIMEOUT < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_CREEPSLOWED;

  //randomly disable the jet pack if damaged
  if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) &&
      BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) )
  {
    if( ent->lastDamageTime + JETPACK_DISABLE_TIME > level.time )
    {
      if( random( ) > JETPACK_DISABLE_CHANCE )
        client->ps.pm_type = PM_NORMAL;
    }

    //switch jetpack off if no reactor
    if( !level.reactorPresent )
      BG_DeactivateUpgrade( UP_JETPACK, client->ps.stats );
  }

  // set up for pmove
  oldEventSequence = client->ps.eventSequence;

  memset( &pm, 0, sizeof( pm ) );

  if( !( ucmd->buttons & BUTTON_TALK ) ) //&& client->ps.weaponTime <= 0 ) //TA: erk more server load
  {
    switch( client->ps.weapon )
    {
      case WP_ALEVEL0:
        if( client->ps.weaponTime <= 0 )
          pm.autoWeaponHit[ client->ps.weapon ] = CheckVenomAttack( ent );
        break;

      case WP_ALEVEL1:
      case WP_ALEVEL1_UPG:
        CheckGrabAttack( ent );
        break;

      case WP_ALEVEL3:
      case WP_ALEVEL3_UPG:
        if( client->ps.weaponTime <= 0 )
          pm.autoWeaponHit[ client->ps.weapon ] = CheckPounceAttack( ent );
        break;

      default:
        break;
    }
  }

  if( ent->flags & FL_FORCE_GESTURE )
  {
    ent->flags &= ~FL_FORCE_GESTURE;
    ent->client->pers.cmd.buttons |= BUTTON_GESTURE;
  }

  pm.ps = &client->ps;
  pm.cmd = *ucmd;

  if( pm.ps->pm_type == PM_DEAD )
    pm.tracemask = MASK_PLAYERSOLID; // & ~CONTENTS_BODY;

  if( pm.ps->stats[ STAT_STATE ] & SS_INFESTING ||
      pm.ps->stats[ STAT_STATE ] & SS_HOVELING )
    pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
  else
    pm.tracemask = MASK_PLAYERSOLID;

  pm.trace = trap_Trace;
  pm.pointcontents = trap_PointContents;
  pm.debugLevel = g_debugMove.integer;
  pm.noFootsteps = (qboolean)0;

  pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed;
  pm.pmove_msec = pmove_msec.integer;

  VectorCopy( client->ps.origin, client->oldOrigin );

  // moved from after Pmove -- potentially the cause of
  // future triggering bugs
  if( !ent->client->noclip )
    G_TouchTriggers( ent );

  Pmove( &pm );

  // save results of pmove
  if( ent->client->ps.eventSequence != oldEventSequence )
    ent->eventTime = level.time;

  if( g_smoothClients.integer )
    BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
  else
    BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );

  SendPendingPredictableEvents( &ent->client->ps );

  if( !( ent->client->ps.eFlags & EF_FIRING ) )
    client->fireHeld = qfalse;    // for grapple
  if( !( ent->client->ps.eFlags & EF_FIRING2 ) )
    client->fire2Held = qfalse;

  // use the snapped origin for linking so it matches client predicted versions
  VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );

  VectorCopy( pm.mins, ent->r.mins );
  VectorCopy( pm.maxs, ent->r.maxs );

  ent->waterlevel = pm.waterlevel;
  ent->watertype = pm.watertype;

  // execute client events
  ClientEvents( ent, oldEventSequence );

  // link entity now, after any personal teleporters have been used
  trap_LinkEntity( ent );

  // NOTE: now copy the exact origin over otherwise clients can be snapped into solid
  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;

  // swap and latch button actions
  client->oldbuttons = client->buttons;
  client->buttons = ucmd->buttons;
  client->latched_buttons |= client->buttons & ~client->oldbuttons;

  if( ( client->buttons & BUTTON_GETFLAG ) && !( client->oldbuttons & BUTTON_GETFLAG ) &&
       client->ps.stats[ STAT_HEALTH ] > 0 )
  {
    trace_t   trace;
    vec3_t    view, point;
    gentity_t *traceEnt;

    if( client->ps.stats[ STAT_STATE ] & SS_HOVELING )
    {
      gentity_t *hovel = client->hovel;

      //only let the player out if there is room
      if( !AHovel_Blocked( hovel, ent, qtrue ) )
      {
        //prevent lerping
        client->ps.eFlags ^= EF_TELEPORT_BIT;
        client->ps.eFlags &= ~EF_NODRAW;

        //client leaves hovel
        client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING;

        //hovel is empty
        G_setBuildableAnim( hovel, BANIM_ATTACK2, qfalse );
        hovel->active = qfalse;
      }
      else
      {
        //exit is blocked
        G_TriggerMenu( ent->client->ps.clientNum, MN_A_HOVEL_BLOCKED );
      }
    }
    else
    {
#define USE_OBJECT_RANGE 64

      int       entityList[ MAX_GENTITIES ];
      vec3_t    range = { USE_OBJECT_RANGE, USE_OBJECT_RANGE, USE_OBJECT_RANGE };
      vec3_t    mins, maxs;
      int       i, num;

      //TA: look for object infront of player
      AngleVectors( client->ps.viewangles, view, NULL, NULL );
      VectorMA( client->ps.origin, USE_OBJECT_RANGE, view, point );
      trap_Trace( &trace, client->ps.origin, NULL, NULL, point, ent->s.number, MASK_SHOT );

      traceEnt = &g_entities[ trace.entityNum ];

      if( traceEnt && traceEnt->biteam == client->ps.stats[ STAT_PTEAM ] && traceEnt->use )
        traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context
      else
      {
        //no entity in front of player - do a small area search

        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++ )
        {
          traceEnt = &g_entities[ entityList[ i ] ];

          if( traceEnt && traceEnt->biteam == client->ps.stats[ STAT_PTEAM ] && traceEnt->use )
          {
            traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context
            break;
          }
        }

        if( i == num && client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
        {
          if( BG_UpgradeClassAvailable( &client->ps ) )
          {
            //no nearby objects and alien - show class menu
            G_TriggerMenu( ent->client->ps.clientNum, MN_A_INFEST );
          }
          else
          {
            //flash frags
            G_AddEvent( ent, EV_ALIEN_EVOLVE_FAILED, 0 );
          }
        }
      }
    }
  }

  // check for respawning
  if( client->ps.stats[ STAT_HEALTH ] <= 0 )
  {
    // wait for the attack button to be pressed
    if( level.time > client->respawnTime )
    {
      // forcerespawn is to prevent users from waiting out powerups
      if( g_forcerespawn.integer > 0 &&
        ( level.time - client->respawnTime ) > 0 )
      {
        respawn( ent );
        return;
      }

      // pressing attack or use is the normal respawn method
      if( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) )
      {
        respawn( ent );
      }
    }
    return;
  }

  if( level.framenum > client->retriggerArmouryMenu && client->retriggerArmouryMenu )
  {
    G_TriggerMenu( client->ps.clientNum, MN_H_ARMOURY );

    client->retriggerArmouryMenu = 0;
  }

  // Give clients some credit periodically
  if( ent->client->lastKillTime + FREEKILL_PERIOD < level.time )
  {
    if( g_suddenDeathTime.integer &&
        ( level.time - level.startTime >= g_suddenDeathTime.integer * 60000 ) )
    {
      //gotta love logic like this eh?
    }
    else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
      G_AddCreditToClient( ent->client, FREEKILL_ALIEN, qtrue );
    else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
      G_AddCreditToClient( ent->client, FREEKILL_HUMAN, qtrue );

    ent->client->lastKillTime = level.time;
  }

  // perform once-a-second actions
  ClientTimerActions( ent, msec );
  
  if( ent->suicideTime > 0 && ent->suicideTime < level.time )
  {
    ent->flags &= ~FL_GODMODE;
    ent->client->ps.stats[ STAT_HEALTH ] = ent->health = 0;
    player_die( ent, ent, ent, 100000, MOD_SUICIDE );

    ent->suicideTime = 0;
  }
}
示例#5
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 );
      }
    }
  }
}