537 lines
13 KiB
C
537 lines
13 KiB
C
//This file contains environmental effects for the designers
|
|
|
|
#include "cg_local.h"
|
|
#include "fx_local.h"
|
|
|
|
// these flags should be synchronized with the spawnflags in g_fx.c for fx_bolt
|
|
#define BOLT_SPARKS (1<<0)
|
|
#define BOLT_BORG (1<<1)
|
|
|
|
|
|
qboolean SparkThink( localEntity_t *le)
|
|
{
|
|
vec3_t dir, direction, start, end;
|
|
vec3_t velocity;
|
|
float scale = 0, alpha = 0;
|
|
int numSparks = 0, i = 0, j = 0;
|
|
sfxHandle_t snd = cgs.media.envSparkSound1;
|
|
|
|
switch(irandom(1, 3))
|
|
{
|
|
case 1:
|
|
snd = cgs.media.envSparkSound1;
|
|
break;
|
|
case 2:
|
|
snd = cgs.media.envSparkSound2;
|
|
break;
|
|
case 3:
|
|
snd = cgs.media.envSparkSound3;
|
|
break;
|
|
}
|
|
trap_S_StartSound (le->refEntity.origin, ENTITYNUM_WORLD, CHAN_BODY, snd );
|
|
|
|
VectorCopy(le->data.spawner.dir, dir);
|
|
|
|
AngleVectors( dir, dir, NULL, NULL );
|
|
for ( j = 0; j < 3; j ++ )
|
|
direction[j] = dir[j] + (0.25f * crandom());
|
|
|
|
VectorNormalize( direction );
|
|
|
|
//cgi_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgi_S_RegisterSound( va( "sound/world/ric%d.wav", (rand() & 2)+1) ) );
|
|
|
|
numSparks = 8 + (random() * 4.0f);
|
|
|
|
scale = 0.2f + (random() *0.4);
|
|
VectorMA( le->refEntity.origin, 24.0f + (crandom() * 4.0f), dir, end );
|
|
|
|
//One long spark
|
|
FX_AddLine( le->refEntity.origin,
|
|
end,
|
|
1.0f,
|
|
scale,
|
|
0.0f,
|
|
1.0f,
|
|
0.25f,
|
|
125.0f,
|
|
cgs.media.sparkShader );
|
|
|
|
for ( i = 0; i < numSparks; i++ )
|
|
{
|
|
scale = 0.2f + (random() *0.4);
|
|
|
|
for ( j = 0; j < 3; j ++ )
|
|
direction[j] = dir[j] + (0.25f * crandom());
|
|
|
|
VectorNormalize(direction);
|
|
|
|
VectorMA( le->refEntity.origin, 0.0f + ( random() * 2.0f ), direction, start );
|
|
VectorMA( start, 2.0f + ( random() * 16.0f ), direction, end );
|
|
|
|
FX_AddLine( start,
|
|
end,
|
|
1.0f,
|
|
scale,
|
|
0.0f,
|
|
1.0f,
|
|
0.25f,
|
|
125.0f,
|
|
cgs.media.sparkShader );
|
|
}
|
|
|
|
if ( rand() & 1 )
|
|
{
|
|
numSparks = 1 + (random() * 2.0f);
|
|
for ( i = 0; i < numSparks; i++ )
|
|
{
|
|
scale = 0.5f + (random() * 0.5f);
|
|
|
|
VectorScale( direction, 250, velocity );
|
|
|
|
FX_AddTrail( start,
|
|
velocity,
|
|
qtrue,
|
|
8.0f,
|
|
-32.0f,
|
|
scale,
|
|
-scale,
|
|
1.0f,
|
|
0.5f,
|
|
0.25f,
|
|
700.0f,
|
|
cgs.media.sparkShader);
|
|
|
|
}
|
|
}
|
|
|
|
VectorMA( le->refEntity.origin, 1, dir, direction );
|
|
|
|
scale = 6.0f + (random() * 8.0f);
|
|
alpha = 0.1 + (random() * 0.4f);
|
|
|
|
VectorSet( velocity, 0, 0, 8 );
|
|
|
|
FX_AddSprite( direction,
|
|
velocity,
|
|
qfalse,
|
|
scale,
|
|
scale,
|
|
alpha,
|
|
0.0f,
|
|
random()*45.0f,
|
|
0.0f,
|
|
1000.0f,
|
|
cgs.media.steamShader );
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
/*
|
|
======================
|
|
CG_Spark
|
|
|
|
Creates a spark effect
|
|
======================
|
|
*/
|
|
|
|
void CG_Spark( vec3_t origin, vec3_t normal, int delay)
|
|
{
|
|
// give it a lifetime of 10 seconds because the refresh thinktime in g_fx.c is 10 seconds
|
|
FX_AddSpawner( origin, normal, NULL, NULL, qfalse, delay, 1.5, 10000, SparkThink, 100 );
|
|
|
|
}
|
|
|
|
|
|
|
|
qboolean SteamThink( localEntity_t *le )
|
|
{
|
|
float speed = 200;
|
|
vec3_t direction;
|
|
vec3_t velocity = { 0, 0, 128 };
|
|
float scale, dscale;
|
|
|
|
//FIXME: Whole lotta randoms...
|
|
|
|
VectorCopy( le->data.spawner.dir, direction );
|
|
AngleVectors( direction, direction, NULL, NULL );
|
|
VectorNormalize(direction);
|
|
|
|
direction[0] += (direction[0] * crandom() * le->data.spawner.variance);
|
|
direction[1] += (direction[0] * crandom() * le->data.spawner.variance);
|
|
direction[2] += (direction[0] * crandom() * le->data.spawner.variance);
|
|
|
|
|
|
VectorScale( direction, speed, velocity );
|
|
|
|
scale = 4.0f + (random());
|
|
dscale = scale * 4.0;
|
|
|
|
FX_AddSprite( le->refEntity.origin,
|
|
velocity,
|
|
qfalse,
|
|
scale,
|
|
dscale,
|
|
1.0f,
|
|
0.0f,
|
|
random() * 360,
|
|
0.25f,
|
|
300,//(len / speed) * 1000,
|
|
cgs.media.steamShader );
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
/*
|
|
======================
|
|
CG_Steam
|
|
|
|
Creates a steam effect
|
|
======================
|
|
*/
|
|
|
|
void CG_Steam( vec3_t position, vec3_t dir )
|
|
{
|
|
// give it a lifetime of 10 seconds because the refresh thinktime in g_fx.c is 10 seconds
|
|
FX_AddSpawner( position, dir, NULL, NULL, qfalse, 0, 0.15, 10000, SteamThink, 100 );
|
|
}
|
|
|
|
/*
|
|
======================
|
|
CG_Bolt
|
|
|
|
Creates a electricity bolt effect
|
|
======================
|
|
*/
|
|
#define DATA_EFFECTS 0
|
|
#define DATA_CHAOS 1
|
|
#define DATA_RADIUS 2
|
|
|
|
//-----------------------------
|
|
void BoltSparkSpew( vec3_t origin, vec3_t normal )
|
|
{
|
|
float scale = 1.0f + ( random() * 1.0f );
|
|
int num = 0, i = 0;
|
|
vec3_t vel;
|
|
|
|
trap_R_AddLightToScene( origin, 75 + (rand()&31), 1.0, 0.8, 1.0 );
|
|
|
|
// Drop some sparks
|
|
num = (int)(random() * 2) + 2;
|
|
|
|
for ( i = 0; i < num; i++ )
|
|
{
|
|
scale = 0.6f + random();
|
|
if ( rand() & 1 )
|
|
FXE_Spray( normal, 70, 80, 0.9f, vel);
|
|
else
|
|
FXE_Spray( normal, 80, 200, 0.5f, vel);
|
|
|
|
FX_AddTrail( origin, vel, qfalse, 8.0f + random() * 8, -48.0f,
|
|
scale, -scale, 1.0f, 0.8f, 0.4f, 600.0f, cgs.media.spark2Shader );
|
|
}
|
|
}
|
|
|
|
|
|
qboolean BoltFireback( localEntity_t *le)
|
|
{
|
|
//localEntity_t *FX_AddElectricity( vec3_t origin, vec3_t origin2, float stScale, float scale, float dscale,
|
|
// float startalpha, float endalpha, float killTime, qhandle_t shader, float deviation );
|
|
float killTime = (0 == le->data.spawner.delay)?10000:200;
|
|
|
|
FX_AddElectricity(le->refEntity.origin, le->data.spawner.dir, 0.2f, 15.0, -15.0, 1.0, 0.5, killTime,
|
|
cgs.media.bolt2Shader, le->data.spawner.variance );
|
|
// is this spawner on a random delay?
|
|
if (le->data.spawner.data1)
|
|
{
|
|
le->data.spawner.delay = flrandom(0,5000);
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
//-----------------------------
|
|
qboolean BorgBoltFireback( localEntity_t *le)
|
|
{
|
|
float killTime = (0 == le->data.spawner.delay)?10000:200;
|
|
|
|
FX_AddElectricity(le->refEntity.origin, le->data.spawner.dir, 0.2f, 15.0, -5.0, 1.0, 0.5, killTime,
|
|
cgs.media.borgLightningShaders[1], le->data.spawner.variance );
|
|
// is this spawner on a random delay?
|
|
if (le->data.spawner.data1)
|
|
{
|
|
le->data.spawner.delay = flrandom(0,5000);
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
//-----------------------------
|
|
qboolean BoltFirebackSparks( localEntity_t *le)
|
|
{
|
|
vec3_t dir;
|
|
float killTime = (0 == le->data.spawner.delay)?10000:200;
|
|
|
|
VectorSubtract(le->refEntity.origin, le->data.spawner.dir, dir);
|
|
VectorNormalize(dir);
|
|
FX_AddElectricity(le->refEntity.origin, le->data.spawner.dir, 0.2f, 15.0, -15.0, 1.0, 0.5, killTime,
|
|
cgs.media.bolt2Shader, le->data.spawner.variance );
|
|
BoltSparkSpew(le->data.spawner.dir, dir);
|
|
// is this spawner on a random delay?
|
|
if (le->data.spawner.data1)
|
|
{
|
|
le->data.spawner.delay = flrandom(0,5000);
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
//-----------------------------
|
|
qboolean BorgBoltFirebackSparks( localEntity_t *le)
|
|
{
|
|
vec3_t dir;
|
|
float killTime = (0 == le->data.spawner.delay)?10000:200;
|
|
|
|
VectorSubtract(le->refEntity.origin, le->data.spawner.dir, dir);
|
|
VectorNormalize(dir);
|
|
FX_AddElectricity(le->refEntity.origin, le->data.spawner.dir, 0.2f, 15.0, -15.0, 1.0, 0.5, killTime,
|
|
cgs.media.borgLightningShaders[0], le->data.spawner.variance );
|
|
BoltSparkSpew(le->data.spawner.dir, dir);
|
|
// is this spawner on a random delay?
|
|
if (le->data.spawner.data1)
|
|
{
|
|
le->data.spawner.delay = flrandom(0,5000);
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
//-----------------------------
|
|
void CG_Bolt( centity_t *cent )
|
|
{
|
|
localEntity_t *le = NULL;
|
|
qboolean bSparks = cent->currentState.eventParm & BOLT_SPARKS;
|
|
qboolean bBorg = cent->currentState.eventParm & BOLT_BORG;
|
|
float radius = cent->currentState.angles2[0], chaos = cent->currentState.angles2[1];
|
|
float delay = cent->currentState.time2 * 1000; // the value given by the designer is in seconds
|
|
qboolean bRandom = qfalse;
|
|
|
|
if (delay < 0)
|
|
{
|
|
// random
|
|
delay = flrandom(0.1, 5000.0);
|
|
bRandom = qtrue;
|
|
}
|
|
if (delay > 10000)
|
|
{
|
|
delay = 10000;
|
|
}
|
|
|
|
if ( bBorg )
|
|
{
|
|
if (bSparks)
|
|
{
|
|
le = FX_AddSpawner( cent->lerpOrigin, cent->currentState.origin2, NULL, NULL, qfalse, delay,
|
|
chaos, 10000, BorgBoltFirebackSparks, radius );
|
|
}
|
|
else
|
|
{
|
|
le = FX_AddSpawner( cent->lerpOrigin, cent->currentState.origin2, NULL, NULL, qfalse, delay,
|
|
chaos, 10000, BorgBoltFireback, radius );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bSparks)
|
|
{
|
|
le = FX_AddSpawner( cent->lerpOrigin, cent->currentState.origin2, NULL, NULL, qfalse, delay,
|
|
chaos, 10000, BoltFirebackSparks, radius );
|
|
}
|
|
else
|
|
{
|
|
le = FX_AddSpawner( cent->lerpOrigin, cent->currentState.origin2, NULL, NULL, qfalse, delay,
|
|
chaos, 10000, BoltFireback, radius );
|
|
}
|
|
}
|
|
if (bRandom)
|
|
{
|
|
le->data.spawner.data1 = 1;
|
|
}
|
|
}
|
|
|
|
void CG_TransporterPad(vec3_t origin)
|
|
{
|
|
FX_TransporterPad(origin);
|
|
}
|
|
|
|
/*
|
|
===========================
|
|
Drip
|
|
|
|
Create timed drip effect
|
|
===========================
|
|
*/
|
|
|
|
qboolean DripCallback( localEntity_t *le )
|
|
{
|
|
localEntity_t *trail = NULL;
|
|
qhandle_t shader = 0;
|
|
|
|
switch (le->data.spawner.data1)
|
|
{
|
|
case 1:
|
|
shader = cgs.media.oilDropShader;
|
|
break;
|
|
case 2:
|
|
shader = cgs.media.greenDropShader;
|
|
break;
|
|
case 0:
|
|
default:
|
|
shader = cgs.media.waterDropShader;
|
|
break;
|
|
}
|
|
trail = FX_AddTrail(le->refEntity.origin, le->pos.trDelta, qfalse, 4, -2, 1, 0, 0.8, 0.4, 0.0,
|
|
300, shader);
|
|
trail->leFlags |= LEF_ONE_FRAME;
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
qboolean DripSplash( localEntity_t *le )
|
|
{
|
|
float scale = 1.0f + ( random() * 1.0f );
|
|
int num = 0, i = 0;
|
|
vec3_t vel, normal, origin;
|
|
qhandle_t shader = 0;
|
|
|
|
switch (le->data.spawner.data1)
|
|
{
|
|
case 1:
|
|
shader = cgs.media.oilDropShader;
|
|
break;
|
|
case 2:
|
|
shader = cgs.media.greenDropShader;
|
|
break;
|
|
case 0:
|
|
default:
|
|
shader = cgs.media.waterDropShader;
|
|
break;
|
|
}
|
|
|
|
VectorCopy(le->data.spawner.dir, normal);
|
|
VectorCopy(le->refEntity.origin, origin);
|
|
|
|
// splashing water droplets. which, I'm fairly certain, is an alternative band from Europe.
|
|
num = (int)(random() * 2) + 6;
|
|
|
|
for ( i = 0; i < num; i++ )
|
|
{
|
|
scale = 0.6f + random();
|
|
if ( rand() & 1 )
|
|
FXE_Spray( normal, 110, 80, 0.9f, vel);
|
|
else
|
|
FXE_Spray( normal, 150, 150, 0.5f, vel);
|
|
|
|
FX_AddTrail( origin, vel, qtrue, 4.0f, 0.0f,
|
|
scale, -scale, 1.0f, 0.8f, 0.4f, 200.0f, shader );
|
|
}
|
|
|
|
switch( rand() & 3 )
|
|
{
|
|
case 1:
|
|
trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_BODY, cgs.media.waterDropSound1 );
|
|
break;
|
|
case 2:
|
|
trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_BODY, cgs.media.waterDropSound2 );
|
|
break;
|
|
default:
|
|
trap_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_BODY, cgs.media.waterDropSound3 );
|
|
break;
|
|
}
|
|
return qtrue;
|
|
|
|
}
|
|
|
|
qboolean JackTheDripper( localEntity_t *le )
|
|
{
|
|
trace_t trace;
|
|
vec3_t vel, down = {0,0,-1}, end, origin, new_origin;
|
|
float time, dis, diameter = 1.0;
|
|
qhandle_t shader = 0;
|
|
int maxDripsPerLifetime = 200; // given a 10 second lifetime
|
|
int desiredDrips = 1 + (int)(le->data.spawner.variance * (maxDripsPerLifetime-1)); // range of (1...max)
|
|
float percentLife = 1.0f - (le->endTime - cg.time)*le->lifeRate;
|
|
localEntity_t *splash = NULL;
|
|
|
|
switch (le->data.spawner.data1)
|
|
{
|
|
case 1:
|
|
shader = cgs.media.oilDropShader;
|
|
break;
|
|
case 2:
|
|
shader = cgs.media.greenDropShader;
|
|
break;
|
|
case 0:
|
|
default:
|
|
shader = cgs.media.waterDropShader;
|
|
break;
|
|
}
|
|
|
|
// do we need to add a drip to maintain our drips-per-second rate?
|
|
while ( (int)(flrandom(percentLife-0.05,percentLife+0.05)*desiredDrips) > le->data.spawner.data2)
|
|
{
|
|
VectorCopy(le->refEntity.origin, origin);
|
|
|
|
// the more drips per second, spread them out from our origin point
|
|
fxRandCircumferencePos(origin, down, 10*le->data.spawner.variance, new_origin);
|
|
|
|
// Ideally, zero should be used for vel...so just use something sufficiently close
|
|
VectorSet( vel, 0, 0, -0.00001 );
|
|
|
|
// Find out where it will hit
|
|
VectorMA( new_origin, 1024, down, end );
|
|
CG_Trace( &trace, new_origin, NULL, NULL, end, 0, MASK_SHOT );
|
|
if ( trace.fraction < 1.0 )
|
|
{
|
|
VectorSubtract( trace.endpos, new_origin, end );
|
|
dis = VectorNormalize( end );
|
|
|
|
time = sqrt( 2*dis / DEFAULT_GRAVITY ) * 1000; // Calculate how long the thing will take to travel that distance
|
|
|
|
// Falling drop
|
|
splash = FX_AddParticle( new_origin, vel, qtrue, diameter, 0.0, 0.8, 0.8, 0.0, 0.0, time, shader, DripCallback );
|
|
splash->data.spawner.data1 = le->data.spawner.data1;
|
|
|
|
splash = FX_AddSpawner(trace.endpos, trace.plane.normal, vel, NULL, qfalse, time, 0, time + 200, DripSplash, 10);
|
|
splash->data.spawner.data1 = le->data.spawner.data1;
|
|
}
|
|
else
|
|
// Falling a long way so just send one that will fall for 2 secs, but don't spawn a splash
|
|
{
|
|
FX_AddParticle( new_origin, vel, qtrue, diameter, 0.0, 0.8, 0.8, 0.0, 0.0, 2000, shader, 0/*NULL*/ );
|
|
}
|
|
//increase our number-of-drips counter
|
|
le->data.spawner.data2++;
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void CG_Drip(centity_t *cent)
|
|
{
|
|
vec3_t down = {0,0,-1};
|
|
localEntity_t *le = NULL;
|
|
|
|
// clamp variance to [0...1]
|
|
if (cent->currentState.angles2[0] < 0)
|
|
{
|
|
cent->currentState.angles2[0] = 0;
|
|
}
|
|
else if (cent->currentState.angles2[0] > 1)
|
|
{
|
|
cent->currentState.angles2[0] = 1;
|
|
}
|
|
// cent->currentState.angles2[0] is the degree of drippiness
|
|
// cent->currentState.time2 is the type of drip (water, oil, etc.)
|
|
le = FX_AddSpawner( cent->lerpOrigin, down, NULL, NULL, qfalse, 0,
|
|
cent->currentState.angles2[0], 10000, JackTheDripper, cent->currentState.time2 );
|
|
//init our number-of-drips counter
|
|
le->data.spawner.data2 = 0;
|
|
}
|
|
|