text: // Client side entity management functions
  
  #include <memory.h>
  
  #include "hud.h"
  #include "cl_util.h"
  #include "common/const.h"
  #include "common/entity_types.h"
  #include "common/studio_event.h" // def. of mstudioevent_t
  #include "common/r_efx.h"
  #include "common/event_api.h"
  #include "pm_shared/pm_defs.h"
  #include "common/pmtrace.h"	
  #include "pm_shared/pm_shared.h"
  #include "mod/AvHParticleSystemManager.h"
  #include "mod/AvHSpecials.h"
  
  #include "engine/APIProxy.h"
  #include "Exports.h"
  
  void Game_AddObjects( void );
  
  extern vec3_t v_origin;
  double gClientTimeLastUpdate;
  int g_iAlive = 1;
  
  bool gTempEntClearAllFlag = false;
  
  /*
  ========================
  HUD_AddEntity
  	Return 0 to filter entity from visible list for rendering
  ========================
  */
  int CL_DLLEXPORT HUD_AddEntity( int type, struct cl_entity_s *ent, const char *modelname )
  {
  //	RecClAddEntity(type, ent, modelname);
  
  	gHUD.ClientProcessEntity(&ent->curstate);
  
  	// Particle entities have a model so they will be forced through this function,
  	// but we don't want to draw them.
  	int theSpecialState = ent->curstate.iuser3;
  	if( (theSpecialState == AVH_USER3_PARTICLE_ON) ||
  		(theSpecialState == AVH_USER3_PARTICLE_OFF) ||
  		(theSpecialState == AVH_USER3_AUDIO_ON) ||
  		(theSpecialState == AVH_USER3_AUDIO_OFF) ||
  		(theSpecialState == AVH_USER3_NOBUILD))
  	{
  		return 0;
  	}
  
  	switch ( type )
  	{
  	case ET_NORMAL:
  	case ET_PLAYER:
  	case ET_BEAM:
  	case ET_TEMPENTITY:
  	case ET_FRAGMENTED:
  	default:
  		break;
  	}
  
  	if ( g_iUser1 == OBS_IN_EYE  && ent->index == g_iUser2 )
  	{
  		// Don't draw the player we are following in eye
          return 0;
  	}
      
  	return 1;
  
  }
  
  /*
  =========================
  HUD_TxferLocalOverrides
  
  The server sends us our origin with extra precision as part of the clientdata structure, not during the normal
  playerstate update in entity_state_t.  In order for these overrides to eventually get to the appropriate playerstate
  structure, we need to copy them into the state structure at this point.
  =========================
  */
  void CL_DLLEXPORT HUD_TxferLocalOverrides( struct entity_state_s *state, const struct clientdata_s *client )
  {
  //	RecClTxferLocalOverrides(state, client);
  
  	VectorCopy( client->origin, state->origin );
  
  	// Spectator
  	state->iuser1 = client->iuser1;
  	state->iuser2 = client->iuser2;
  	state->iuser3 = client->iuser3;
  	state->iuser4 = client->iuser4;
  
  	state->fuser1 = client->fuser1;
  	state->fuser2 = client->fuser2;
  	state->fuser3 = client->fuser3;
  	state->fuser4 = client->fuser4;
  
  	state->vuser1 = client->vuser1;
  	//state->vuser2 = client->vuser2;
  	//state->vuser3 = client->vuser3;
  	state->vuser4 = client->vuser4;
  }
  
  /*
  =========================
  HUD_ProcessPlayerState
  
  We have received entity_state_t for this player over the network.  We need to copy appropriate fields to the
  playerstate structure
  =========================
  */
  void CL_DLLEXPORT HUD_ProcessPlayerState( struct entity_state_s *dst, const struct entity_state_s *src )
  {
  //	RecClProcessPlayerState(dst, src);
  
  	// Copy in network data
  	VectorCopy( src->origin, dst->origin );
  	VectorCopy( src->angles, dst->angles );
  
  	VectorCopy( src->velocity, dst->velocity );
  
  	dst->frame					= src->frame;
  	dst->modelindex				= src->modelindex;
  	dst->skin					= src->skin;
  	dst->effects				= src->effects;
  	dst->weaponmodel			= src->weaponmodel;
  	dst->movetype				= src->movetype;
  	dst->sequence				= src->sequence;
  	dst->animtime				= src->animtime;
  	
  	dst->solid					= src->solid;
  	
  	dst->rendermode				= src->rendermode;
  	dst->renderamt				= src->renderamt;	
  	dst->rendercolor.r			= src->rendercolor.r;
  	dst->rendercolor.g			= src->rendercolor.g;
  	dst->rendercolor.b			= src->rendercolor.b;
  	dst->renderfx				= src->renderfx;
  
  	dst->framerate				= src->framerate;
  	dst->body					= src->body;
  
  	memcpy( &dst->controller[0], &src->controller[0], 4 * sizeof( byte ) );
  	memcpy( &dst->blending[0], &src->blending[0], 2 * sizeof( byte ) );
  
  	VectorCopy( src->basevelocity, dst->basevelocity );
  
  	dst->friction				= src->friction;
  	dst->gravity				= src->gravity;
  	dst->gaitsequence			= src->gaitsequence;
  	dst->spectator				= src->spectator;
  	dst->usehull				= src->usehull;
  	dst->playerclass			= src->playerclass;
  	dst->team					= src->team;
  	dst->colormap				= src->colormap;
  	
  	// Save off some data so other areas of the Client DLL can get to it
  	cl_entity_t *player = gEngfuncs.GetLocalPlayer();	// Get the local player's index
  	if ( dst->number == player->index )
  	{
  		g_iPlayerClass = dst->playerclass;
  		g_iTeamNumber = dst->team;
  
  		g_iUser1 = src->iuser1;
  		g_iUser2 = src->iuser2;
  		g_iUser3 = src->iuser3;
  
  	}
  	
  	// AvH
  	// Copy special movement mode...is this needed?
  	dst->iuser1 = src->iuser1;
  	dst->iuser2 = src->iuser2;
  	dst->iuser3 = src->iuser3;
  	dst->iuser4 = src->iuser4;
  
  	dst->fuser1 = src->fuser1;
  	dst->fuser2 = src->fuser2;
  	dst->fuser3 = src->fuser3;
  	dst->fuser4 = src->fuser4;
  
  	dst->vuser1 = src->vuser1;
  	//dst->vuser2 = src->vuser2;
  	//dst->vuser3 = src->vuser3;
  	dst->vuser4 = src->vuser4;
  }
  
  /*
  =========================
  HUD_TxferPredictionData
  
  Because we can predict an arbitrary number of frames before the server responds with an update, we need to be able to copy client side prediction data in
   from the state that the server ack'd receiving, which can be anywhere along the predicted frame path ( i.e., we could predict 20 frames into the future and the server ack's
   up through 10 of those frames, so we need to copy persistent client-side only state from the 10th predicted frame to the slot the server
   update is occupying.
  =========================
  */
  void CL_DLLEXPORT HUD_TxferPredictionData ( struct entity_state_s *ps, const struct entity_state_s *pps, struct clientdata_s *pcd, const struct clientdata_s *ppcd, struct weapon_data_s *wd, const struct weapon_data_s *pwd )
  {
  //	RecClTxferPredictionData(ps, pps, pcd, ppcd, wd, pwd);
  
  	ps->oldbuttons				= pps->oldbuttons;
  	ps->flFallVelocity			= pps->flFallVelocity;
  	ps->iStepLeft				= pps->iStepLeft;
  	ps->playerclass				= pps->playerclass;
  
  	pcd->viewmodel				= ppcd->viewmodel;
  	pcd->m_iId					= ppcd->m_iId;
  	pcd->ammo_shells			= ppcd->ammo_shells;
  	pcd->ammo_nails				= ppcd->ammo_nails;
  	pcd->ammo_cells				= ppcd->ammo_cells;
  	pcd->ammo_rockets			= ppcd->ammo_rockets;
  	pcd->m_flNextAttack			= ppcd->m_flNextAttack;
  	pcd->fov					= ppcd->fov;
  	pcd->weaponanim				= ppcd->weaponanim;
  	pcd->tfstate				= ppcd->tfstate;
  	pcd->maxspeed				= ppcd->maxspeed;
  
  	pcd->deadflag				= ppcd->deadflag;
  
  	// Spectating or not dead == get control over view angles.
  	g_iAlive = ( ppcd->iuser1 || ( pcd->deadflag == DEAD_NO ) ) ? 1 : 0;
  
  	// Spectator
  	pcd->iuser1					= ppcd->iuser1;
  	pcd->iuser2					= ppcd->iuser2;
  	pcd->iuser3					= ppcd->iuser3;
  	pcd->iuser4					= ppcd->iuser4;
  
  	pcd->fuser1					= ppcd->fuser1;
  	pcd->fuser2					= ppcd->fuser2;
  	pcd->fuser3					= ppcd->fuser3;
  	pcd->fuser4					= ppcd->fuser4;
  
  	if( gEngfuncs.IsSpectateOnly() )
  	{
  		// in specator mode we tell the engine who we want to spectate and how
  		// iuser3 is not used for duck prevention (since the spectator can't duck at all)
  		pcd->iuser1 = g_iUser1;	// observer mode
  		pcd->iuser2 = g_iUser2; // first target
  		pcd->iuser3 = g_iUser3; // second target
  
  	}
  
  	VectorCopy( ppcd->vuser1, pcd->vuser1 );
  	//VectorCopy( ppcd->vuser2, pcd->vuser2 );
  	//VectorCopy( ppcd->vuser3, pcd->vuser3 );
  	VectorCopy( ppcd->vuser4, pcd->vuser4 );
  
  	memcpy( wd, pwd, 32 * sizeof( weapon_data_t ) );
  }
  
  /*
  //#define TEST_IT
  #if defined( TEST_IT )
  
  cl_entity_t mymodel[9];
  
  void MoveModel( void )
  {
  	cl_entity_t *player;
  	int i, j;
  	int modelindex;
  	struct model_s *mod;
  
  	// Load it up with some bogus data
  	player = gEngfuncs.GetLocalPlayer();
  	if ( !player )
  		return;
  
  	mod = gEngfuncs.CL_LoadModel( "models/sentry3.mdl", &modelindex );
  	for ( i = 0; i < 3; i++ )
  	{
  		for ( j = 0; j < 3; j++ )
  		{
  			// Don't draw over ourself...
  			if ( ( i == 1 ) && ( j == 1 ) )
  				continue;
  
  			mymodel[ i * 3 + j ] = *player;
  
  			mymodel[ i * 3 + j ].player = 0;
  
  			mymodel[ i * 3 + j ].model = mod;
  			mymodel[ i * 3 + j ].curstate.modelindex = modelindex;
  		
  				// Move it out a bit
  			mymodel[ i * 3 + j ].origin[0] = player->origin[0] + 50 * ( 1 - i );
  			mymodel[ i * 3 + j ].origin[1] = player->origin[1] + 50 * ( 1 - j );
  
  			gEngfuncs.CL_CreateVisibleEntity( ET_NORMAL, &mymodel[i*3+j] );
  		}
  	}
  
  }
  
  #endif
  */
  //#define TRACE_TEST
  #if defined( TRACE_TEST )
  
  extern int hitent;
  
  cl_entity_t hit;
  
  void TraceModel( void )
  {
  	cl_entity_t *ent;
  
  	if ( hitent <= 0 )
  		return;
  
  	// Load it up with some bogus data
  	ent = gEngfuncs.GetEntityByIndex( hitent );
  	if ( !ent )
  		return;
  
  	hit = *ent;
  	//hit.curstate.rendermode = kRenderTransTexture;
  	//hit.curstate.renderfx = kRenderFxGlowShell;
  	//hit.curstate.renderamt = 100;
  
  	hit.origin[2] += 40;
  
  	gEngfuncs.CL_CreateVisibleEntity( ET_NORMAL, &hit );
  }
  
  #endif
  
  
  /*
  void ParticleCallback( struct particle_s *particle, float frametime )
  {
  	int i;
  
  	for ( i = 0; i < 3; i++ )
  	{
  		particle->org[ i ] += particle->vel[ i ] * frametime;
  	}
  }
  
  cvar_t *color = NULL;
  void Particles( void )
  {
  	static float lasttime;
  	float curtime;
  	
  	curtime = gEngfuncs.GetClientTime();
  
  	if ( ( curtime - lasttime ) < 2.0 )
  		return;
  
  	if ( !color )
  	{
  		color = gEngfuncs.pfnRegisterVariable ( "color","255 0 0", 0 );
  	}
  
  	lasttime = curtime;
  
  	// Create a few particles
  	particle_t *p;
  	int i, j;
  
  	for ( i = 0; i < 1000; i++ )
  	{
  		int r, g, b;
  		p = gEngfuncs.pEfxAPI->R_AllocParticle( ParticleCallback );
  		if ( !p )
  			break;
  
  		for ( j = 0; j < 3; j++ )
  		{
  			p->org[ j ] = v_origin[ j ] + gEngfuncs.pfnRandomFloat( -32.0, 32.0 );;
  			p->vel[ j ] = gEngfuncs.pfnRandomFloat( -100.0, 100.0 );
  		}
  
  		if ( color )
  		{
  			sscanf( color->string, "%i %i %i", &r, &g, &b );
  		}
  		else
  		{
  			r = 192;
  			g = 0;
  			b = 0;
  		}
  
  		p->color = 	gEngfuncs.pEfxAPI->R_LookupColor( r, g, b );
  		gEngfuncs.pEfxAPI->R_GetPackedColor( &p->packedColor, p->color );
  
  		// p->die is set to current time so all you have to do is add an additional time to it
  		p->die += 3.0;
  	}
  }
  */
  
  /*
  void TempEntCallback ( struct tempent_s *ent, float frametime, float currenttime )
  {
  	int i;
  
  	for ( i = 0; i < 3; i++ )
  	{
  		ent->entity.curstate.origin[ i ] += ent->entity.baseline.origin[ i ] * frametime;
  	}
  }
  
  void TempEnts( void )
  {
  	static float lasttime;
  	float curtime;
  	
  	curtime = gEngfuncs.GetClientTime();
  
  	if ( ( curtime - lasttime ) < 10.0 )
  		return;
  
  	lasttime = curtime;
  
  	TEMPENTITY *p;
  	int i, j;
  	struct model_s *mod;
  	vec3_t origin;
  	int index;
  
  	mod = gEngfuncs.CL_LoadModel( "sprites/laserdot.spr", &index );
  
  	for ( i = 0; i < 100; i++ )
  	{
  		for ( j = 0; j < 3; j++ )
  		{
  			origin[ j ] = v_origin[ j ];
  			if ( j != 2 )
  			{
  				origin[ j ] += 75;
  			}
  		}
  
  		p = gEngfuncs.pEfxAPI->CL_TentEntAllocCustom( (float *)&origin, mod, 0, TempEntCallback );
  		if ( !p )
  			break;
  
  		for ( j = 0; j < 3; j++ )
  		{
  			p->entity.curstate.origin[ j ] = origin[ j ];
  
  			// Store velocity in baseline origin
  			p->entity.baseline.origin[ j ] = gEngfuncs.pfnRandomFloat( -100, 100 );
  		}
  
  		// p->die is set to current time so all you have to do is add an additional time to it
  		p->die += 10.0;
  	}
  }
  */
  
  #if defined( BEAM_TEST )
  // Note can't index beam[ 0 ] in Beam callback, so don't use that index
  // Room for 1 beam ( 0 can't be used )
  static cl_entity_t beams[ 2 ];
  
  void BeamEndModel( void )
  {
  	cl_entity_t *player, *model;
  	int modelindex;
  	struct model_s *mod;
  
  	// Load it up with some bogus data
  	player = gEngfuncs.GetLocalPlayer();
  	if ( !player )
  		return;
  
  	mod = gEngfuncs.CL_LoadModel( "models/sentry3.mdl", &modelindex );
  	if ( !mod )
  		return;
  
  	// Slot 1
  	model = &beams[ 1 ];
  
  	*model = *player;
  	model->player = 0;
  	model->model = mod;
  	model->curstate.modelindex = modelindex;
  		
  	// Move it out a bit
  	model->origin[0] = player->origin[0] - 100;
  	model->origin[1] = player->origin[1];
  
  	model->attachment[0] = model->origin;
  	model->attachment[1] = model->origin;
  	model->attachment[2] = model->origin;
  	model->attachment[3] = model->origin;
  
  	gEngfuncs.CL_CreateVisibleEntity( ET_NORMAL, model );
  }
  
  void Beams( void )
  {
  	static float lasttime;
  	float curtime;
  	struct model_s *mod;
  	int index;
  
  	BeamEndModel();
  	
  	curtime = gEngfuncs.GetClientTime();
  	float end[ 3 ];
  
  	if ( ( curtime - lasttime ) < 10.0 )
  		return;
  
  	mod = gEngfuncs.CL_LoadModel( "sprites/laserbeam.spr", &index );
  	if ( !mod )
  		return;
  
  	lasttime = curtime;
  
  	end [ 0 ] = v_origin.x + 100;
  	end [ 1 ] = v_origin.y + 100;
  	end [ 2 ] = v_origin.z;
  
  	BEAM *p1;
  	p1 = gEngfuncs.pEfxAPI->R_BeamEntPoint( -1, end, index,
  		10.0, 2.0, 0.3, 1.0, 5.0, 0.0, 1.0, 1.0, 1.0, 1.0 );
  }
  #endif
  
  /*
  =========================
  HUD_CreateEntities
  	
  Gives us a chance to add additional entities to the render this frame
  =========================
  */
  void CL_DLLEXPORT HUD_CreateEntities( void )
  {
  //	RecClCreateEntities();
  
  	// e.g., create a persistent cl_entity_t somewhere.
  	// Load an appropriate model into it ( gEngfuncs.CL_LoadModel )
  	// Call gEngfuncs.CL_CreateVisibleEntity to add it to the visedicts list
  /*
  #if defined( TEST_IT )
  	MoveModel();
  #endif
  */
  #if defined( TRACE_TEST )
  	TraceModel();
  #endif
  
  /*
  	Particles();
  */
  /*
  	TempEnts();
  */
  
  #if defined( BEAM_TEST )
  	Beams();
  #endif
  
  	// Add in any game specific objects
  	Game_AddObjects();
  
  	GetClientVoiceMgr()->CreateEntities();
  }
  
  /*
  =========================
  HUD_StudioEvent
  
  The entity's studio model description indicated an event was
  fired during this frame, handle the event by it's tag ( e.g., muzzleflash, sound )
  =========================
  */
  void CL_DLLEXPORT HUD_StudioEvent( const struct mstudioevent_s *event, const struct cl_entity_s *entity )
  {
  //	RecClStudioEvent(event, entity);
  
  	switch( event->event )
  	{
  	case 5001:
  		gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[0], atoi( event->options) );
  		break;
  	case 5011:
  		gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[1], atoi( event->options) );
  		break;
  	case 5021:
  		gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[2], atoi( event->options) );
  		break;
  	case 5031:
  		gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[3], atoi( event->options) );
  		break;
  	case 5002:
  		gEngfuncs.pEfxAPI->R_SparkEffect( (float *)&entity->attachment[0], atoi( event->options), -100, 100 );
  		break;
  	// Client side sound
  	case 5004:		
  		gEngfuncs.pfnPlaySoundByNameAtLocation( (char *)event->options, 1.0, (float *)&entity->attachment[0] );
  		break;
  
  	// Particles!
  	case 7000:
  		AvHParticleSystemManager::Instance()->CreateParticleSystem(string(event->options), entity->attachment[0]);
  		break;
  
  	case 7010:
  		AvHParticleSystemManager::Instance()->CreateParticleSystem(string(event->options), entity->attachment[1]);
  		break;
  		
  	case 7020:
  		AvHParticleSystemManager::Instance()->CreateParticleSystem(string(event->options), entity->attachment[2]);
  		break;
  		
  	case 7030:
  		AvHParticleSystemManager::Instance()->CreateParticleSystem(string(event->options), entity->attachment[3]);
  		break;
  		
  	default:
  		break;
  	}
  }
  
  /*
  =================
  CL_UpdateTEnts
  
  Simulation and cleanup of temporary entities
  =================
  */
  void CL_DLLEXPORT HUD_TempEntUpdate (
  	double frametime,   // Simulation time
  	double client_time, // Absolute time on client
  	double cl_gravity,  // True gravity on client
  	TEMPENTITY **ppTempEntFree,   // List of freed temporary ents
  	TEMPENTITY **ppTempEntActive, // List 
  	int		( *Callback_AddVisibleEntity )( cl_entity_t *pEntity ),
  	void	( *Callback_TempEntPlaySound )( TEMPENTITY *pTemp, float damp ) )
  {
  //	RecClTempEntUpdate(frametime, client_time, cl_gravity, ppTempEntFree, ppTempEntActive, Callback_AddVisibleEntity, Callback_TempEntPlaySound);
  
  	static int gTempEntFrame = 0;
  	int			i;
  	TEMPENTITY	*pTemp, *pnext, *pprev;
  	float		freq, gravity, gravitySlow, life, fastFreq;
  
  	// don't simulate when we're paused
  	if(gClientTimeLastUpdate != client_time)
  	{
  		gClientTimeLastUpdate = client_time;
  		
  		// Nothing to simulate
  		if ( *ppTempEntActive )		
  		{
  			// in order to have tents collide with players, we have to run the player prediction code so
  			// that the client has the player list. We run this code once when we detect any COLLIDEALL 
  			// tent, then set this BOOL to true so the code doesn't get run again if there's more than
  			// one COLLIDEALL ent for this update. (often are).
  			gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, true );
  			
  			// Store off the old count
  			gEngfuncs.pEventAPI->EV_PushPMStates();
  			
  			// Now add in all of the players.
  			gEngfuncs.pEventAPI->EV_SetSolidPlayers ( -1 );	
  			
  			// !!!BUGBUG	-- This needs to be time based
  			gTempEntFrame = (gTempEntFrame+1) & 31;
  			
  			pTemp = *ppTempEntActive;
  
  			bool theIsDone = false;
  			
  			// !!! Don't simulate while paused....  This is sort of a hack, revisit.
  			if ( frametime <= 0 )
  			{
  				while ( pTemp )
  				{
  					if ( !(pTemp->flags & FTENT_NOMODEL ) )
  					{
  						Callback_AddVisibleEntity( &pTemp->entity );
  					}
  					pTemp = pTemp->next;
  				}
  				//goto finish;
  				
  				theIsDone = true;
  			}
  
  			if(!theIsDone)
  			{
  				pprev = NULL;
  				freq = client_time * 0.01;
  				fastFreq = client_time * 5.5;
  				gravity = -frametime * cl_gravity;
  				gravitySlow = gravity * 0.5;
  				
  				while ( pTemp )
  				{
  					int active;
  					
  					active = 1;
  					
  					life = pTemp->die - client_time;
  					pnext = pTemp->next;
  					if ( life < 0 )
  					{
  						if ( pTemp->flags & FTENT_FADEOUT )
  						{
  							if (pTemp->entity.curstate.rendermode == kRenderNormal)
  								pTemp->entity.curstate.rendermode = kRenderTransTexture;
  							pTemp->entity.curstate.renderamt = pTemp->entity.baseline.renderamt * ( 1 + life * pTemp->fadeSpeed );
  							if ( pTemp->entity.curstate.renderamt <= 0 )
  								active = 0;
  							
  						}
  						else 
  							active = 0;
  					}
  					if ( !active )		// Kill it
  					{
  						pTemp->next = *ppTempEntFree;
  						*ppTempEntFree = pTemp;
  						if ( !pprev )	// Deleting at head of list
  							*ppTempEntActive = pnext;
  						else
  							pprev->next = pnext;
  					}
  					else
  					{
  						pprev = pTemp;
  						
  						VectorCopy( pTemp->entity.origin, pTemp->entity.prevstate.origin );
  						
  						if ( pTemp->flags & FTENT_SPARKSHOWER )
  						{
  							// Adjust speed if it's time
  							// Scale is next think time
  							if ( client_time > pTemp->entity.baseline.scale )
  							{
  								// Show Sparks
  								gEngfuncs.pEfxAPI->R_SparkEffect( pTemp->entity.origin, 8, -200, 200 );
  								
  								// Reduce life
  								pTemp->entity.baseline.framerate -= 0.1;
  								
  								if ( pTemp->entity.baseline.framerate <= 0.0 )
  								{
  									pTemp->die = client_time;
  								}
  								else
  								{
  									// So it will die no matter what
  									pTemp->die = client_time + 0.5;
  									
  									// Next think
  									pTemp->entity.baseline.scale = client_time + 0.1;
  								}
  							}
  						}
  						else if ( pTemp->flags & FTENT_PLYRATTACHMENT )
  						{
  							cl_entity_t *pClient;
  							
  							pClient = gEngfuncs.GetEntityByIndex( pTemp->clientIndex );
  							
  							VectorAdd( pClient->origin, pTemp->tentOffset, pTemp->entity.origin );
  						}
  						else if ( pTemp->flags & FTENT_SINEWAVE )
  						{
  							pTemp->x += pTemp->entity.baseline.origin[0] * frametime;
  							pTemp->y += pTemp->entity.baseline.origin[1] * frametime;
  							
  							pTemp->entity.origin[0] = pTemp->x + sin( pTemp->entity.baseline.origin[2] + client_time * pTemp->entity.prevstate.frame ) * (10*pTemp->entity.curstate.framerate);
  							pTemp->entity.origin[1] = pTemp->y + sin( pTemp->entity.baseline.origin[2] + fastFreq + 0.7 ) * (8*pTemp->entity.curstate.framerate);
  							pTemp->entity.origin[2] += pTemp->entity.baseline.origin[2] * frametime;
  						}
  						else if ( pTemp->flags & FTENT_SPIRAL )
  						{
  							float s, c;
  							s = sin( pTemp->entity.baseline.origin[2] + fastFreq );
  							c = cos( pTemp->entity.baseline.origin[2] + fastFreq );
  #pragma warning(push)
  #pragma warning(disable: 311)							
  							pTemp->entity.origin[0] += pTemp->entity.baseline.origin[0] * frametime + 8 * sin( client_time * 20 + (int)pTemp );
  							pTemp->entity.origin[1] += pTemp->entity.baseline.origin[1] * frametime + 4 * sin( client_time * 30 + (int)pTemp );
  							pTemp->entity.origin[2] += pTemp->entity.baseline.origin[2] * frametime;
  #pragma warning(pop)
  						}
  						
  						else 
  						{
  							for ( i = 0; i < 3; i++ ) 
  								pTemp->entity.origin[i] += pTemp->entity.baseline.origin[i] * frametime;
  						}
  						
  						if ( pTemp->flags & FTENT_SPRANIMATE )
  						{
  							pTemp->entity.curstate.frame += frametime * pTemp->entity.curstate.framerate;
  							if ( pTemp->entity.curstate.frame >= pTemp->frameMax )
  							{
  								pTemp->entity.curstate.frame = pTemp->entity.curstate.frame - (int)(pTemp->entity.curstate.frame);
  								
  								if ( !(pTemp->flags & FTENT_SPRANIMATELOOP) )
  								{
  									// this animating sprite isn't set to loop, so destroy it.
  									pTemp->die = client_time;
  									pTemp = pnext;
  									continue;
  								}
  							}
  						}
  						else if ( pTemp->flags & FTENT_SPRCYCLE )
  						{
  							pTemp->entity.curstate.frame += frametime * 10;
  							if ( pTemp->entity.curstate.frame >= pTemp->frameMax )
  							{
  								pTemp->entity.curstate.frame = pTemp->entity.curstate.frame - (int)(pTemp->entity.curstate.frame);
  							}
  						}
  						// Experiment
  #if 0
  						if ( pTemp->flags & FTENT_SCALE )
  							pTemp->entity.curstate.framerate += 20.0 * (frametime / pTemp->entity.curstate.framerate);
  #endif
  						
  						if ( pTemp->flags & FTENT_ROTATE )
  						{
  							pTemp->entity.angles[0] += pTemp->entity.baseline.angles[0] * frametime;
  							pTemp->entity.angles[1] += pTemp->entity.baseline.angles[1] * frametime;
  							pTemp->entity.angles[2] += pTemp->entity.baseline.angles[2] * frametime;
  							
  							VectorCopy( pTemp->entity.angles, pTemp->entity.latched.prevangles );
  						}
  						
  						if ( pTemp->flags & (FTENT_COLLIDEALL | FTENT_COLLIDEWORLD) )
  						{
  							vec3_t	traceNormal;
  							float	traceFraction = 1;
  							
  							if ( pTemp->flags & FTENT_COLLIDEALL )
  							{
  								pmtrace_t pmtrace;
  								physent_t *pe;
  								
  								gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
  								
  								gEngfuncs.pEventAPI->EV_PlayerTrace( pTemp->entity.prevstate.origin, pTemp->entity.origin, PM_STUDIO_BOX, -1, &pmtrace );
  								
  								
  								if ( pmtrace.fraction != 1 )
  								{
  									pe = gEngfuncs.pEventAPI->EV_GetPhysent( pmtrace.ent );
  									
  									if ( !pmtrace.ent || ( pe->info != pTemp->clientIndex ) )
  									{
  										traceFraction = pmtrace.fraction;
  										VectorCopy( pmtrace.plane.normal, traceNormal );
  										
  										if ( pTemp->hitcallback )
  										{
  											(*pTemp->hitcallback)( pTemp, &pmtrace );
  										}
  									}
  								}
  							}
  							else if ( pTemp->flags & FTENT_COLLIDEWORLD )
  							{
  								pmtrace_t pmtrace;
  								
  								gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
  								
  								gEngfuncs.pEventAPI->EV_PlayerTrace( pTemp->entity.prevstate.origin, pTemp->entity.origin, PM_STUDIO_BOX | PM_WORLD_ONLY, -1, &pmtrace );					
  								
  								if ( pmtrace.fraction != 1 )
  								{
  									traceFraction = pmtrace.fraction;
  									VectorCopy( pmtrace.plane.normal, traceNormal );
  									
  									if ( pTemp->flags & FTENT_SPARKSHOWER )
  									{
  										// Chop spark speeds a bit more
  										//
  										VectorScale( pTemp->entity.baseline.origin, 0.6, pTemp->entity.baseline.origin );
  										
  										if ( Length( pTemp->entity.baseline.origin ) < 10 )
  										{
  											pTemp->entity.baseline.framerate = 0.0;								
  										}
  									}
  									
  									if ( pTemp->hitcallback )
  									{
  										(*pTemp->hitcallback)( pTemp, &pmtrace );
  									}
  								}
  							}
  							
  							if ( traceFraction != 1 )	// Decent collision now, and damping works
  							{
  								float  proj, damp;
  								
  								// Place at contact point
  								VectorMA( pTemp->entity.prevstate.origin, traceFraction*frametime, pTemp->entity.baseline.origin, pTemp->entity.origin );
  								// Damp velocity
  								damp = pTemp->bounceFactor;
  								if ( pTemp->flags & (FTENT_GRAVITY|FTENT_SLOWGRAVITY) )
  								{
  									damp *= 0.5;
  									if ( traceNormal[2] > 0.9 )		// Hit floor?
  									{
  										if ( pTemp->entity.baseline.origin[2] <= 0 && pTemp->entity.baseline.origin[2] >= gravity*3 )
  										{
  											damp = 0;		// Stop
  											pTemp->flags &= ~(FTENT_ROTATE|FTENT_GRAVITY|FTENT_SLOWGRAVITY|FTENT_COLLIDEWORLD|FTENT_SMOKETRAIL);
  											pTemp->entity.angles[0] = 0;
  											pTemp->entity.angles[2] = 0;
  										}
  									}
  								}
  								
  								if (pTemp->hitSound)
  								{
  									Callback_TempEntPlaySound(pTemp, damp);
  								}
  								
  								if (pTemp->flags & FTENT_COLLIDEKILL)
  								{
  									// die on impact
  									pTemp->flags &= ~FTENT_FADEOUT;	
  									pTemp->die = client_time;			
  								}
  								else
  								{
  									// Reflect velocity
  									if ( damp != 0 )
  									{
  										proj = DotProduct( pTemp->entity.baseline.origin, traceNormal );
  										VectorMA( pTemp->entity.baseline.origin, -proj*2, traceNormal, pTemp->entity.baseline.origin );
  										// Reflect rotation (fake)
  										
  										pTemp->entity.angles[1] = -pTemp->entity.angles[1];
  									}
  									
  									if ( damp != 1 )
  									{
  										
  										VectorScale( pTemp->entity.baseline.origin, damp, pTemp->entity.baseline.origin );
  										VectorScale( pTemp->entity.angles, 0.9, pTemp->entity.angles );
  									}
  								}
  							}
  							}
  							
  							
  							if ( (pTemp->flags & FTENT_FLICKER) && gTempEntFrame == pTemp->entity.curstate.effects )
  							{
  								dlight_t *dl = gEngfuncs.pEfxAPI->CL_AllocDlight (0);
  								VectorCopy (pTemp->entity.origin, dl->origin);
  								dl->radius = 60;
  								dl->color.r = 255;
  								dl->color.g = 120;
  								dl->color.b = 0;
  								dl->die = client_time + 0.01;
  							}
  							
  							if ( pTemp->flags & FTENT_SMOKETRAIL )
  							{
  								gEngfuncs.pEfxAPI->R_RocketTrail (pTemp->entity.prevstate.origin, pTemp->entity.origin, 1);
  							}
  							
  							if ( pTemp->flags & FTENT_GRAVITY )
  								pTemp->entity.baseline.origin[2] += gravity;
  							else if ( pTemp->flags & FTENT_SLOWGRAVITY )
  								pTemp->entity.baseline.origin[2] += gravitySlow;
  							
  							if ( pTemp->flags & FTENT_CLIENTCUSTOM )
  							{
  								if ( pTemp->callback )
  								{
  									( *pTemp->callback )( pTemp, frametime, client_time );
  								}
  							}
  							
  							// Cull to PVS (not frustum cull, just PVS)
  							if ( !(pTemp->flags & FTENT_NOMODEL ) )
  							{
  								if ( !Callback_AddVisibleEntity( &pTemp->entity ) )
  								{
  									if ( !(pTemp->flags & FTENT_PERSIST) ) 
  									{
  										pTemp->die = client_time;			// If we can't draw it this frame, just dump it.
  										pTemp->flags &= ~FTENT_FADEOUT;	// Don't fade out, just die
  									}
  								}
  							}
  					}
  					pTemp = pnext;
  				}
  			}
  
  			// Restore state info
  			gEngfuncs.pEventAPI->EV_PopPMStates();
  		}
  	}
  
  //finish:
  //	// Restore state info
  //	gEngfuncs.pEventAPI->EV_PopPMStates();
  }
  
  /*
  =================
  HUD_GetUserEntity
  
  If you specify negative numbers for beam start and end point entities, then
    the engine will call back into this function requesting a pointer to a cl_entity_t 
    object that describes the entity to attach the beam onto.
  
  Indices must start at 1, not zero.
  =================
  */
  cl_entity_t CL_DLLEXPORT *HUD_GetUserEntity( int index )
  {
  //	RecClGetUserEntity(index);
  
  #if defined( BEAM_TEST )
  	// None by default, you would return a valic pointer if you create a client side
  	//  beam and attach it to a client side entity.
  	if ( index > 0 && index <= 1 )
  	{
  		return &beams[ index ];
  	}
  	else
  	{
  		return NULL;
  	}
  #else
  	return NULL;
  #endif
  }
  