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_effects.c

1172 lines
23 KiB
C

//=======================================================================
// Copyright XashXT Group 2008 ©
// cl_effects.c - entity effects parsing and management
//=======================================================================
#include "common.h"
#include "client.h"
#include "const.h"
/*
==============================================================
LIGHT STYLE MANAGEMENT
==============================================================
*/
typedef struct
{
int length;
float value[3];
float map[MAX_STRING];
} clightstyle_t;
clightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
static int lastofs;
/*
================
CL_ClearLightStyles
================
*/
void CL_ClearLightStyles( void )
{
Mem_Set( cl_lightstyle, 0, sizeof( cl_lightstyle ));
lastofs = -1;
}
/*
================
CL_RunLightStyles
light animations
'm' is normal light, 'a' is no light, 'z' is double bright
================
*/
void CL_RunLightStyles( void )
{
int i, ofs;
float l, oldval, curval;
clightstyle_t *ls;
if( cls.state != ca_active ) return;
ofs = (cl.time * 10);
if( !cl_lightstyle_lerping->integer )
{
if( ofs == lastofs ) return;
lastofs = ofs;
}
for( i = 0, ls = cl_lightstyle; i < MAX_LIGHTSTYLES; i++, ls++ )
{
if( ls->length == 0 ) l = 0.0f;
else if( ls->length == 1 ) l = ls->map[0];
else if( cl_lightstyle_lerping->integer )
{
int curnum = (ofs % ls->length);
int oldnum = curnum - 1;
// sequence is ends ?
if( oldnum < 0 )
oldnum += ls->length;
oldval = ls->map[oldnum];
curval = ls->map[curnum];
// don't lerping fast sequences
if( fabs( curval - oldval ) >= 1.0f ) l = curval;
else l = oldval + cl.lerpFrac * (curval - oldval);
}
else l = ls->map[ofs%ls->length];
VectorSet( ls->value, l, l, l );
}
}
void CL_SetLightstyle( int i )
{
char *s;
int j, k;
s = cl.configstrings[i+CS_LIGHTSTYLES];
j = com.strlen( s );
if( j >= MAX_STRING )
Host_Error("CL_SetLightStyle: lightstyle %s is too long\n", s );
cl_lightstyle[i].length = j;
for( k = 0; k < j; k++ )
cl_lightstyle[i].map[k] = (float)(s[k]-'a') / (float)('m'-'a');
}
/*
================
CL_AddLightStyles
================
*/
void CL_AddLightStyles( void )
{
int i;
clightstyle_t *ls;
for( i = 0, ls = cl_lightstyle; i < MAX_LIGHTSTYLES; i++, ls++ )
re->AddLightStyle( i, ls->value );
}
/*
==============================================================
DLIGHT MANAGEMENT
==============================================================
*/
struct dlight_s
{
vec3_t origin;
float radius;
byte color[3];
float die; // stop lighting after this time
float decay; // drop this each second
float minlight; // don't add when contributing less
int key;
bool dark; // subtracts light instead of adding
bool elight; // true when calls with CL_AllocElight
};
dlight_t cl_dlights[MAX_DLIGHTS];
/*
================
CL_ClearDlights
================
*/
void CL_ClearDlights( void )
{
Mem_Set( cl_dlights, 0, sizeof( cl_dlights ));
}
/*
===============
CL_AllocDlight
===============
*/
dlight_t *CL_AllocDlight( int key )
{
dlight_t *dl;
int i;
// first look for an exact key match
if( key )
{
for( i = 0, dl = cl_dlights; i < MAX_DLIGHTS; i++, dl++ )
{
if( dl->key == key )
{
// reuse this light
Mem_Set( dl, 0, sizeof( *dl ));
dl->key = key;
return dl;
}
}
}
// then look for anything else
for( i = 0, dl = cl_dlights; i < MAX_DLIGHTS; i++, dl++ )
{
if( dl->die < cl.time )
{
Mem_Set( dl, 0, sizeof( *dl ));
dl->key = key;
return dl;
}
}
// otherwise grab first dlight
dl = &cl_dlights[0];
Mem_Set( dl, 0, sizeof( *dl ));
dl->key = key;
return dl;
}
/*
===============
CL_AllocElight
===============
*/
dlight_t *CL_AllocElight( int key )
{
dlight_t *dl;
dl = CL_AllocDlight( key );
dl->elight = true;
return dl;
}
/*
===============
CL_AddDlight
copy dlight to renderer
===============
*/
bool CL_AddDlight( dlight_t *dl )
{
int flags = 0;
bool add;
if( dl->dark ) flags |= DLIGHT_DARK;
if( dl->elight ) flags |= DLIGHT_ONLYENTS;
add = re->AddDLight( dl->origin, dl->color, dl->radius, flags );
return add;
}
/*
===============
CL_AddDLights
===============
*/
void CL_AddDLights( void )
{
dlight_t *dl;
float time;
int i;
time = cl.time - cl.oldtime;
for( i = 0, dl = cl_dlights; i < MAX_DLIGHTS; i++, dl++ )
{
if( dl->die < cl.time || !dl->radius )
continue;
dl->radius -= time * dl->decay;
if( dl->radius < 0 ) dl->radius = 0;
CL_AddDlight( dl );
}
}
/*
================
CL_TestLights
if cl_testlights is set, create 32 lights models
================
*/
void CL_TestLights( void )
{
int i, j;
float f, r;
dlight_t dl;
if( !cl_testlights->integer ) return;
Mem_Set( &dl, 0, sizeof( dlight_t ));
for( i = 0; i < bound( 1, cl_testlights->integer, MAX_DLIGHTS ); i++ )
{
r = 64 * ( (i%4) - 1.5 );
f = 64 * (i/4) + 128;
for( j = 0; j < 3; j++ )
dl.origin[j] = cl.refdef.vieworg[j] + cl.refdef.forward[j] * f + cl.refdef.right[j] * r;
dl.color[0] = ((((i%6)+1) & 1)>>0) * 255;
dl.color[1] = ((((i%6)+1) & 2)>>1) * 255;
dl.color[2] = ((((i%6)+1) & 4)>>2) * 255;
dl.radius = 200;
if( !CL_AddDlight( &dl ))
break;
}
}
/*
==============================================================
PARTICLE MANAGEMENT
==============================================================
*/
struct particle_s
{
// this part are shared both client and engine
byte color; // colorIndex for R_GetPaletteColor
vec3_t org;
vec3_t vel;
float die;
float ramp;
int type;
// this part are private for engine
particle_t *next;
poly_t poly;
vec3_t pVerts[4];
vec2_t pStcoords[4];
rgba_t pColor[4];
};
#define NUMVERTEXNORMALS 162 // quake avertex normals
float cl_avertexnormals[NUMVERTEXNORMALS][3] =
{
#include "anorms.h"
};
// particle ramps
int ramp1[8] = { 0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61 };
int ramp2[8] = { 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66 };
int ramp3[8] = { 0x6d, 0x6b, 6, 5, 4, 3 };
particle_t *cl_active_particles, *cl_free_particles;
static vec3_t cl_particle_avelocities[NUMVERTEXNORMALS];
static particle_t cl_particle_list[MAX_PARTICLES];
static rgb_t cl_particlePalette[256];
void CL_GetPaletteColor( int colorIndex, vec3_t outColor )
{
if( !outColor ) return;
colorIndex = bound( 0, colorIndex, 255 );
VectorCopy( cl_particlePalette[colorIndex], outColor );
}
/*
=================
CL_AllocParticle
=================
*/
particle_t *CL_AllocParticle( void )
{
particle_t *p;
if( !cl_free_particles )
return NULL;
p = cl_free_particles;
cl_free_particles = p->next;
p->next = cl_active_particles;
cl_active_particles = p;
return p;
}
/*
===============
CL_ClearParticles
===============
*/
void CL_ClearParticles( void )
{
int i;
particle_t *p;
rgbdata_t *pic;
byte buf[1]; // fake stub
pic = FS_LoadImage( "#quake.pal", buf, 768 );
cl_active_particles = NULL;
cl_free_particles = cl_particle_list;
for( i = 0; i < MAX_PARTICLES; i++ )
cl_particle_list[i].next = &cl_particle_list[i+1];
cl_particle_list[MAX_PARTICLES-1].next = NULL;
for( i = 0; i < NUMVERTEXNORMALS; i++ )
{
cl_particle_avelocities[i][0] = (rand() & 255) * 0.01f;
cl_particle_avelocities[i][1] = (rand() & 255) * 0.01f;
cl_particle_avelocities[i][2] = (rand() & 255) * 0.01f;
}
for( i = 0, p = cl_particle_list; i < MAX_PARTICLES; i++, p++ )
{
p->pStcoords[0][0] = p->pStcoords[2][1] = p->pStcoords[1][0] = p->pStcoords[1][1] = 0;
p->pStcoords[0][1] = p->pStcoords[2][0] = p->pStcoords[3][0] = p->pStcoords[3][1] = 1;
}
// Xash3D have built-in Quake1 palette - use it
for( i = 0; pic && i < 256; i++ )
{
cl_particlePalette[i][0] = pic->palette[i*4+0];
cl_particlePalette[i][1] = pic->palette[i*4+1];
cl_particlePalette[i][2] = pic->palette[i*4+2];
}
if( pic ) FS_FreeImage( pic );
else MsgDev( D_WARN, "SV_InitParticles: palette not installed\n" );
}
/*
===============
CL_AddParticles
===============
*/
void CL_AddParticles( void )
{
particle_t *p, *kill;
float time1, time2, time3;
float grav, dvel, frametime;
vec3_t up, right, point1;
float scale = 3.0f; // FIXME: make cvar
rgba_t modulate;
int i;
VectorScale( cl.refdef.right, scale, right );
VectorScale( cl.refdef.up, scale, up );
frametime = cl.time - cl.oldtime;
time3 = frametime * 15;
time2 = frametime * 10; // 15;
time1 = frametime * 5;
grav = frametime * clgame.movevars.gravity * 0.05f;
dvel = frametime * 4;
while( 1 )
{
// free time-expired particles
kill = cl_active_particles;
if( kill && kill->die < clgame.globals->time )
{
cl_active_particles = kill->next;
kill->next = cl_free_particles;
cl_free_particles = kill;
continue;
}
break;
}
for( p = cl_active_particles; p; p = p->next )
{
while( 1 )
{
kill = p->next;
if( kill && kill->die < clgame.globals->time )
{
p->next = kill->next;
kill->next = cl_free_particles;
cl_free_particles = kill;
continue;
}
break;
}
scale = 0.0f;
// hack a scale up to keep particles from disapearing
scale += (p->org[0] - cl.refdef.vieworg[0]) * cl.refdef.forward[0];
scale += (p->org[1] - cl.refdef.vieworg[1]) * cl.refdef.forward[1];
scale += (p->org[2] - cl.refdef.vieworg[2]) * cl.refdef.forward[2];
if( scale < 20.0f ) scale = 1.0f;
else scale = 1.0f + scale * 0.004f;
// setup color
modulate[0] = cl_particlePalette[p->color][0];
modulate[1] = cl_particlePalette[p->color][1];
modulate[2] = cl_particlePalette[p->color][2];
modulate[3] = 0xFF; // quake particles doesn't have transparency
*(int *)p->pColor[0] = *(int *)modulate;
*(int *)p->pColor[1] = *(int *)modulate;
*(int *)p->pColor[2] = *(int *)modulate;
*(int *)p->pColor[3] = *(int *)modulate;
VectorMA( p->org, 1.0f, up, point1 );
VectorMA( point1, 0.0f, right, p->pVerts[0] );
VectorMA( p->org, 0.0f, up, point1 );
VectorMA( point1, 0.0f, right, p->pVerts[1] );
VectorMA( p->org, 0.0f, up, point1 );
VectorMA( point1, 1.0f, right, p->pVerts[2] );
VectorMA( p->org, 1.0f, up, point1 );
VectorMA( point1, 1.0f, right, p->pVerts[3] );
p->poly.numverts = 4;
p->poly.verts = p->pVerts;
p->poly.stcoords = p->pStcoords;
p->poly.colors = p->pColor;
p->poly.shadernum = cls.particle;
p->poly.fognum = -1;
// send the particle to the renderer
re->AddPolygon( &p->poly );
// evaluate particle
p->org[0] += p->vel[0] * frametime;
p->org[1] += p->vel[1] * frametime;
p->org[2] += p->vel[2] * frametime;
switch( p->type )
{
case pt_static:
break;
case pt_fire:
p->ramp += time1;
if( p->ramp >= 6 )
p->die = -1;
else p->color = ramp3[(int)p->ramp];
p->vel[2] += grav;
break;
case pt_explode:
p->ramp += time2;
if( p->ramp >=8 )
p->die = -1;
else p->color = ramp1[(int)p->ramp];
for( i = 0; i < 3; i++ )
p->vel[i] += p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_explode2:
p->ramp += time3;
if( p->ramp >=8 )
p->die = -1;
else p->color = ramp2[(int)p->ramp];
for( i = 0; i < 3; i++ )
p->vel[i] -= p->vel[i] * frametime;
p->vel[2] -= grav;
break;
case pt_blob:
for( i = 0; i < 3; i++ )
p->vel[i] += p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_blob2:
for( i = 0; i < 2; i++ )
p->vel[i] -= p->vel[i] * dvel;
p->vel[2] -= grav;
break;
case pt_grav:
p->vel[2] -= grav * 20;
break;
case pt_slowgrav:
p->vel[2] -= grav;
break;
}
}
}
/*
===============
CL_ParticleEffect
old good quake1 particles
===============
*/
void CL_ParticleEffect( const vec3_t org, const vec3_t dir, int color, int count )
{
int i, j;
particle_t *p;
for( i = 0; i < count; i++ )
{
p = CL_AllocParticle();
if( !p ) return;
if( count == 1024 )
{
// rocket explosion
p->die = clgame.globals->time + 5.0f;
p->color = ramp1[0];
p->ramp = rand() & 3;
if( i & 1 )
{
p->type = pt_explode;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
else
{
p->type = pt_explode2;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
else
{
p->die = clgame.globals->time + 0.1f * (rand() % 5);
p->color = (color & ~7) + (rand() & 7);
p->type = pt_slowgrav;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() & 15 ) - 8);
p->vel[j] = dir[j] * 15;// + (rand() % 300) - 150;
}
}
}
}
/*
===============
CL_EntityParticles
EF_BRIGHTFIELD effect
===============
*/
void CL_EntityParticles( edict_t *ent )
{
int i, count;
float angle;
float sr, sp, sy, cr, cp, cy;
float dist, beamlength = 16;
vec3_t forward;
particle_t *p;
dist = 64;
count = 50;
for( i = 0; i < NUMVERTEXNORMALS; i++ )
{
angle = clgame.globals->time * cl_particle_avelocities[i][0];
com.sincos( angle, &sy, &cy );
angle = clgame.globals->time * cl_particle_avelocities[i][1];
com.sincos( angle, &sp, &cp );
angle = clgame.globals->time * cl_particle_avelocities[i][2];
com.sincos( angle, &sr, &cr );
forward[0] = cp * cy;
forward[1] = cp * sy;
forward[2] = -sp;
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 0.01f;
p->color = 0x6f;
p->type = pt_explode;
p->org[0] = ent->v.origin[0] + cl_avertexnormals[i][0] * dist + forward[0] * beamlength;
p->org[1] = ent->v.origin[1] + cl_avertexnormals[i][1] * dist + forward[1] * beamlength;
p->org[2] = ent->v.origin[2] + cl_avertexnormals[i][2] * dist + forward[2] * beamlength;
}
}
/*
===============
CL_ParticleExplosion
===============
*/
void CL_ParticleExplosion( const vec3_t org )
{
int i, j;
particle_t *p;
for( i = 0; i < 1024; i++ )
{
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 5.0f;
p->color = ramp1[0];
p->ramp = rand() & 3;
if( i & 1 )
{
p->type = pt_explode;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
else
{
p->type = pt_explode2;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
}
/*
===============
CL_ParticleExplosion2
===============
*/
void CL_ParticleExplosion2( const vec3_t org, int colorStart, int colorLength)
{
int i, j;
int colorMod = 0;
particle_t *p;
for( i = 0; i < 512; i++ )
{
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 0.3f;
p->color = colorStart + (colorMod % colorLength);
colorMod++;
p->type = pt_blob;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
/*
===============
CL_BlobExplosion
===============
*/
void CL_BlobExplosion( const vec3_t org )
{
int i, j;
particle_t *p;
for( i = 0; i < 1024; i++ )
{
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 1.0f + (rand() & 8) * 0.05f;
if( i & 1 )
{
p->type = pt_blob;
p->color = 66 + rand() % 6;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
else
{
p->type = pt_blob2;
p->color = 150 + rand() % 6;
for( j = 0; j < 3; j++ )
{
p->org[j] = org[j] + ((rand() % 32) - 16);
p->vel[j] = (rand() % 512) - 256;
}
}
}
}
/*
===============
CL_LavaSplash
===============
*/
void CL_LavaSplash( const vec3_t org )
{
int i, j, k;
particle_t *p;
float vel;
vec3_t dir;
for( i = -16; i < 16; i++ )
{
for( j = -16; j <16; j++ )
{
for( k = 0; k < 1; k++ )
{
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 2.0f + (rand() & 31) * 0.02f;
p->color = 224 + (rand() & 7);
p->type = pt_slowgrav;
dir[0] = j * 8 + (rand() & 7);
dir[1] = i * 8 + (rand() & 7);
dir[2] = 256;
p->org[0] = org[0] + dir[0];
p->org[1] = org[1] + dir[1];
p->org[2] = org[2] + (rand() & 63);
VectorNormalize( dir );
vel = 50 + (rand() & 63);
VectorScale( dir, vel, p->vel );
}
}
}
}
/*
===============
CL_TeleportSplash
===============
*/
void CL_TeleportSplash( const vec3_t org )
{
int i, j, k;
particle_t *p;
float vel;
vec3_t dir;
for( i = -16; i < 16; i+=4 )
{
for( j =-16; j < 16; j += 4 )
{
for( k = -24; k < 32; k += 4 )
{
p = CL_AllocParticle();
if( !p ) return;
p->die = clgame.globals->time + 0.2f + (rand() & 7) * 0.02f;
p->color = 7 + (rand() & 7);
p->type = pt_slowgrav;
dir[0] = j * 8;
dir[1] = i * 8;
dir[2] = k * 8;
p->org[0] = org[0] + i + (rand() & 3);
p->org[1] = org[1] + j + (rand() & 3);
p->org[2] = org[2] + k + (rand() & 3);
VectorNormalize( dir );
vel = 50.0f + (rand() & 63);
VectorScale( dir, vel, p->vel );
}
}
}
}
/*
===============
CL_RocketTrail
===============
*/
void CL_RocketTrail( const vec3_t org, const vec3_t end, int type )
{
vec3_t vec, start;
float len;
particle_t *p;
int j, dec;
static int tracercount;
VectorCopy( org, start );
VectorSubtract( end, start, vec );
len = VectorNormalizeLength( vec );
if( type < 128 )
{
dec = 3;
}
else
{
dec = 1;
type -= 128;
}
while( len > 0 )
{
len -= dec;
p = CL_AllocParticle();
if( !p ) return;
VectorClear( p->vel );
p->die = clgame.globals->time + 2.0f;
switch( type )
{
case 0: // rocket trail
p->ramp = (rand() & 3);
p->color = ramp3[(int)p->ramp];
p->type = pt_fire;
for( j = 0; j < 3; j++ )
p->org[j] = start[j] + ((rand()%6)-3);
break;
case 1: // smoke smoke
p->ramp = (rand() & 3) + 2;
p->color = ramp3[(int)p->ramp];
p->type = pt_fire;
for( j = 0; j < 3; j++ )
p->org[j] = start[j] + ((rand() % 6) - 3);
break;
case 2: // blood
p->type = pt_grav;
p->color = 67 + (rand()&3);
for( j = 0; j < 3; j++ )
p->org[j] = start[j] + ((rand() % 6) - 3);
break;
case 3:
case 5: // tracer
p->die = clgame.globals->time + 0.5f;
p->type = pt_static;
if( type == 3 ) p->color = 52 + ((tracercount & 4)<<1);
else p->color = 230 + ((tracercount & 4)<<1);
tracercount++;
VectorCopy( start, p->org );
if( tracercount & 1 )
{
p->vel[0] = 30 * vec[1];
p->vel[1] = 30 * -vec[0];
}
else
{
p->vel[0] = 30 * -vec[1];
p->vel[1] = 30 * vec[0];
}
break;
case 4: // slight blood
p->type = pt_grav;
p->color = 67 + (rand() & 3);
for( j = 0; j < 3; j++ )
p->org[j] = start[j] + ((rand() % 6) - 3);
len -= 3;
break;
case 6: // voor trail
p->color = 9 * 16 + 8 + (rand() & 3);
p->type = pt_static;
p->die = clgame.globals->time + 0.3f;
for( j = 0; j < 3; j++ )
p->org[j] = start[j] + ((rand() & 15) - 8);
break;
}
VectorAdd( start, vec, start );
}
}
/*
===============
CL_DecalShoot
normal temporary decal
===============
*/
void CL_DecalShoot( HSPRITE hDecal, int entityIndex, int modelIndex, float *pos, int flags )
{
rgba_t color;
Vector4Set( color, 255, 255, 255, 255 ); // don't use custom colors
if( re ) re->DecalShoot( hDecal, entityIndex, modelIndex, pos, NULL, flags, color, 0.0f, 0.0f );
}
/*
===============
CL_PlayerDecal
spray custom colored decal (clan logo etc)
===============
*/
void CL_PlayerDecal( HSPRITE hDecal, int entityIndex, float *pos, byte *color )
{
edict_t *pEnt;
int modelIndex = 0;
pEnt = CL_GetEdictByIndex( entityIndex );
if( CL_IsValidEdict( pEnt )) modelIndex = pEnt->v.modelindex;
if( re ) re->DecalShoot( hDecal, entityIndex, modelIndex, pos, NULL, FDECAL_CUSTOM, color, 0.0f, 0.0f );
}
/*
===============
CL_LightForPoint
get lighting color for specified point
===============
*/
void CL_LightForPoint( const vec3_t point, vec3_t ambientLight )
{
if( re ) re->LightForPoint( point, ambientLight );
else VectorClear( ambientLight );
}
/*
===============
CL_ResetEvent
===============
*/
void CL_ResetEvent( event_info_t *ei )
{
Mem_Set( ei, 0, sizeof( *ei ));
}
word CL_PrecacheEvent( const char *name )
{
int i;
if( !name || !name[0] ) return 0;
for( i = 1; i < MAX_EVENTS && cl.configstrings[CS_EVENTS+i][0]; i++ )
if( !com.strcmp( cl.configstrings[CS_EVENTS+i], name ))
return i;
return 0;
}
bool CL_FireEvent( event_info_t *ei )
{
user_event_t *ev;
const char *name;
int i;
if( !ei || !ei->index )
return false;
// get the func pointer
for( i = 0; i < MAX_EVENTS; i++ )
{
ev = clgame.events[i];
if( !ev ) break;
if( ev->index == ei->index )
{
if( ev->func )
{
ev->func( &ei->args );
return true;
}
name = cl.configstrings[CS_EVENTS+ei->index];
MsgDev( D_ERROR, "CL_FireEvent: %s not hooked\n", name );
break;
}
}
return false;
}
void CL_FireEvents( void )
{
int i;
event_state_t *es;
event_info_t *ei;
bool success;
es = &cl.events;
for( i = 0; i < MAX_EVENT_QUEUE; i++ )
{
ei = &es->ei[i];
if( ei->index == 0 )
continue;
if( cls.state == ca_disconnected )
{
CL_ResetEvent( ei );
continue;
}
// delayed event!
if( ei->fire_time && ( ei->fire_time > clgame.globals->time ))
continue;
success = CL_FireEvent( ei );
// zero out the remaining fields
CL_ResetEvent( ei );
}
}
event_info_t *CL_FindEmptyEvent( void )
{
int i;
event_state_t *es;
event_info_t *ei;
es = &cl.events;
// look for first slot where index is != 0
for( i = 0; i < MAX_EVENT_QUEUE; i++ )
{
ei = &es->ei[i];
if( ei->index != 0 )
continue;
return ei;
}
// no slots available
return NULL;
}
event_info_t *CL_FindUnreliableEvent( void )
{
int i;
event_state_t *es;
event_info_t *ei;
es = &cl.events;
for ( i = 0; i < MAX_EVENT_QUEUE; i++ )
{
ei = &es->ei[i];
if( ei->index != 0 )
{
// it's reliable, so skip it
if( ei->flags & FEV_RELIABLE )
continue;
}
return ei;
}
// this should never happen
return NULL;
}
void CL_QueueEvent( int flags, int index, float delay, event_args_t *args )
{
bool unreliable = (flags & FEV_RELIABLE) ? false : true;
event_info_t *ei;
// find a normal slot
ei = CL_FindEmptyEvent();
if( !ei && unreliable )
{
return;
}
// okay, so find any old unreliable slot
if( !ei )
{
ei = CL_FindUnreliableEvent();
if( !ei ) return;
}
ei->index = index;
ei->fire_time = delay ? (clgame.globals->time + delay) : 0.0f;
ei->flags = flags;
// copy in args event data
Mem_Copy( &ei->args, args, sizeof( ei->args ));
}
/*
==============
CL_ClearEffects
==============
*/
void CL_ClearEffects( void )
{
CL_ClearParticles ();
CL_ClearDlights ();
CL_ClearLightStyles ();
}