Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

958 lines
25 KiB

//========= Copyright (c) 1996-2002, Valve LLC, All rights reserved. ============
9 years ago
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
// Client side entity management functions
#include "hud.h"
#include "cl_util.h"
#include "const.h"
#include "entity_types.h"
#include "studio_event.h" // def. of mstudioevent_t
#include "r_efx.h"
#include "event_api.h"
#include "pm_defs.h"
#include "pmtrace.h"
#include "pm_shared.h"
void Game_AddObjects( void );
extern vec3_t v_origin;
int g_iAlive = 1;
9 years ago
extern "C"
9 years ago
{
int DLLEXPORT HUD_AddEntity( int type, struct cl_entity_s *ent, const char *modelname );
void DLLEXPORT HUD_CreateEntities( void );
void DLLEXPORT HUD_StudioEvent( const struct mstudioevent_s *event, const struct cl_entity_s *entity );
void DLLEXPORT HUD_TxferLocalOverrides( struct entity_state_s *state, const struct clientdata_s *client );
void DLLEXPORT HUD_ProcessPlayerState( struct entity_state_s *dst, const struct entity_state_s *src );
void 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 );
void DLLEXPORT HUD_TempEntUpdate( double frametime, double client_time, double cl_gravity, struct tempent_s **ppTempEntFree, struct tempent_s **ppTempEntActive, int ( *Callback_AddVisibleEntity )( struct cl_entity_s *pEntity ), void ( *Callback_TempEntPlaySound )( struct tempent_s *pTemp, float damp ) );
struct cl_entity_s DLLEXPORT *HUD_GetUserEntity( int index );
}
/*
========================
HUD_AddEntity
Return 0 to filter entity from visible list for rendering
========================
*/
int DLLEXPORT HUD_AddEntity( int type, struct cl_entity_s *ent, const char *modelname )
{
9 years ago
switch( type )
9 years ago
{
case ET_NORMAL:
case ET_PLAYER:
case ET_BEAM:
case ET_TEMPENTITY:
case ET_FRAGMENTED:
default:
break;
}
// each frame every entity passes this function, so the overview hooks it to filter the overview entities
// in spectator mode:
// each frame every entity passes this function, so the overview hooks
// it to filter the overview entities
9 years ago
if( g_iUser1 )
9 years ago
{
gHUD.m_Spectator.AddOverviewEntity( type, ent, modelname );
9 years ago
if( ( g_iUser1 == OBS_IN_EYE || gHUD.m_Spectator.m_pip->value == INSET_IN_EYE ) &&
9 years ago
ent->index == g_iUser2 )
return 0; // don't draw the player we are following in eye
}
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 DLLEXPORT HUD_TxferLocalOverrides( struct entity_state_s *state, const struct clientdata_s *client )
{
VectorCopy( client->origin, state->origin );
// Spectator
state->iuser1 = client->iuser1;
state->iuser2 = client->iuser2;
// Duck prevention
state->iuser3 = client->iuser3;
// Fire prevention
state->iuser4 = client->iuser4;
}
/*
=========================
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 DLLEXPORT HUD_ProcessPlayerState( struct entity_state_s *dst, const struct entity_state_s *src )
{
// Copy in network data
VectorCopy( src->origin, dst->origin );
VectorCopy( src->angles, dst->angles );
VectorCopy( src->velocity, dst->velocity );
9 years ago
dst->frame = src->frame;
9 years ago
dst->modelindex = src->modelindex;
9 years ago
dst->skin = src->skin;
9 years ago
dst->effects = src->effects;
dst->weaponmodel = src->weaponmodel;
dst->movetype = src->movetype;
dst->sequence = src->sequence;
dst->animtime = src->animtime;
9 years ago
dst->solid = src->solid;
9 years ago
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;
9 years ago
dst->body = src->body;
9 years ago
9 years ago
memcpy( &dst->controller[0], &src->controller[0], 4 * sizeof(byte) );
memcpy( &dst->blending[0], &src->blending[0], 2 * sizeof(byte) );
9 years ago
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;
9 years ago
dst->team = src->team;
9 years ago
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
9 years ago
if( dst->number == player->index )
9 years ago
{
g_iPlayerClass = dst->playerclass;
g_iTeamNumber = dst->team;
g_iUser1 = src->iuser1;
g_iUser2 = src->iuser2;
g_iUser3 = src->iuser3;
}
}
/*
=========================
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.
=========================
*/
9 years ago
void 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 )
9 years ago
{
ps->oldbuttons = pps->oldbuttons;
ps->flFallVelocity = pps->flFallVelocity;
ps->iStepLeft = pps->iStepLeft;
ps->playerclass = pps->playerclass;
pcd->viewmodel = ppcd->viewmodel;
9 years ago
pcd->m_iId = ppcd->m_iId;
9 years ago
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;
9 years ago
pcd->fov = ppcd->fov;
9 years ago
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;
// Duck prevention
pcd->iuser3 = ppcd->iuser3;
9 years ago
if( gEngfuncs.IsSpectateOnly() )
9 years ago
{
// 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
}
// Fire prevention
9 years ago
pcd->iuser4 = ppcd->iuser4;
9 years ago
pcd->fuser2 = ppcd->fuser2;
pcd->fuser3 = ppcd->fuser3;
VectorCopy( ppcd->vuser1, pcd->vuser1 );
VectorCopy( ppcd->vuser2, pcd->vuser2 );
VectorCopy( ppcd->vuser3, pcd->vuser3 );
VectorCopy( ppcd->vuser4, pcd->vuser4 );
9 years ago
memcpy( wd, pwd, 32 * sizeof(weapon_data_t) );
9 years ago
}
/*
//#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();
9 years ago
if( !player )
9 years ago
return;
mod = gEngfuncs.CL_LoadModel( "models/sentry3.mdl", &modelindex );
9 years ago
for( i = 0; i < 3; i++ )
9 years ago
{
9 years ago
for( j = 0; j < 3; j++ )
9 years ago
{
// Don't draw over ourself...
9 years ago
if( ( i == 1 ) && ( j == 1 ) )
9 years ago
continue;
9 years ago
mymodel[i * 3 + j] = *player;
9 years ago
9 years ago
mymodel[i * 3 + j].player = 0;
9 years ago
9 years ago
mymodel[i * 3 + j].model = mod;
mymodel[i * 3 + j].curstate.modelindex = modelindex;
9 years ago
9 years ago
// 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 );
9 years ago
9 years ago
gEngfuncs.CL_CreateVisibleEntity( ET_NORMAL, &mymodel[i * 3 + j] );
9 years ago
}
}
}
#endif
//#define TRACE_TEST
#if defined( TRACE_TEST )
extern int hitent;
cl_entity_t hit;
void TraceModel( void )
{
cl_entity_t *ent;
9 years ago
if( hitent <= 0 )
9 years ago
return;
// Load it up with some bogus data
ent = gEngfuncs.GetEntityByIndex( hitent );
9 years ago
if( !ent )
9 years ago
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;
9 years ago
for( i = 0; i < 3; i++ )
9 years ago
{
9 years ago
particle->org[i] += particle->vel[i] * frametime;
9 years ago
}
}
cvar_t *color = NULL;
void Particles( void )
{
static float lasttime;
float curtime;
9 years ago
9 years ago
curtime = gEngfuncs.GetClientTime();
if( ( curtime - lasttime ) < 2.0f )
9 years ago
return;
9 years ago
if( !color )
9 years ago
{
color = gEngfuncs.pfnRegisterVariable( "color", "255 0 0", 0 );
9 years ago
}
lasttime = curtime;
// Create a few particles
particle_t *p;
int i, j;
9 years ago
for( i = 0; i < 1000; i++ )
9 years ago
{
int r, g, b;
p = gEngfuncs.pEfxAPI->R_AllocParticle( ParticleCallback );
9 years ago
if( !p )
9 years ago
break;
9 years ago
for( j = 0; j < 3; j++ )
9 years ago
{
p->org[j] = v_origin[j] + gEngfuncs.pfnRandomFloat( -32.0f, 32.0f );
p->vel[j] = gEngfuncs.pfnRandomFloat( -100.0f, 100.0f );
9 years ago
}
9 years ago
if( color )
9 years ago
{
sscanf( color->string, "%i %i %i", &r, &g, &b );
}
else
{
r = 192;
g = 0;
b = 0;
}
9 years ago
p->color = gEngfuncs.pEfxAPI->R_LookupColor( r, g, b );
9 years ago
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.0f;
9 years ago
}
}
*/
/*
9 years ago
void TempEntCallback( struct tempent_s *ent, float frametime, float currenttime )
9 years ago
{
int i;
9 years ago
for( i = 0; i < 3; i++ )
9 years ago
{
9 years ago
ent->entity.curstate.origin[i] += ent->entity.baseline.origin[i] * frametime;
9 years ago
}
}
void TempEnts( void )
{
static float lasttime;
float curtime;
9 years ago
9 years ago
curtime = gEngfuncs.GetClientTime();
if( ( curtime - lasttime ) < 10.0f )
9 years ago
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 );
9 years ago
for( i = 0; i < 100; i++ )
9 years ago
{
9 years ago
for( j = 0; j < 3; j++ )
9 years ago
{
9 years ago
origin[j] = v_origin[j];
if( j != 2 )
9 years ago
{
9 years ago
origin[j] += 75;
9 years ago
}
}
p = gEngfuncs.pEfxAPI->CL_TentEntAllocCustom( (float *)&origin, mod, 0, TempEntCallback );
9 years ago
if( !p )
9 years ago
break;
9 years ago
for( j = 0; j < 3; j++ )
9 years ago
{
9 years ago
p->entity.curstate.origin[j] = origin[j];
9 years ago
// Store velocity in baseline origin
p->entity.baseline.origin[j] = gEngfuncs.pfnRandomFloat( -100.0f, 100.0f );
9 years ago
}
// p->die is set to current time so all you have to do is add an additional time to it
p->die += 10.0f;
9 years ago
}
}
*/
#if defined( BEAM_TEST )
9 years ago
// Note can't index beam[0] in Beam callback, so don't use that index
9 years ago
// Room for 1 beam ( 0 can't be used )
9 years ago
static cl_entity_t beams[2];
9 years ago
void BeamEndModel( void )
{
cl_entity_t *player, *model;
int modelindex;
struct model_s *mod;
// Load it up with some bogus data
player = gEngfuncs.GetLocalPlayer();
9 years ago
if( !player )
9 years ago
return;
mod = gEngfuncs.CL_LoadModel( "models/sentry3.mdl", &modelindex );
9 years ago
if( !mod )
9 years ago
return;
// Slot 1
9 years ago
model = &beams[1];
9 years ago
*model = *player;
model->player = 0;
model->model = mod;
model->curstate.modelindex = modelindex;
9 years ago
9 years ago
// 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();
9 years ago
9 years ago
curtime = gEngfuncs.GetClientTime();
9 years ago
float end[3];
9 years ago
9 years ago
if( ( curtime - lasttime ) < 10.0 )
9 years ago
return;
mod = gEngfuncs.CL_LoadModel( "sprites/laserbeam.spr", &index );
9 years ago
if( !mod )
9 years ago
return;
lasttime = curtime;
9 years ago
end[0] = v_origin.x + 100;
end[1] = v_origin.y + 100;
end[2] = v_origin.z;
9 years ago
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 DLLEXPORT HUD_CreateEntities( void )
{
// 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();
}
/*
=========================
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 DLLEXPORT HUD_StudioEvent( const struct mstudioevent_s *event, const struct cl_entity_s *entity )
{
switch( event->event )
{
case 5001:
9 years ago
gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[0], atoi( event->options ) );
9 years ago
break;
case 5011:
9 years ago
gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[1], atoi( event->options ) );
9 years ago
break;
case 5021:
9 years ago
gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[2], atoi( event->options ) );
9 years ago
break;
case 5031:
9 years ago
gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[3], atoi( event->options ) );
9 years ago
break;
case 5002:
9 years ago
gEngfuncs.pEfxAPI->R_SparkEffect( (float *)&entity->attachment[0], atoi( event->options ), -100, 100 );
9 years ago
break;
// Client side sound
case 5004:
gEngfuncs.pfnPlaySoundByNameAtLocation( (char *)event->options, 1.0, (float *)&entity->attachment[0] );
break;
default:
break;
}
}
/*
=================
CL_UpdateTEnts
Simulation and cleanup of temporary entities
=================
*/
void 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 ) )
{
static int gTempEntFrame = 0;
int i;
TEMPENTITY *pTemp, *pnext, *pprev;
float /*freq,*/ gravity, gravitySlow, life, fastFreq;
9 years ago
// Nothing to simulate
if( !*ppTempEntActive )
9 years ago
return;
// 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 );
9 years ago
// !!!BUGBUG -- This needs to be time based
9 years ago
gTempEntFrame = ( gTempEntFrame + 1 ) & 31;
9 years ago
pTemp = *ppTempEntActive;
// !!! Don't simulate while paused.... This is sort of a hack, revisit.
9 years ago
if( frametime <= 0 )
9 years ago
{
9 years ago
while( pTemp )
9 years ago
{
9 years ago
if( !( pTemp->flags & FTENT_NOMODEL ) )
9 years ago
{
Callback_AddVisibleEntity( &pTemp->entity );
}
pTemp = pTemp->next;
}
goto finish;
}
pprev = NULL;
//freq = client_time * 0.01;
9 years ago
fastFreq = client_time * 5.5;
gravity = -frametime * cl_gravity;
gravitySlow = gravity * 0.5f;
9 years ago
9 years ago
while( pTemp )
9 years ago
{
int active;
active = 1;
life = pTemp->die - (float)client_time;
9 years ago
pnext = pTemp->next;
9 years ago
if( life < 0 )
9 years ago
{
9 years ago
if( pTemp->flags & FTENT_FADEOUT )
9 years ago
{
9 years ago
if( pTemp->entity.curstate.rendermode == kRenderNormal)
9 years ago
pTemp->entity.curstate.rendermode = kRenderTransTexture;
pTemp->entity.curstate.renderamt = pTemp->entity.baseline.renderamt * ( 1 + life * pTemp->fadeSpeed );
9 years ago
if( pTemp->entity.curstate.renderamt <= 0 )
9 years ago
active = 0;
}
else
active = 0;
}
9 years ago
if( !active ) // Kill it
9 years ago
{
pTemp->next = *ppTempEntFree;
*ppTempEntFree = pTemp;
9 years ago
if( !pprev ) // Deleting at head of list
9 years ago
*ppTempEntActive = pnext;
else
pprev->next = pnext;
}
else
{
pprev = pTemp;
9 years ago
9 years ago
VectorCopy( pTemp->entity.origin, pTemp->entity.prevstate.origin );
9 years ago
if( pTemp->flags & FTENT_SPARKSHOWER )
9 years ago
{
// Adjust speed if it's time
// Scale is next think time
9 years ago
if( client_time > pTemp->entity.baseline.scale )
9 years ago
{
// Show Sparks
gEngfuncs.pEfxAPI->R_SparkEffect( pTemp->entity.origin, 8, -200, 200 );
// Reduce life
pTemp->entity.baseline.framerate -= 0.1f;
9 years ago
if( pTemp->entity.baseline.framerate <= 0.0f )
9 years ago
{
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;
}
}
}
9 years ago
else if( pTemp->flags & FTENT_PLYRATTACHMENT )
9 years ago
{
cl_entity_t *pClient;
pClient = gEngfuncs.GetEntityByIndex( pTemp->clientIndex );
VectorAdd( pClient->origin, pTemp->tentOffset, pTemp->entity.origin );
}
9 years ago
else if( pTemp->flags & FTENT_SINEWAVE )
9 years ago
{
pTemp->x += pTemp->entity.baseline.origin[0] * (float)frametime;
pTemp->y += pTemp->entity.baseline.origin[1] * (float)frametime;
9 years ago
9 years ago
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.7f ) * ( 8 * pTemp->entity.curstate.framerate );
9 years ago
pTemp->entity.origin[2] += pTemp->entity.baseline.origin[2] * frametime;
}
9 years ago
else if( pTemp->flags & FTENT_SPIRAL )
9 years ago
{
/*float s, c;
9 years ago
s = sin( pTemp->entity.baseline.origin[2] + fastFreq );
c = cos( pTemp->entity.baseline.origin[2] + fastFreq );*/
9 years ago
pTemp->entity.origin[0] += pTemp->entity.baseline.origin[0] * (float)frametime + 8 * sin( client_time * 20 + (size_t)pTemp );
pTemp->entity.origin[1] += pTemp->entity.baseline.origin[1] * (float)frametime + 4 * sin( client_time * 30 + (size_t)pTemp );
pTemp->entity.origin[2] += pTemp->entity.baseline.origin[2] * (float)frametime;
9 years ago
}
else
{
9 years ago
for( i = 0; i < 3; i++ )
pTemp->entity.origin[i] += pTemp->entity.baseline.origin[i] * (float)frametime;
9 years ago
}
9 years ago
if( pTemp->flags & FTENT_SPRANIMATE )
9 years ago
{
pTemp->entity.curstate.frame += (float)frametime * pTemp->entity.curstate.framerate;
9 years ago
if( pTemp->entity.curstate.frame >= pTemp->frameMax )
9 years ago
{
9 years ago
pTemp->entity.curstate.frame = pTemp->entity.curstate.frame - (int)( pTemp->entity.curstate.frame );
9 years ago
9 years ago
if( !( pTemp->flags & FTENT_SPRANIMATELOOP ) )
9 years ago
{
// this animating sprite isn't set to loop, so destroy it.
pTemp->die = client_time;
pTemp = pnext;
continue;
}
}
}
9 years ago
else if( pTemp->flags & FTENT_SPRCYCLE )
9 years ago
{
pTemp->entity.curstate.frame += frametime * 10;
9 years ago
if( pTemp->entity.curstate.frame >= pTemp->frameMax )
9 years ago
{
9 years ago
pTemp->entity.curstate.frame = pTemp->entity.curstate.frame - (int)( pTemp->entity.curstate.frame );
9 years ago
}
}
// Experiment
#if 0
9 years ago
if( pTemp->flags & FTENT_SCALE )
pTemp->entity.curstate.framerate += 20.0 * ( frametime / pTemp->entity.curstate.framerate );
9 years ago
#endif
9 years ago
if( pTemp->flags & FTENT_ROTATE )
9 years ago
{
pTemp->entity.angles[0] += pTemp->entity.baseline.angles[0] * (float)frametime;
pTemp->entity.angles[1] += pTemp->entity.baseline.angles[1] * (float)frametime;
pTemp->entity.angles[2] += pTemp->entity.baseline.angles[2] * (float)frametime;
9 years ago
VectorCopy( pTemp->entity.angles, pTemp->entity.latched.prevangles );
}
9 years ago
if( pTemp->flags & ( FTENT_COLLIDEALL | FTENT_COLLIDEWORLD ) )
9 years ago
{
vec3_t traceNormal( 0.0f, 0.0f, 0.0f );
9 years ago
float traceFraction = 1;
9 years ago
if( pTemp->flags & FTENT_COLLIDEALL )
9 years ago
{
pmtrace_t pmtrace;
physent_t *pe;
9 years ago
9 years ago
gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
gEngfuncs.pEventAPI->EV_PlayerTrace( pTemp->entity.prevstate.origin, pTemp->entity.origin, PM_STUDIO_BOX, -1, &pmtrace );
9 years ago
if( pmtrace.fraction != 1 )
9 years ago
{
pe = gEngfuncs.pEventAPI->EV_GetPhysent( pmtrace.ent );
9 years ago
if( !pmtrace.ent || ( pe->info != pTemp->clientIndex ) )
9 years ago
{
traceFraction = pmtrace.fraction;
VectorCopy( pmtrace.plane.normal, traceNormal );
9 years ago
if( pTemp->hitcallback )
9 years ago
{
(*pTemp->hitcallback)( pTemp, &pmtrace );
}
}
}
}
9 years ago
else if( pTemp->flags & FTENT_COLLIDEWORLD )
9 years ago
{
pmtrace_t pmtrace;
9 years ago
9 years ago
gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
9 years ago
gEngfuncs.pEventAPI->EV_PlayerTrace( pTemp->entity.prevstate.origin, pTemp->entity.origin, PM_STUDIO_BOX | PM_WORLD_ONLY, -1, &pmtrace );
9 years ago
9 years ago
if( pmtrace.fraction != 1 )
9 years ago
{
traceFraction = pmtrace.fraction;
VectorCopy( pmtrace.plane.normal, traceNormal );
9 years ago
if( pTemp->flags & FTENT_SPARKSHOWER )
9 years ago
{
// Chop spark speeds a bit more
//
VectorScale( pTemp->entity.baseline.origin, 0.6f, pTemp->entity.baseline.origin );
9 years ago
9 years ago
if( Length( pTemp->entity.baseline.origin ) < 10 )
9 years ago
{
pTemp->entity.baseline.framerate = 0.0;
}
}
9 years ago
if( pTemp->hitcallback )
9 years ago
{
(*pTemp->hitcallback)( pTemp, &pmtrace );
}
}
}
9 years ago
if( traceFraction != 1 ) // Decent collision now, and damping works
9 years ago
{
float proj, damp;
// Place at contact point
VectorMA( pTemp->entity.prevstate.origin, traceFraction * (float)frametime, pTemp->entity.baseline.origin, pTemp->entity.origin );
9 years ago
// Damp velocity
damp = pTemp->bounceFactor;
9 years ago
if( pTemp->flags & ( FTENT_GRAVITY | FTENT_SLOWGRAVITY ) )
9 years ago
{
damp *= 0.5f;
if( traceNormal[2] > 0.9f ) // Hit floor?
9 years ago
{
9 years ago
if( pTemp->entity.baseline.origin[2] <= 0 && pTemp->entity.baseline.origin[2] >= gravity*3 )
9 years ago
{
damp = 0; // Stop
9 years ago
pTemp->flags &= ~( FTENT_ROTATE | FTENT_GRAVITY | FTENT_SLOWGRAVITY | FTENT_COLLIDEWORLD | FTENT_SMOKETRAIL);
9 years ago
pTemp->entity.angles[0] = 0;
pTemp->entity.angles[2] = 0;
}
}
}
9 years ago
if( pTemp->hitSound )
9 years ago
{
9 years ago
Callback_TempEntPlaySound( pTemp, damp );
9 years ago
}
9 years ago
if( pTemp->flags & FTENT_COLLIDEKILL )
9 years ago
{
// die on impact
pTemp->flags &= ~FTENT_FADEOUT;
pTemp->die = client_time;
}
else
{
// Reflect velocity
9 years ago
if( damp != 0 )
9 years ago
{
proj = DotProduct( pTemp->entity.baseline.origin, traceNormal );
9 years ago
VectorMA( pTemp->entity.baseline.origin, -proj * 2, traceNormal, pTemp->entity.baseline.origin );
9 years ago
// Reflect rotation (fake)
pTemp->entity.angles[1] = -pTemp->entity.angles[1];
}
9 years ago
if( damp != 1 )
9 years ago
{
VectorScale( pTemp->entity.baseline.origin, damp, pTemp->entity.baseline.origin );
VectorScale( pTemp->entity.angles, 0.9, pTemp->entity.angles );
}
}
}
}
9 years ago
if( ( pTemp->flags & FTENT_FLICKER ) && gTempEntFrame == pTemp->entity.curstate.effects )
9 years ago
{
9 years ago
dlight_t *dl = gEngfuncs.pEfxAPI->CL_AllocDlight(0);
VectorCopy( pTemp->entity.origin, dl->origin );
9 years ago
dl->radius = 60;
dl->color.r = 255;
dl->color.g = 120;
dl->color.b = 0;
dl->die = client_time + 0.01;
}
9 years ago
if( pTemp->flags & FTENT_SMOKETRAIL )
9 years ago
{
9 years ago
gEngfuncs.pEfxAPI->R_RocketTrail( pTemp->entity.prevstate.origin, pTemp->entity.origin, 1 );
9 years ago
}
9 years ago
if( pTemp->flags & FTENT_GRAVITY )
9 years ago
pTemp->entity.baseline.origin[2] += gravity;
9 years ago
else if( pTemp->flags & FTENT_SLOWGRAVITY )
9 years ago
pTemp->entity.baseline.origin[2] += gravitySlow;
9 years ago
if( pTemp->flags & FTENT_CLIENTCUSTOM )
9 years ago
{
9 years ago
if( pTemp->callback )
9 years ago
{
9 years ago
(*pTemp->callback)( pTemp, frametime, client_time );
9 years ago
}
}
// Cull to PVS (not frustum cull, just PVS)
9 years ago
if( !( pTemp->flags & FTENT_NOMODEL ) )
9 years ago
{
9 years ago
if( !Callback_AddVisibleEntity( &pTemp->entity ) )
9 years ago
{
9 years ago
if( !( pTemp->flags & FTENT_PERSIST ) )
9 years ago
{
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;
}
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 DLLEXPORT *HUD_GetUserEntity( int 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.
9 years ago
if( index > 0 && index <= 1 )
9 years ago
{
9 years ago
return &beams[index];
9 years ago
}
else
{
return NULL;
}
#else
return NULL;
#endif
}