This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/client/cl_tent.c

465 lines
10 KiB
C

//=======================================================================
// Copyright XashXT Group 2009 ©
// cl_tent.c - temp entity effects management
//=======================================================================
#include "common.h"
#include "client.h"
#include "effects_api.h"
#include "const.h"
/*
==============================================================
TEMPENTS MANAGEMENT
==============================================================
*/
#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 )
{
// 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;
pTemp->modelindex = modelIndex;
pTemp->renderMode = kRenderNormal;
pTemp->renderFX = kRenderFxNone;
pTemp->renderAmt = 255;
pTemp->body = 0;
pTemp->skin = 0;
pTemp->fadeSpeed = 0.5f;
pTemp->hitSound = 0;
pTemp->clientIndex = -1;
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;
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;
}
}
/*
===============
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( float *pos, int modelIndex, int type )
{
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 );
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;
VectorCopy( pos, pTemp->origin );
pTemp->die = clgame.globals->time + 0.02f;
pTemp->m_flFrame = Com_RandomLong( 0, frameCount - 1 );
pTemp->m_flFrameMax = frameCount - 1;
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 );
}
re->AddTmpEntity( pTemp, ED_TEMPENTITY );
}