781 lines
19 KiB
C
781 lines
19 KiB
C
//=======================================================================
|
|
// Copyright XashXT Group 2009 ©
|
|
// cl_tent.c - temp entity effects management
|
|
//=======================================================================
|
|
|
|
#include "common.h"
|
|
#include "client.h"
|
|
#include "effects_api.h"
|
|
#include "triangle_api.h"
|
|
#include "beam_def.h"
|
|
#include "const.h"
|
|
|
|
/*
|
|
==============================================================
|
|
|
|
TEMPENTS MANAGEMENT
|
|
|
|
==============================================================
|
|
*/
|
|
|
|
#define SHARD_VOLUME 12.0 // on shard ever n^3 units
|
|
#define MAX_TEMPENTS 1024
|
|
TEMPENTITY *cl_active_tents, *cl_free_tents;
|
|
static TEMPENTITY cl_tents_list[MAX_TEMPENTS];
|
|
|
|
/*
|
|
=================
|
|
CL_FreeTempEntity
|
|
=================
|
|
*/
|
|
static void CL_FreeTempEnt( TEMPENTITY *pTemp, TEMPENTITY *pPrev )
|
|
{
|
|
// remove from the active list.
|
|
if( pPrev )
|
|
{
|
|
pPrev->next = pTemp->next;
|
|
}
|
|
else
|
|
{
|
|
cl_active_tents = pTemp->next;
|
|
}
|
|
|
|
// cleanup its data.
|
|
if( pTemp->pvEngineData )
|
|
Mem_Free( pTemp->pvEngineData );
|
|
Mem_Set( pTemp, 0, sizeof( TEMPENTITY ));
|
|
|
|
// add to the free list.
|
|
pTemp->next = cl_free_tents;
|
|
cl_free_tents = pTemp;
|
|
}
|
|
|
|
// free the first low priority tempent it finds.
|
|
static bool CL_FreeLowPriorityTempEnt( void )
|
|
{
|
|
TEMPENTITY *pActive = cl_active_tents;
|
|
TEMPENTITY *pPrev = NULL;
|
|
|
|
while( pActive )
|
|
{
|
|
if( pActive->priority == TENTPRIORITY_LOW )
|
|
{
|
|
CL_FreeTempEnt( pActive, pPrev );
|
|
return true;
|
|
}
|
|
pPrev = pActive;
|
|
pActive = pActive->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void CL_PrepareTempEnt( TEMPENTITY *pTemp, int modelIndex )
|
|
{
|
|
int frameCount;
|
|
|
|
// Use these to set per-frame and termination conditions / actions
|
|
pTemp->flags = FTENT_NONE;
|
|
pTemp->die = clgame.globals->time + 0.75f;
|
|
|
|
if( modelIndex != 0 ) // allocate prevframe only for tents with valid model
|
|
pTemp->pvEngineData = Mem_Alloc( cls.mempool, sizeof( studioframe_t ));
|
|
else pTemp->flags |= FTENT_NOMODEL;
|
|
|
|
Mod_GetFrames( modelIndex, &frameCount );
|
|
|
|
pTemp->modelindex = modelIndex;
|
|
pTemp->renderMode = kRenderNormal;
|
|
pTemp->renderFX = kRenderFxNone;
|
|
VectorSet( pTemp->renderColor, 255, 255, 255 );
|
|
pTemp->m_flFrameMax = max( 0, frameCount - 1 );
|
|
pTemp->renderAmt = 255;
|
|
pTemp->body = 0;
|
|
pTemp->skin = 0;
|
|
pTemp->fadeSpeed = 0.5f;
|
|
pTemp->hitSound = 0;
|
|
pTemp->clientIndex = NULLENT_INDEX;
|
|
pTemp->bounceFactor = 1;
|
|
pTemp->m_flSpriteScale = 1.0f;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_AllocTempEntity
|
|
=================
|
|
*/
|
|
TEMPENTITY *CL_TempEntAlloc( float *org, int modelIndex )
|
|
{
|
|
TEMPENTITY *pTemp;
|
|
|
|
if( !cl_free_tents )
|
|
{
|
|
MsgDev( D_WARN, "Overflow %d temporary ents!\n", MAX_TEMPENTS );
|
|
return NULL;
|
|
}
|
|
|
|
pTemp = cl_free_tents;
|
|
cl_free_tents = pTemp->next;
|
|
|
|
Com_Assert( pTemp->pvEngineData );
|
|
CL_PrepareTempEnt( pTemp, modelIndex );
|
|
|
|
pTemp->priority = TENTPRIORITY_LOW;
|
|
if( org ) VectorCopy( org, pTemp->origin );
|
|
|
|
pTemp->next = cl_active_tents;
|
|
cl_active_tents = pTemp;
|
|
|
|
return pTemp;
|
|
}
|
|
|
|
TEMPENTITY *CL_TempEntAllocNoModel( float *org )
|
|
{
|
|
return CL_TempEntAlloc( org, 0 );
|
|
}
|
|
|
|
TEMPENTITY *CL_TempEntAllocHigh( float *org, int modelIndex )
|
|
{
|
|
TEMPENTITY *pTemp;
|
|
|
|
if( !cl_free_tents )
|
|
{
|
|
// no temporary ents free, so find the first active low-priority temp ent
|
|
// and overwrite it.
|
|
CL_FreeLowPriorityTempEnt();
|
|
}
|
|
|
|
if( !cl_free_tents )
|
|
{
|
|
// didn't find anything? The tent list is either full of high-priority tents
|
|
// or all tents in the list are still due to live for > 10 seconds.
|
|
MsgDev( D_ERROR, "Couldn't alloc a high priority TENT!\n" );
|
|
return NULL;
|
|
}
|
|
|
|
// Move out of the free list and into the active list.
|
|
pTemp = cl_free_tents;
|
|
cl_free_tents = pTemp->next;
|
|
|
|
Com_Assert( pTemp->pvEngineData );
|
|
CL_PrepareTempEnt( pTemp, modelIndex );
|
|
|
|
pTemp->priority = TENTPRIORITY_HIGH;
|
|
if( org ) VectorCopy( org, pTemp->origin );
|
|
|
|
pTemp->next = cl_active_tents;
|
|
cl_active_tents = pTemp;
|
|
|
|
return pTemp;
|
|
}
|
|
|
|
TEMPENTITY *CL_TentEntAllocCustom( float *org, int modelIndex, int high, ENTCALLBACK pfnCallback )
|
|
{
|
|
TEMPENTITY *pTemp;
|
|
|
|
if( high ) pTemp = CL_TempEntAllocHigh( org, modelIndex );
|
|
else CL_TempEntAlloc( org, modelIndex );
|
|
|
|
if( pTemp && pfnCallback )
|
|
{
|
|
pTemp->callback = pfnCallback;
|
|
pTemp->flags |= FTENT_CLIENTCUSTOM;
|
|
}
|
|
return pTemp;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_ClearTempEnts
|
|
===============
|
|
*/
|
|
void CL_ClearTempEnts( void )
|
|
{
|
|
int i;
|
|
|
|
cl_active_tents = NULL;
|
|
cl_free_tents = cl_tents_list;
|
|
|
|
for( i = 0; i < MAX_TEMPENTS; i++ )
|
|
{
|
|
cl_tents_list[i].next = &cl_tents_list[i+1];
|
|
if( cl_tents_list[i].pvEngineData )
|
|
Mem_Free( cl_tents_list[i].pvEngineData );
|
|
cl_tents_list[i].pvEngineData = NULL;
|
|
}
|
|
cl_tents_list[MAX_TEMPENTS-1].next = NULL;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_IsActiveTempEnt
|
|
===============
|
|
*/
|
|
bool CL_IsActiveTempEnt( TEMPENTITY *pTemp )
|
|
{
|
|
bool active = true;
|
|
float life;
|
|
|
|
if( !pTemp ) return false;
|
|
life = pTemp->die - clgame.globals->time;
|
|
|
|
if( life < 0.0f )
|
|
{
|
|
if( pTemp->flags & FTENT_FADEOUT )
|
|
{
|
|
int alpha;
|
|
|
|
if( pTemp->renderMode == kRenderNormal )
|
|
pTemp->renderMode = kRenderTransTexture;
|
|
|
|
alpha = pTemp->startAlpha * ( 1.0f + life * pTemp->fadeSpeed );
|
|
|
|
if( alpha <= 0 )
|
|
{
|
|
active = false;
|
|
alpha = 0;
|
|
}
|
|
pTemp->renderAmt = alpha;
|
|
}
|
|
else
|
|
{
|
|
active = false;
|
|
}
|
|
}
|
|
|
|
// never die tempents only die when their die is cleared
|
|
if( pTemp->flags & FTENT_NEVERDIE )
|
|
{
|
|
active = (pTemp->die != 0.0f);
|
|
}
|
|
return active;
|
|
}
|
|
|
|
bool CL_UpdateTempEnt( TEMPENTITY *pTemp, int framenumber )
|
|
{
|
|
if( !pTemp ) return false;
|
|
return clgame.dllFuncs.pfnUpdateEntity( pTemp, framenumber );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_AddTempEnts
|
|
===============
|
|
*/
|
|
void CL_AddTempEnts( void )
|
|
{
|
|
static int gTempEntFrame = 0;
|
|
TEMPENTITY *current, *pnext, *pprev;
|
|
|
|
// nothing to simulate ?
|
|
if( !cl_active_tents )
|
|
return;
|
|
|
|
// !!!BUGBUG -- This needs to be time based
|
|
// g-cont. it's used only for flickering dlights, what difference ?
|
|
gTempEntFrame = (gTempEntFrame + 1) & 31;
|
|
|
|
current = cl_active_tents;
|
|
|
|
// !!! Don't simulate while paused.... This is sort of a hack, revisit.
|
|
if( cls.key_dest != key_game )
|
|
{
|
|
while( current )
|
|
{
|
|
re->AddTmpEntity( current, ED_TEMPENTITY );
|
|
current = current->next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pprev = NULL;
|
|
|
|
while( current )
|
|
{
|
|
bool fTempEntFreed = false;
|
|
|
|
pnext = current->next;
|
|
|
|
// Kill it
|
|
if( !CL_IsActiveTempEnt( current ) || !CL_UpdateTempEnt( current, gTempEntFrame ))
|
|
{
|
|
CL_FreeTempEnt( current, pprev );
|
|
fTempEntFreed = true;
|
|
}
|
|
else
|
|
{
|
|
// renderer rejected entity for some reasons...
|
|
if( !re->AddTmpEntity( current, ED_TEMPENTITY ))
|
|
{
|
|
if(!( current->flags & FTENT_PERSIST ))
|
|
{
|
|
// If we can't draw it this frame, just dump it.
|
|
current->die = clgame.globals->time;
|
|
// don't fade out, just die
|
|
current->flags &= ~FTENT_FADEOUT;
|
|
|
|
CL_FreeTempEnt( current, pprev );
|
|
fTempEntFreed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !fTempEntFreed )
|
|
pprev = current;
|
|
current = pnext;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CL_BloodSprite( float *org, int colorIndex, int modelIndex, float size )
|
|
{
|
|
TEMPENTITY *pTemp;
|
|
|
|
if( CM_GetModelType( modelIndex ) == mod_bad )
|
|
return;
|
|
|
|
// Large, single blood sprite is a high-priority tent
|
|
if( pTemp = CL_TempEntAllocHigh( org, modelIndex ))
|
|
{
|
|
int frameCount;
|
|
|
|
Mod_GetFrames( modelIndex, &frameCount );
|
|
pTemp->renderMode = kRenderTransTexture;
|
|
pTemp->renderFX = kRenderFxClampMinScale;
|
|
pTemp->m_flSpriteScale = Com_RandomFloat(( size / 25.0f), ( size / 35.0f ));
|
|
pTemp->flags = FTENT_SPRANIMATE;
|
|
|
|
CL_GetPaletteColor( colorIndex, pTemp->renderColor );
|
|
pTemp->m_flFrameRate = frameCount * 4; // Finish in 0.250 seconds
|
|
// play the whole thing once
|
|
pTemp->die = clgame.globals->time + (frameCount / pTemp->m_flFrameRate);
|
|
|
|
pTemp->angles[2] = Com_RandomLong( 0, 360 );
|
|
pTemp->bounceFactor = 0;
|
|
}
|
|
}
|
|
|
|
void CL_BreakModel( float *pos, float *size, float *dir, float random, float life, int count, int modelIndex, char flags )
|
|
{
|
|
int i, frameCount;
|
|
TEMPENTITY *pTemp;
|
|
char type;
|
|
|
|
if( !modelIndex ) return;
|
|
|
|
type = flags & BREAK_TYPEMASK;
|
|
|
|
if( CM_GetModelType( modelIndex ) == mod_bad )
|
|
return;
|
|
|
|
Mod_GetFrames( modelIndex, &frameCount );
|
|
|
|
if( count == 0 )
|
|
{
|
|
// assume surface (not volume)
|
|
count = (size[0] * size[1] + size[1] * size[2] + size[2] * size[0]) / (3 * SHARD_VOLUME * SHARD_VOLUME);
|
|
}
|
|
|
|
// limit to 100 pieces
|
|
if( count > 100 ) count = 100;
|
|
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
vec3_t vecSpot;
|
|
|
|
// fill up the box with stuff
|
|
vecSpot[0] = pos[0] + Com_RandomFloat( -0.5f, 0.5f ) * size[0];
|
|
vecSpot[1] = pos[1] + Com_RandomFloat( -0.5f, 0.5f ) * size[1];
|
|
vecSpot[2] = pos[2] + Com_RandomFloat( -0.5f, 0.5f ) * size[2];
|
|
|
|
pTemp = CL_TempEntAlloc( vecSpot, modelIndex );
|
|
if( !pTemp ) return;
|
|
|
|
// keep track of break_type, so we know how to play sound on collision
|
|
pTemp->hitSound = type;
|
|
|
|
if( CM_GetModelType( modelIndex ) == mod_sprite )
|
|
pTemp->m_flFrame = Com_RandomLong( 0, frameCount - 1 );
|
|
else if( CM_GetModelType( modelIndex ) == mod_studio )
|
|
pTemp->body = Com_RandomLong( 0, frameCount - 1 );
|
|
|
|
pTemp->flags |= FTENT_COLLIDEWORLD | FTENT_FADEOUT | FTENT_SLOWGRAVITY;
|
|
|
|
if( Com_RandomLong( 0, 255 ) < 200 )
|
|
{
|
|
pTemp->flags |= FTENT_ROTATE;
|
|
pTemp->m_vecAvelocity[0] = Com_RandomFloat( -256, 255 );
|
|
pTemp->m_vecAvelocity[1] = Com_RandomFloat( -256, 255 );
|
|
pTemp->m_vecAvelocity[2] = Com_RandomFloat( -256, 255 );
|
|
}
|
|
|
|
if(( Com_RandomLong( 0, 255 ) < 100 ) && ( flags & BREAK_SMOKE ))
|
|
pTemp->flags |= FTENT_SMOKETRAIL;
|
|
|
|
if(( type == BREAK_GLASS ) || ( flags & BREAK_TRANS ))
|
|
{
|
|
pTemp->renderMode = kRenderTransTexture;
|
|
pTemp->renderAmt = pTemp->startAlpha = 128;
|
|
}
|
|
else
|
|
{
|
|
pTemp->renderMode = kRenderNormal;
|
|
pTemp->renderAmt = 255; // set this for fadeout
|
|
}
|
|
|
|
pTemp->m_vecVelocity[0] = dir[0] + Com_RandomFloat( -random, random );
|
|
pTemp->m_vecVelocity[1] = dir[1] + Com_RandomFloat( -random, random );
|
|
pTemp->m_vecVelocity[2] = dir[2] + Com_RandomFloat( 0, random );
|
|
|
|
pTemp->die = clgame.globals->time + life + Com_RandomFloat( 0, 1 ); // Add an extra 0-1 secs of life
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_TempModel
|
|
===============
|
|
*/
|
|
TEMPENTITY *CL_TempModel( float *pos, float *dir, float *ang, float life, int modelIndex, int type )
|
|
{
|
|
// alloc a new tempent
|
|
TEMPENTITY *pTemp = CL_TempEntAlloc( pos, modelIndex );
|
|
if( !pTemp ) return NULL;
|
|
|
|
// keep track of shell type
|
|
switch( type )
|
|
{
|
|
case 1: pTemp->hitSound = TE_BOUNCE_SHELL; break;
|
|
case 2: pTemp->hitSound = TE_BOUNCE_SHOTSHELL; break;
|
|
}
|
|
|
|
if( pos ) VectorCopy( pos, pTemp->origin );
|
|
if( dir ) VectorCopy( dir, pTemp->m_vecVelocity );
|
|
if( ang ) VectorCopy( ang, pTemp->angles );
|
|
pTemp->body = 0;
|
|
pTemp->flags = (FTENT_COLLIDEWORLD|FTENT_FADEOUT|FTENT_GRAVITY|FTENT_ROTATE);
|
|
pTemp->m_vecAvelocity[0] = Com_RandomFloat( -255, 255 );
|
|
pTemp->m_vecAvelocity[1] = Com_RandomFloat( -255, 255 );
|
|
pTemp->m_vecAvelocity[2] = Com_RandomFloat( -255, 255 );
|
|
pTemp->renderMode = kRenderNormal;
|
|
pTemp->startAlpha = 255;
|
|
pTemp->die = clgame.globals->time + life;
|
|
|
|
return pTemp;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_DefaultSprite
|
|
===============
|
|
*/
|
|
TEMPENTITY *CL_DefaultSprite( float *pos, int spriteIndex, float framerate )
|
|
{
|
|
TEMPENTITY *pTemp;
|
|
int frameCount;
|
|
|
|
if( !spriteIndex || CM_GetModelType( spriteIndex ) != mod_sprite )
|
|
{
|
|
MsgDev( D_ERROR, "No Sprite %d!\n", spriteIndex );
|
|
return NULL;
|
|
}
|
|
|
|
Mod_GetFrames( spriteIndex, &frameCount );
|
|
|
|
pTemp = CL_TempEntAlloc( pos, spriteIndex );
|
|
if( !pTemp ) return NULL;
|
|
|
|
pTemp->m_flFrameMax = frameCount - 1;
|
|
pTemp->m_flSpriteScale = 1.0f;
|
|
pTemp->flags |= FTENT_SPRANIMATE;
|
|
if( framerate == 0 ) framerate = 10;
|
|
|
|
pTemp->m_flFrameRate = framerate;
|
|
pTemp->die = clgame.globals->time + (float)frameCount / framerate;
|
|
pTemp->m_flFrame = 0;
|
|
|
|
return pTemp;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_TempSprite
|
|
===============
|
|
*/
|
|
TEMPENTITY *CL_TempSprite( float *pos, float *dir, float scale, int modelIndex, int rendermode, int renderfx, float a, float life, int flags )
|
|
{
|
|
TEMPENTITY *pTemp;
|
|
int frameCount;
|
|
|
|
if( !modelIndex )
|
|
return NULL;
|
|
|
|
if( CM_GetModelType( modelIndex ) == mod_bad )
|
|
{
|
|
MsgDev( D_ERROR, "No model %d!\n", modelIndex );
|
|
return NULL;
|
|
}
|
|
|
|
Mod_GetFrames( modelIndex, &frameCount );
|
|
|
|
pTemp = CL_TempEntAlloc( pos, modelIndex );
|
|
if( !pTemp ) return NULL;
|
|
|
|
pTemp->m_flFrameMax = frameCount - 1;
|
|
pTemp->m_flFrameRate = 10;
|
|
pTemp->renderMode = rendermode;
|
|
pTemp->renderFX = renderfx;
|
|
pTemp->m_flSpriteScale = scale;
|
|
pTemp->startAlpha = a * 255;
|
|
pTemp->renderAmt = a * 255;
|
|
pTemp->flags |= flags;
|
|
|
|
if( pos ) VectorCopy( pos, pTemp->origin );
|
|
if( dir ) VectorCopy( dir, pTemp->m_vecVelocity );
|
|
if( life ) pTemp->die = clgame.globals->time + life;
|
|
else pTemp->die = clgame.globals->time + (frameCount * 0.1f) + 1.0f;
|
|
pTemp->m_flFrame = 0;
|
|
|
|
return pTemp;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_MuzzleFlash
|
|
|
|
NOTE: this temp entity live once one frame
|
|
so we wan't add it to tent list
|
|
===============
|
|
*/
|
|
void CL_MuzzleFlash( int modelIndex, int entityIndex, int iAttachment, int type )
|
|
{
|
|
vec3_t pos;
|
|
TEMPENTITY *pTemp;
|
|
int frameCount;
|
|
float scale;
|
|
int index;
|
|
|
|
if( CM_GetModelType( modelIndex ) == mod_bad )
|
|
{
|
|
MsgDev( D_ERROR, "Invalid muzzleflash %d!\n", modelIndex );
|
|
return;
|
|
}
|
|
|
|
Mod_GetFrames( modelIndex, &frameCount );
|
|
if( !CL_GetAttachment( entityIndex, iAttachment, pos, NULL ))
|
|
{
|
|
MsgDev( D_ERROR, "Invalid muzzleflash entity %d!\n", entityIndex );
|
|
return;
|
|
}
|
|
|
|
// must set position for right culling on render
|
|
pTemp = CL_TempEntAlloc( pos, modelIndex );
|
|
if( !pTemp ) return;
|
|
|
|
index = type % 10;
|
|
scale = (type / 10) * 0.1f;
|
|
if( scale == 0.0f ) scale = 0.5f;
|
|
|
|
pTemp->renderMode = kRenderTransAdd;
|
|
pTemp->renderAmt = 255;
|
|
pTemp->renderFX = 0;
|
|
pTemp->die = clgame.globals->time + 0.01; // die at next frame
|
|
pTemp->m_flFrame = Com_RandomLong( 0, frameCount - 1 );
|
|
pTemp->m_flFrameMax = frameCount - 1;
|
|
pTemp->clientIndex = entityIndex;
|
|
pTemp->m_iAttachment = iAttachment;
|
|
|
|
if( index == 0 )
|
|
{
|
|
// Rifle flash
|
|
pTemp->m_flSpriteScale = scale * Com_RandomFloat( 0.5f, 0.6f );
|
|
pTemp->angles[2] = 90 * Com_RandomLong( 0, 3 );
|
|
}
|
|
else
|
|
{
|
|
pTemp->m_flSpriteScale = scale;
|
|
pTemp->angles[2] = Com_RandomLong( 0, 359 );
|
|
}
|
|
|
|
// render now (guranteed that muzzleflash will be draw)
|
|
re->AddTmpEntity( pTemp, ED_TEMPENTITY );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_SpriteExplode
|
|
|
|
just a preset for exploding sprite
|
|
===============
|
|
*/
|
|
void CL_SpriteExplode( TEMPENTITY *pTemp, float scale, int flags )
|
|
{
|
|
if( !pTemp ) return;
|
|
|
|
// NOTE: Xash3D doesn't needs this stuff, because sprites
|
|
// have right rendermodes already at loading point
|
|
// but i'm leave it for backward compatibility
|
|
if( flags & TE_EXPLFLAG_NOADDITIVE )
|
|
{
|
|
// solid sprite
|
|
pTemp->renderMode = kRenderNormal;
|
|
pTemp->renderAmt = 255;
|
|
}
|
|
else if( flags & TE_EXPLFLAG_DRAWALPHA )
|
|
{
|
|
// alpha sprite
|
|
pTemp->renderMode = kRenderTransAlpha;
|
|
pTemp->renderAmt = 180;
|
|
}
|
|
else
|
|
{
|
|
// additive sprite
|
|
pTemp->renderMode = kRenderTransAdd;
|
|
pTemp->renderAmt = 120;
|
|
}
|
|
|
|
if( flags & TE_EXPLFLAG_ROTATE )
|
|
{
|
|
pTemp->angles[2] = Com_RandomLong( 0, 360 );
|
|
}
|
|
|
|
pTemp->renderFX = kRenderFxNone;
|
|
pTemp->m_vecVelocity[2] = 8;
|
|
pTemp->origin[2] += 10;
|
|
pTemp->m_flSpriteScale = scale;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_SpriteExplode
|
|
|
|
just a preset for smoke sprite
|
|
===============
|
|
*/
|
|
void CL_SpriteSmoke( TEMPENTITY *pTemp, float scale )
|
|
{
|
|
int iColor;
|
|
|
|
if( !pTemp ) return;
|
|
|
|
iColor = Com_RandomLong( 20, 35 );
|
|
pTemp->renderMode = kRenderTransAlpha;
|
|
pTemp->renderFX = kRenderFxNone;
|
|
pTemp->m_vecVelocity[2] = 30;
|
|
VectorSet( pTemp->renderColor, iColor, iColor, iColor );
|
|
pTemp->origin[2] += 20;
|
|
pTemp->m_flSpriteScale = scale;
|
|
pTemp->flags = FTENT_WINDBLOWN;
|
|
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_SpriteSpray
|
|
|
|
just a preset for smoke sprite
|
|
===============
|
|
*/
|
|
void CL_SpriteSpray( float *pos, float *dir, int modelIndex, int count, int speed, int iRand )
|
|
{
|
|
TEMPENTITY *pTemp;
|
|
float noise;
|
|
float znoise;
|
|
int i, frameCount;
|
|
|
|
noise = (float)iRand / 100;
|
|
|
|
// more vertical displacement
|
|
znoise = noise * 1.5f;
|
|
|
|
if( znoise > 1 ) znoise = 1;
|
|
|
|
if( CM_GetModelType( modelIndex ) == mod_bad )
|
|
{
|
|
MsgDev( D_ERROR, "No model %d!\n", modelIndex );
|
|
return;
|
|
}
|
|
|
|
Mod_GetFrames( modelIndex, &frameCount );
|
|
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
vec3_t velocity;
|
|
float scale;
|
|
|
|
pTemp = CL_TempEntAlloc( pos, modelIndex );
|
|
if( !pTemp ) return;
|
|
|
|
pTemp->renderMode = kRenderTransAlpha;
|
|
pTemp->renderFX = kRenderFxNoDissipation;
|
|
pTemp->m_flSpriteScale = 0.5f;
|
|
pTemp->flags |= FTENT_FADEOUT|FTENT_SLOWGRAVITY;
|
|
pTemp->fadeSpeed = 2.0f;
|
|
|
|
// make the spittle fly the direction indicated, but mix in some noise.
|
|
velocity[0] = dir[0] + Com_RandomFloat( -noise, noise );
|
|
velocity[1] = dir[1] + Com_RandomFloat( -noise, noise );
|
|
velocity[2] = dir[2] + Com_RandomFloat( 0, znoise );
|
|
scale = Com_RandomFloat(( speed * 0.8f ), ( speed * 1.2f ));
|
|
VectorScale( velocity, scale, pTemp->m_vecVelocity );
|
|
pTemp->die = clgame.globals->time + 0.35f;
|
|
pTemp->m_flFrame = Com_RandomLong( 0, frameCount - 1 );
|
|
}
|
|
}
|
|
|
|
void CL_Sprite_Trail( int type, float *vecStart, float *vecEnd, int modelIndex, int nCount, float flLife, float flSize, float flAmplitude, int nRenderamt, float flSpeed )
|
|
{
|
|
TEMPENTITY *pTemp;
|
|
vec3_t vecDelta, vecDir;
|
|
int i, flFrameCount;
|
|
|
|
if( CM_GetModelType( modelIndex ) == mod_bad )
|
|
{
|
|
MsgDev( D_ERROR, "No model %d!\n", modelIndex );
|
|
return;
|
|
}
|
|
|
|
Mod_GetFrames( modelIndex, &flFrameCount );
|
|
|
|
VectorSubtract( vecEnd, vecStart, vecDelta );
|
|
|
|
VectorCopy( vecDelta, vecDir );
|
|
VectorNormalize( vecDir );
|
|
|
|
flAmplitude /= 256.0;
|
|
|
|
for ( i = 0; i < nCount; i++ )
|
|
{
|
|
vec3_t vecPos, vecVel;
|
|
|
|
// Be careful of divide by 0 when using 'count' here...
|
|
if ( i == 0 )
|
|
{
|
|
VectorMA( vecStart, 0, vecDelta, vecPos );
|
|
}
|
|
else
|
|
{
|
|
VectorMA( vecStart, i / (nCount - 1.0), vecDelta, vecPos );
|
|
}
|
|
|
|
pTemp = CL_TempEntAlloc( vecPos, modelIndex );
|
|
if( !pTemp ) return;
|
|
|
|
pTemp->flags |= FTENT_COLLIDEWORLD | FTENT_SPRCYCLE | FTENT_FADEOUT | FTENT_SLOWGRAVITY;
|
|
|
|
VectorScale( vecDir, flSpeed, vecVel );
|
|
vecVel[0] += Com_RandomLong( -127, 128 ) * flAmplitude;
|
|
vecVel[1] += Com_RandomLong( -127, 128 ) * flAmplitude;
|
|
vecVel[2] += Com_RandomLong( -127, 128 ) * flAmplitude;
|
|
VectorCopy( vecVel, pTemp->m_vecVelocity );
|
|
VectorCopy( vecPos, pTemp->origin );
|
|
|
|
pTemp->m_flSpriteScale = flSize;
|
|
pTemp->renderMode = kRenderGlow;
|
|
pTemp->renderFX = kRenderFxNoDissipation;
|
|
pTemp->renderAmt = pTemp->startAlpha = nRenderamt;
|
|
|
|
pTemp->m_flFrame = Com_RandomLong( 0, flFrameCount - 1 );
|
|
pTemp->m_flFrameMax = flFrameCount - 1;
|
|
pTemp->die = clgame.globals->time + flLife + Com_RandomFloat( 0, 4 );
|
|
}
|
|
} |