123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236 |
- // leave this line at the top for all g_xxxx.cpp files...
- #include "g_headers.h"
- #include "g_local.h"
- #include "g_functions.h"
- extern int G_FindConfigstringIndex( const char *name, int start, int max, qboolean create );
- #define FX_ENT_RADIUS 32
- extern int BMS_START;
- extern int BMS_MID;
- extern int BMS_END;
- //----------------------------------------------------------
- /*QUAKED fx_runner (0 0 1) (-8 -8 -8) (8 8 8) STARTOFF ONESHOT DAMAGE
- Runs the specified effect, can also be targeted at an info_notnull to orient the effect
- STARTOFF - effect starts off, toggles on/off when used
- ONESHOT - effect fires only when used
- DAMAGE - does radius damage around effect every "delay" milliseonds
- "fxFile" - name of the effect file to play
- "target" - direction to aim the effect in, otherwise defaults to up
- "target2" - uses its target2 when the fx gets triggered
- "delay" - how often to call the effect, don't over-do this ( default 200 )
- "random" - random amount of time to add to delay, ( default 0, 200 = 0ms to 200ms )
- "splashRadius" - only works when damage is checked ( default 16 )
- "splashDamage" - only works when damage is checked ( default 5 )
- "soundset" - bmodel set to use, plays start sound when toggled on, loop sound while on ( doesn't play on a oneshot), and a stop sound when turned off
- */
- #define FX_RUNNER_RESERVED 0x800000
- //----------------------------------------------------------
- void fx_runner_think( gentity_t *ent )
- {
- vec3_t temp;
- EvaluateTrajectory( &ent->s.pos, level.time, ent->currentOrigin );
- EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles );
- // call the effect with the desired position and orientation
- G_AddEvent( ent, EV_PLAY_EFFECT, ent->fxID );
- // Assume angles, we'll do a cross product on the other end to finish up
- AngleVectors( ent->currentAngles, ent->pos3, NULL, NULL );
- MakeNormalVectors( ent->pos3, ent->pos4, temp ); // there IS a reason this is done...it's so that it doesn't break every effect in the game...
- ent->nextthink = level.time + ent->delay + random() * ent->random;
- if ( ent->spawnflags & 4 ) // damage
- {
- G_RadiusDamage( ent->currentOrigin, ent, ent->splashDamage, ent->splashRadius, ent, MOD_UNKNOWN );
- }
- if ( ent->target2 )
- {
- // let our target know that we have spawned an effect
- G_UseTargets2( ent, ent, ent->target2 );
- }
- if ( !(ent->spawnflags & 2 ) && !ent->s.loopSound ) // NOT ONESHOT...this is an assy thing to do
- {
- if ( VALIDSTRING( ent->soundSet ) == true )
- {
- ent->s.loopSound = CAS_GetBModelSound( ent->soundSet, BMS_MID );
- if ( ent->s.loopSound < 0 )
- {
- ent->s.loopSound = 0;
- }
- }
- }
- }
- //----------------------------------------------------------
- void fx_runner_use( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- if (self->s.isPortalEnt)
- { //rww - mark it as broadcast upon first use if it's within the area of a skyportal
- self->svFlags |= SVF_BROADCAST;
- }
- if ( self->spawnflags & 2 ) // ONESHOT
- {
- // call the effect with the desired position and orientation, as a safety thing,
- // make sure we aren't thinking at all.
- fx_runner_think( self );
- self->nextthink = -1;
- if ( self->target2 )
- {
- // let our target know that we have spawned an effect
- G_UseTargets2( self, self, self->target2 );
- }
- if ( VALIDSTRING( self->soundSet ) == true )
- {
- G_AddEvent( self, EV_BMODEL_SOUND, CAS_GetBModelSound( self->soundSet, BMS_START ));
- }
- }
- else
- {
- // ensure we are working with the right think function
- self->e_ThinkFunc = thinkF_fx_runner_think;
- // toggle our state
- if ( self->nextthink == -1 )
- {
- // NOTE: we fire the effect immediately on use, the fx_runner_think func will set
- // up the nextthink time.
- fx_runner_think( self );
- if ( VALIDSTRING( self->soundSet ) == true )
- {
- G_AddEvent( self, EV_BMODEL_SOUND, CAS_GetBModelSound( self->soundSet, BMS_START ));
- self->s.loopSound = CAS_GetBModelSound( self->soundSet, BMS_MID );
- if ( self->s.loopSound < 0 )
- {
- self->s.loopSound = 0;
- }
- }
- }
- else
- {
- // turn off for now
- self->nextthink = -1;
- if ( VALIDSTRING( self->soundSet ) == true )
- {
- G_AddEvent( self, EV_BMODEL_SOUND, CAS_GetBModelSound( self->soundSet, BMS_END ));
- self->s.loopSound = 0;
- }
- }
- }
- }
- //----------------------------------------------------------
- void fx_runner_link( gentity_t *ent )
- {
- vec3_t dir;
- if ( ent->target )
- {
- // try to use the target to override the orientation
- gentity_t *target = NULL;
- target = G_Find( target, FOFS(targetname), ent->target );
- if ( !target )
- {
- // Bah, no good, dump a warning, but continue on and use the UP vector
- Com_Printf( "fx_runner_link: target specified but not found: %s\n", ent->target );
- Com_Printf( " -assuming UP orientation.\n" );
- }
- else
- {
- // Our target is valid so let's override the default UP vector
- VectorSubtract( target->s.origin, ent->s.origin, dir );
- VectorNormalize( dir );
- vectoangles( dir, ent->s.angles );
- }
- }
- // don't really do anything with this right now other than do a check to warn the designers if the target2 is bogus
- if ( ent->target2 )
- {
- gentity_t *target = NULL;
- target = G_Find( target, FOFS(targetname), ent->target2 );
- if ( !target )
- {
- // Target2 is bogus, but we can still continue
- Com_Printf( "fx_runner_link: target2 was specified but is not valid: %s\n", ent->target2 );
- }
- }
- G_SetAngles( ent, ent->s.angles );
- if ( ent->spawnflags & 1 || ent->spawnflags & 2 ) // STARTOFF || ONESHOT
- {
- // We won't even consider thinking until we are used
- ent->nextthink = -1;
- }
- else
- {
- if ( VALIDSTRING( ent->soundSet ) == true )
- {
- ent->s.loopSound = CAS_GetBModelSound( ent->soundSet, BMS_MID );
- if ( ent->s.loopSound < 0 )
- {
- ent->s.loopSound = 0;
- }
- }
- // Let's get to work right now!
- ent->e_ThinkFunc = thinkF_fx_runner_think;
- ent->nextthink = level.time + 200; // wait a small bit, then start working
- }
- // make us useable if we can be targeted
- if ( ent->targetname )
- {
- ent->e_UseFunc = useF_fx_runner_use;
- }
- }
- //----------------------------------------------------------
- void SP_fx_runner( gentity_t *ent )
- {
- // Get our defaults
- G_SpawnInt( "delay", "200", &ent->delay );
- G_SpawnFloat( "random", "0", &ent->random );
- G_SpawnInt( "splashRadius", "16", &ent->splashRadius );
- G_SpawnInt( "splashDamage", "5", &ent->splashDamage );
- if ( !G_SpawnAngleHack( "angle", "0", ent->s.angles ))
- {
- // didn't have angles, so give us the default of up
- VectorSet( ent->s.angles, -90, 0, 0 );
- }
- if ( !ent->fxFile )
- {
- gi.Printf( S_COLOR_RED"ERROR: fx_runner %s at %s has no fxFile specified\n", ent->targetname, vtos(ent->s.origin) );
- G_FreeEntity( ent );
- return;
- }
- // Try and associate an effect file, unfortunately we won't know if this worked or not
- // until the CGAME trys to register it...
- ent->fxID = G_EffectIndex( ent->fxFile );
- ent->s.eType = ET_MOVER;
- // Give us a bit of time to spawn in the other entities, since we may have to target one of 'em
- ent->e_ThinkFunc = thinkF_fx_runner_link;
- ent->nextthink = level.time + 400;
- // Save our position and link us up!
- G_SetOrigin( ent, ent->s.origin );
- VectorSet( ent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
- VectorScale( ent->maxs, -1, ent->mins );
- gi.linkentity( ent );
- }
- /*QUAKED fx_snow (1 0 0) (-16 -16 -16) (16 16 16) LIGHT MEDIUM HEAVY MISTY_FOG
- This world effect will spawn snow globally into the level.
- */
- void SP_CreateSnow( gentity_t *ent )
- {
- cvar_t *r_weatherScale = gi.cvar( "r_weatherScale", "1", CVAR_ARCHIVE );
- if ( r_weatherScale->value == 0.0f )
- {
- return;
- }
- // Different Types Of Rain
- //-------------------------
- if (ent->spawnflags & 1)
- {
- G_FindConfigstringIndex("lightsnow", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- else if (ent->spawnflags & 2)
- {
- G_FindConfigstringIndex("snow", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- else if (ent->spawnflags & 4)
- {
- G_FindConfigstringIndex("heavysnow", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- else
- {
- G_FindConfigstringIndex("snow", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- G_FindConfigstringIndex("fog", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- // MISTY FOG
- //===========
- if (ent->spawnflags & 8)
- {
- G_FindConfigstringIndex("fog", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- }
- /*QUAKED fx_wind (0 .5 .8) (-16 -16 -16) (16 16 16) NORMAL CONSTANT GUSTING SWIRLING x FOG LIGHT_FOG
- Generates global wind forces
- NORMAL creates a random light global wind
- CONSTANT forces all wind to go in a specified direction
- GUSTING causes random gusts of wind
- SWIRLING causes random swirls of wind
- "angles" the direction for constant wind
- "speed" the speed for constant wind
- */
- void SP_CreateWind( gentity_t *ent )
- {
- cvar_t *r_weatherScale = gi.cvar( "r_weatherScale", "1", CVAR_ARCHIVE );
- if ( r_weatherScale->value <= 0.0f )
- {
- return;
- }
- char temp[256];
- // Normal Wind
- //-------------
- if (ent->spawnflags & 1)
- {
- G_FindConfigstringIndex("wind", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- // Constant Wind
- //---------------
- if (ent->spawnflags & 2)
- {
- vec3_t windDir;
- AngleVectors(ent->s.angles, windDir, 0, 0);
- G_SpawnFloat( "speed", "500", &ent->speed );
- VectorScale(windDir, ent->speed, windDir);
- sprintf( temp, "constantwind ( %f %f %f )", windDir[0], windDir[1], windDir[2] );
- G_FindConfigstringIndex(temp, CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- // Gusting Wind
- //--------------
- if (ent->spawnflags & 4)
- {
- G_FindConfigstringIndex("gustingwind", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- // Swirling Wind
- //---------------
- if (ent->spawnflags & 8)
- {
- G_FindConfigstringIndex("swirlingwind", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- // MISTY FOG
- //===========
- if (ent->spawnflags & 32)
- {
- G_FindConfigstringIndex("fog", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- // MISTY FOG
- //===========
- if (ent->spawnflags & 64)
- {
- G_FindConfigstringIndex("light_fog", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- }
- /*QUAKED fx_wind_zone (0 .5 .8) ? Creates a constant wind in a local area
- Generates local wind forces
- "angles" the direction for constant wind
- "speed" the speed for constant wind
- */
- void SP_CreateWindZone( gentity_t *ent )
- {
- cvar_t *r_weatherScale = gi.cvar( "r_weatherScale", "1", CVAR_ARCHIVE );
- if ( r_weatherScale->value <= 0.0f )
- {
- return;
- }
- gi.SetBrushModel(ent, ent->model);
- vec3_t windDir;
- AngleVectors(ent->s.angles, windDir, 0, 0);
- G_SpawnFloat( "speed", "500", &ent->speed );
- VectorScale(windDir, ent->speed, windDir);
- char temp[256];
- sprintf( temp, "windzone ( %f %f %f ) ( %f %f %f ) ( %f %f %f )",
- ent->mins[0], ent->mins[1], ent->mins[2],
- ent->maxs[0], ent->maxs[1], ent->maxs[2],
- windDir[0], windDir[1], windDir[2]
- );
- G_FindConfigstringIndex(temp, CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- /*QUAKED fx_rain (1 0 0) (-16 -16 -16) (16 16 16) LIGHT MEDIUM HEAVY ACID OUTSIDE_SHAKE MISTY_FOG
- This world effect will spawn rain globally into the level.
- LIGHT create light drizzle
- MEDIUM create average medium rain
- HEAVY create heavy downpour (with fog and lightning automatically)
- ACID create acid rain
- OUTSIDE_SHAKE will cause the camera to shake slightly whenever outside
- MISTY_FOG causes clouds of misty fog to float through the level
- LIGHTNING causes random bursts of lightning and thunder in the level
- The following fields are for lightning:
- "flashcolor" "200 200 200" (r g b) (values 0.0-255.0)
- "flashdelay" "12000" maximum time delay between lightning strikes
- "chanceflicker" "2" 1 in 2 chance of flickering fog
- "chancesound" "3" 1 in 3 chance of playing a sound
- "chanceeffect" "4" 1 in 4 chance of playing the effect
- */
- //----------------------------------------------------------
- //----------------------------------------------------------
- extern void G_SoundAtSpot( vec3_t org, int soundIndex, qboolean broadcast );
- void fx_rain_think( gentity_t *ent )
- {
- if (player)
- {
- if (ent->count!=0)
- {
- ent->count--;
- if (ent->count==0 || (ent->count%2)==0)
- {
- gi.WE_SetTempGlobalFogColor(ent->pos2); // Turn Off
- if (ent->count==0)
- {
- ent->nextthink = level.time + Q_irand(1000, 12000);
- }
- else if (ent->count==2)
- {
- ent->nextthink = level.time + Q_irand(150, 450);
- }
- else
- {
- ent->nextthink = level.time + Q_irand(50, 150);
- }
- }
- else
- {
- gi.WE_SetTempGlobalFogColor(ent->pos3); // Turn On
- ent->nextthink = level.time + 50;
- }
- }
- else if (gi.WE_IsOutside(player->currentOrigin))
- {
- vec3_t effectPos;
- vec3_t effectDir;
- VectorClear(effectDir);
- effectDir[0] += Q_flrand(-1.0f, 1.0f);
- effectDir[1] += Q_flrand(-1.0f, 1.0f);
- bool PlayEffect = Q_irand(1,ent->aimDebounceTime)==1;
- bool PlayFlicker = Q_irand(1,ent->attackDebounceTime)==1;
- bool PlaySound = (PlayEffect || PlayFlicker || Q_irand(1,ent->pushDebounceTime)==1);
- // Play The Sound
- //----------------
- if (PlaySound && !PlayEffect)
- {
- VectorMA(player->currentOrigin, 250.0f, effectDir, effectPos);
- G_SoundAtSpot(effectPos, G_SoundIndex(va("sound/ambience/thunder%d", Q_irand(1,4))), qtrue);
- }
- // Play The Effect
- //-----------------
- if (PlayEffect)
- {
- VectorMA(player->currentOrigin, 400.0f, effectDir, effectPos);
- if (PlaySound)
- {
- G_Sound(player, G_SoundIndex(va("sound/ambience/thunder_close%d", Q_irand(1,2))));
- }
- // Raise It Up Into The Sky
- //--------------------------
- effectPos[2] += Q_flrand(600.0f, 1000.0f);
- VectorClear(effectDir);
- effectDir[2] = -1.0f;
- G_PlayEffect("env/huge_lightning", effectPos, effectDir);
- ent->nextthink = level.time + Q_irand(100, 200);
- }
- // Change The Fog Color
- //----------------------
- if (PlayFlicker)
- {
- ent->count = (Q_irand(1,4) * 2);
- ent->nextthink = level.time + 50;
- gi.WE_SetTempGlobalFogColor(ent->pos3);
- }
- else
- {
- ent->nextthink = level.time + Q_irand(1000, ent->delay);
- }
- }
- else
- {
- ent->nextthink = level.time + Q_irand(1000, ent->delay);
- }
- }
- else
- {
- ent->nextthink = level.time + Q_irand(1000, ent->delay);
- }
- }
- void SP_CreateRain( gentity_t *ent )
- {
- // Different Types Of Rain
- //-------------------------
- if (ent->spawnflags & 1)
- {
- G_FindConfigstringIndex("lightrain", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- else if (ent->spawnflags & 2)
- {
- G_FindConfigstringIndex("rain", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- else if (ent->spawnflags & 4)
- {
- G_FindConfigstringIndex("heavyrain", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- // Automatically Get Heavy Fog
- //-----------------------------
- G_FindConfigstringIndex("heavyrainfog", CS_WORLD_FX, MAX_WORLD_FX, qtrue );
- // Automatically Get Lightning & Thunder
- //---------------------------------------
- ent->spawnflags |= 64;
- }
- else if (ent->spawnflags & 8)
- {
- G_EffectIndex( "world/acid_fizz" );
- G_FindConfigstringIndex("acidrain", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- // OUTSIDE SHAKE
- //===============
- if (ent->spawnflags & 16)
- {
- G_FindConfigstringIndex("outsideShake", CS_WORLD_FX, MAX_WORLD_FX, qtrue);
- }
- // MISTY FOG
- //===========
- if (ent->spawnflags & 32)
- {
- G_FindConfigstringIndex("fog", CS_WORLD_FX, MAX_WORLD_FX, qtrue );
- }
- // LIGHTNING
- //===========
- if (ent->spawnflags & 64)
- {
- G_SoundIndex("sound/ambience/thunder1");
- G_SoundIndex("sound/ambience/thunder2");
- G_SoundIndex("sound/ambience/thunder3");
- G_SoundIndex("sound/ambience/thunder4");
- G_SoundIndex("sound/ambience/thunder_close1");
- G_SoundIndex("sound/ambience/thunder_close2");
- G_EffectIndex( "env/huge_lightning" );
- ent->e_ThinkFunc = thinkF_fx_rain_think;
- ent->nextthink = level.time + Q_irand(4000, 8000);
-
- if (!G_SpawnVector( "flashcolor", "200 200 200", ent->pos3))
- {
- VectorSet(ent->pos3, 200, 200, 200);
- }
- VectorClear(ent->pos2); // the "off" color
- G_SpawnInt("flashdelay", "12000", &ent->delay);
- G_SpawnInt("chanceflicker", "2", &ent->attackDebounceTime);
- G_SpawnInt("chancesound", "3", &ent->pushDebounceTime);
- G_SpawnInt("chanceeffect", "4", &ent->aimDebounceTime);
- }
- }
- // Added by Aurelio Reis on 10/20/02.
- /*QUAKED fx_puff (1 0 0) (-16 -16 -16) (16 16 16)
- This world effect will spawn a puff system globally into the level.
- Enter any valid puff command as a key and value to setup the puff
- system properties.
- "count" The number of puffs/particles (default of 1000).
- "whichsystem" Which puff system to use (currently 0 and 1. Default 0).
- // Apply a default puff system.
- default <value>
- Current defaults are "snowstorm", "sandstorm", "foggy", and "smokey"
- // Set the color of the particles (0-1.0).
- color ( <red>, <green>, <blue> )
- default ( 0.5, 0.5, 0.5 )
- // Set the alpha (transparency) value for the particles (0-1.0).
- alpha <value>
- default 0.5
- // Set which texture to use for the particles (make sure to include full path).
- texture <textures/texture.tga>
- default gfx/effects/alpha_smoke2b.tga
- // Set the size of particles (from center, like a radius) (MIN 4, MAX 2048).
- size <value>
- default 100
- // Whether the saber should flicker and spark or not (0 false, 1 true).
- sabersparks <value>
- default 0
- // Set texture filtering mode (0 = Bilinear(default), 1 = Nearest(less quality).
- filtermode <value>
- default 0
- // Set the alpha blending mode (0 = src, src-1, 1 = one, one (additive)).
- blendmode <value>
- default 0
- // How much to rotate particles per second (in degree's).
- rotate ( <min>, <max> )
- default ( 0, 0 )
- // Set the area around the player the puffs cover:
- spread ( minX minY minZ ) ( maxX maxY maxZ )
- default: ( -600 -600 -500 ) ( 600 600 550 )
- // Set the random range that sets the speed the puffs fall:
- velocity ( minX minY minZ ) ( maxX maxY maxZ )
- default: ( -15 -15 -20 ) ( 15 15 -70 )
- // Set an area of puff blowing:
- wind ( windOriginX windOriginY windOriginZ ) ( windVelocityX windVelocityY windVelocityZ ) ( sizeX sizeY sizeZ )
- // Set puff blowing data:
- blowing duration <int>
- blowing low <int>
- default: 3
- blowing velocity ( min max )
- default: ( 30 70 )
- blowing size ( minX minY minZ )
- default: ( 1000 300 300 )
- */
- //----------------------------------------------------------
- void SP_CreatePuffSystem( gentity_t *ent )
- {
- char temp[128];
- // Initialize the puff system to either 1000 particles or whatever they choose.
- G_SpawnInt( "count", "1000", &ent->count );
- cvar_t *r_weatherScale = gi.cvar( "r_weatherScale", "1", CVAR_ARCHIVE );
- // See which puff system to use.
- int iPuffSystem = 0;
- int iVal = 0;
- if ( G_SpawnInt( "whichsystem", "0", &iVal ) )
- {
- iPuffSystem = iVal;
- if ( iPuffSystem < 0 || iPuffSystem > 1 )
- {
- iPuffSystem = 0;
- //ri.Error( ERR_DROP, "Weather Effect: Invalid value for whichsystem key" );
- Com_Printf( "Weather Effect: Invalid value for whichsystem key\n" );
- }
- }
- if ( r_weatherScale->value > 0.0f )
- {
- sprintf( temp, "puff%i init %i", iPuffSystem, (int)( ent->count * r_weatherScale->value ));
- G_FindConfigstringIndex( temp, CS_WORLD_FX, MAX_WORLD_FX, qtrue );
- // Should return here??? It didn't originally...
- }
- // See whether we should have the saber spark from the puff system.
- iVal = 0;
- G_SpawnInt( "sabersparks", "0", &iVal );
- if ( iVal == 1 )
- level.worldFlags |= WF_PUFFING;
- else
- level.worldFlags &= ~WF_PUFFING;
- // Go through all the fields and assign the values to the created puff system now.
- for ( int i = 0; i < 20; i++ )
- {
- char *key = NULL;
- char *value = NULL;
- // Fetch a field.
- if ( !G_SpawnField( i, &key, &value ) )
- continue;
- // Make sure we don't get key's that are worthless.
- if ( Q_stricmp( key, "origin" ) == 0 || Q_stricmp( key, "classname" ) == 0 ||
- Q_stricmp( key, "count" ) == 0 || Q_stricmp( key, "targetname" ) == 0 ||
- Q_stricmp( key, "sabersparks" ) == 0 || Q_stricmp( key, "whichsystem" ) == 0 )
- continue;
- // Send the command.
- _snprintf( temp, 128, "puff%i %s %s", iPuffSystem, key, value );
- G_FindConfigstringIndex( temp, CS_WORLD_FX, MAX_WORLD_FX, qtrue );
- }
- }
- // Don't use this! Too powerful! - Aurelio
- /*NOTEINUSE! fx_command (1 0 0) (-16 -16 -16) (16 16 16)
- //This effect allows you to issue console commands from within the world editor.
- //Use the variables c00 to c99 to issue a maximum of 100 console commands.
- //example: c00 r_showtris 1
- */
- //----------------------------------------------------------
- /*void SP_Command( gentity_t *ent )
- {
- char *strCommand;
- // Go through all the commands.
- for ( int i = 0; i < 100; i++ )
- {
- strCommand = NULL;
- // Fetch a command.
- G_SpawnString( va("c%02d", i), NULL, &strCommand );
- // If it's valid, issue it.
- if ( strCommand && strCommand[0] )
- {
- gi.SendConsoleCommand( strCommand );
- }
- }
- }*/
- //-----------------
- // Explosion Trail
- //-----------------
- //----------------------------------------------------------
- void fx_explosion_trail_think( gentity_t *ent )
- {
- vec3_t origin;
- trace_t tr;
- if ( ent->spawnflags & 1 ) // gravity
- {
- ent->s.pos.trType = TR_GRAVITY;
- }
- else
- {
- ent->s.pos.trType = TR_LINEAR;
- }
- EvaluateTrajectory( &ent->s.pos, level.time, origin );
- gi.trace( &tr, ent->currentOrigin, vec3_origin, vec3_origin, origin,
- ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask, G2_RETURNONHIT, 10 );
- if ( tr.fraction < 1.0f )
- {
- // never explode or bounce on sky
- if ( !( tr.surfaceFlags & SURF_NOIMPACT ))
- {
- if ( ent->splashDamage && ent->splashRadius )
- {
- G_RadiusDamage( tr.endpos, ent, ent->splashDamage, ent->splashRadius, ent, MOD_EXPLOSIVE_SPLASH );
- }
- }
- if ( ent->cameraGroup )
- {
- // fxFile2....in other words, impact fx
- G_PlayEffect( ent->cameraGroup, tr.endpos, tr.plane.normal );
- }
- if ( VALIDSTRING( ent->soundSet ) == true )
- {
- G_AddEvent( ent, EV_BMODEL_SOUND, CAS_GetBModelSound( ent->soundSet, BMS_END ));
- }
- G_FreeEntity( ent );
- return;
- }
- G_RadiusDamage( origin, ent, ent->damage, ent->radius, ent, MOD_EXPLOSIVE_SPLASH );
- // call the effect with the desired position and orientation
- G_PlayEffect( ent->fxID, origin, ent->currentAngles );
- ent->nextthink = level.time + 50;
- gi.linkentity( ent );
- }
- //----------------------------------------------------------
- void fx_explosion_trail_use( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- gentity_t *missile = G_Spawn();
- // We aren't a missile in the truest sense, rather we just move through the world and spawn effects
- if ( missile )
- {
- missile->classname = "fx_exp_trail";
- missile->nextthink = level.time + 50;
- missile->e_ThinkFunc = thinkF_fx_explosion_trail_think;
- missile->s.eType = ET_MOVER;
- missile->owner = self;
- missile->s.modelindex = self->s.modelindex2;
- missile->s.pos.trTime = level.time;
- G_SetOrigin( missile, self->currentOrigin );
- if ( self->spawnflags & 1 ) // gravity
- {
- missile->s.pos.trType = TR_GRAVITY;
- }
- else
- {
- missile->s.pos.trType = TR_LINEAR;
- }
- missile->spawnflags = self->spawnflags;
- G_SetAngles( missile, self->currentAngles );
- VectorScale( self->currentAngles, self->speed, missile->s.pos.trDelta );
- missile->s.pos.trTime = level.time;
- missile->radius = self->radius;
- missile->damage = self->damage;
- missile->splashDamage = self->splashDamage;
- missile->splashRadius = self->splashRadius;
- missile->fxID = self->fxID;
- missile->cameraGroup = self->cameraGroup; //fxfile2
- missile->clipmask = MASK_SHOT;
- gi.linkentity( missile );
- if ( VALIDSTRING( self->soundSet ) == true )
- {
- G_AddEvent( self, EV_BMODEL_SOUND, CAS_GetBModelSound( self->soundSet, BMS_START ));
- missile->s.loopSound = CAS_GetBModelSound( self->soundSet, BMS_MID );
- missile->soundSet = G_NewString(self->soundSet);//get my own copy so i can free it when i die
- if ( missile->s.loopSound < 0 )
- {
- missile->s.loopSound = 0;
- }
- }
- }
- }
- //----------------------------------------------------------
- void fx_explosion_trail_link( gentity_t *ent )
- {
- vec3_t dir;
- gentity_t *target = NULL;
- // we ony activate when used
- ent->e_UseFunc = useF_fx_explosion_trail_use;
- if ( ent->target )
- {
- // try to use the target to override the orientation
- target = G_Find( target, FOFS(targetname), ent->target );
- if ( !target )
- {
- gi.Printf( S_COLOR_RED"ERROR: fx_explosion_trail %s could not find target %s\n", ent->targetname, ent->target );
- G_FreeEntity( ent );
- return;
- }
- // Our target is valid so lets use that
- VectorSubtract( target->s.origin, ent->s.origin, dir );
- VectorNormalize( dir );
- }
- else
- {
- // we are assuming that we have angles, but there are no checks to verify this
- AngleVectors( ent->s.angles, dir, NULL, NULL );
- }
- // NOTE: this really isn't an angle, but rather an orientation vector
- G_SetAngles( ent, dir );
- }
- /*QUAKED fx_explosion_trail (0 0 1) (-8 -8 -8) (8 8 8) GRAVITY
- Creates an explosion type trail using the specified effect file, damaging things as it moves through the environment
- Can also be used for something like a meteor, just add an impact effect ( fxFile2 ) and a splashDamage and splashRadius
- GRAVITY - object uses gravity instead of linear motion
- "fxFile" - name of the effect to play for the trail ( default "env/exp_trail_comp" )
- "fxFile2" - effect file to play on impact
- "model" - model to attach to the trail
- "target" - direction to aim the trail in, required unless you specify angles
- "targetname" - (required) trail effect spawns only when used.
- "speed" - velocity through the world, ( default 350 )
- "radius" - damage radius around trail as it travels through the world ( default 128 )
- "damage" - radius damage ( default 128 )
- "splashDamage" - damage when thing impacts ( default 0 )
- "splashRadius" - damage radius on impact ( default 0 )
- "soundset" - soundset to use, start sound plays when explosion trail starts, loop sound plays on explosion trail, end sound plays when it impacts
- */
- //----------------------------------------------------------
- void SP_fx_explosion_trail( gentity_t *ent )
- {
- // We have to be useable, otherwise we won't spawn in
- if ( !ent->targetname )
- {
- gi.Printf( S_COLOR_RED"ERROR: fx_explosion_trail at %s has no targetname specified\n", vtos( ent->s.origin ));
- G_FreeEntity( ent );
- return;
- }
- // Get our defaults
- G_SpawnString( "fxFile", "env/exp_trail_comp", &ent->fxFile );
- G_SpawnInt( "damage", "128", &ent->damage );
- G_SpawnFloat( "radius", "128", &ent->radius );
- G_SpawnFloat( "speed", "350", &ent->speed );
- // Try to associate an effect file, unfortunately we won't know if this worked or not until the CGAME trys to register it...
- ent->fxID = G_EffectIndex( ent->fxFile );
- if ( ent->cameraGroup )
- {
- G_EffectIndex( ent->cameraGroup );
- }
- if ( ent->model )
- {
- ent->s.modelindex2 = G_ModelIndex( ent->model );
- }
- // Give us a bit of time to spawn in the other entities, since we may have to target one of 'em
- ent->e_ThinkFunc = thinkF_fx_explosion_trail_link;
- ent->nextthink = level.time + 500;
- // Save our position and link us up!
- G_SetOrigin( ent, ent->s.origin );
- VectorSet( ent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
- VectorScale( ent->maxs, -1, ent->mins );
- gi.linkentity( ent );
- }
- //
- //
- //------------------------------------------
- void fx_target_beam_set_debounce( gentity_t *self )
- {
- if ( self->wait >= FRAMETIME )
- {
- self->attackDebounceTime = level.time + self->wait + Q_irand( -self->random, self->random );
- }
- else if ( self->wait < 0 )
- {
- self->e_UseFunc = useF_NULL;
- }
- else
- {
- self->attackDebounceTime = level.time + FRAMETIME + Q_irand( -self->random, self->random );
- }
- }
- //------------------------------------------
- void fx_target_beam_fire( gentity_t *ent )
- {
- trace_t trace;
- vec3_t dir, org, end;
- int ignore;
- qboolean open;
- if ( !ent->enemy || !ent->enemy->inuse )
- {//info_null most likely
- ignore = ent->s.number;
- ent->enemy = NULL;
- VectorCopy( ent->s.origin2, org );
- }
- else
- {
- ignore = ent->enemy->s.number;
- VectorCopy( ent->enemy->currentOrigin, org );
- }
- VectorCopy( org, ent->s.origin2 );
- VectorSubtract( org, ent->s.origin, dir );
- VectorNormalize( dir );
- gi.trace( &trace, ent->s.origin, NULL, NULL, org, ENTITYNUM_NONE, MASK_SHOT );//ignore
- if ( ent->spawnflags & 2 )
- {
- open = qtrue;
- VectorCopy( org, end );
- }
- else
- {
- open = qfalse;
- VectorCopy( trace.endpos, end );
- }
- if ( trace.fraction < 1.0 )
- {
- if ( trace.entityNum < ENTITYNUM_WORLD )
- {
- gentity_t *victim = &g_entities[trace.entityNum];
- if ( victim && victim->takedamage )
- {
- if ( ent->spawnflags & 4 ) // NO_KNOCKBACK
- {
- G_Damage( victim, ent, ent->activator, dir, trace.endpos, ent->damage, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN );
- }
- else
- {
- G_Damage( victim, ent, ent->activator, dir, trace.endpos, ent->damage, 0, MOD_UNKNOWN );
- }
- }
- }
- }
- G_AddEvent( ent, EV_TARGET_BEAM_DRAW, ent->fxID );
- VectorCopy( end, ent->s.origin2 );
- if ( open )
- {
- VectorScale( dir, -1, ent->pos1 );
- }
- else
- {
- VectorCopy( trace.plane.normal, ent->pos1 );
- }
- ent->e_ThinkFunc = thinkF_fx_target_beam_think;
- ent->nextthink = level.time + FRAMETIME;
- }
- //------------------------------------------
- void fx_target_beam_fire_start( gentity_t *self )
- {
- fx_target_beam_set_debounce( self );
- self->e_ThinkFunc = thinkF_fx_target_beam_think;
- self->nextthink = level.time + FRAMETIME;
- self->painDebounceTime = level.time + self->speed + Q_irand( -500, 500 );
- fx_target_beam_fire( self );
- }
- //------------------------------------------
- void fx_target_beam_use( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- if ( self->spawnflags & 8 ) // one shot
- {
- fx_target_beam_fire( self );
- self->e_ThinkFunc = thinkF_NULL;
- }
- else if ( self->e_ThinkFunc == thinkF_NULL )
- {
- self->e_ThinkFunc = thinkF_fx_target_beam_think;
- self->nextthink = level.time + 50;
- }
- else
- {
- self->e_ThinkFunc = thinkF_NULL;
- }
- self->activator = activator;
- }
- //------------------------------------------
- void fx_target_beam_think( gentity_t *ent )
- {
- if ( ent->attackDebounceTime > level.time )
- {
- ent->nextthink = level.time + FRAMETIME;
- return;
- }
- fx_target_beam_fire_start( ent );
- }
- //------------------------------------------
- void fx_target_beam_link( gentity_t *ent )
- {
- gentity_t *target = NULL;
- vec3_t dir;
- float len;
- target = G_Find( target, FOFS(targetname), ent->target );
- if ( !target )
- {
- Com_Printf( "bolt_link: unable to find target %s\n", ent->target );
- G_FreeEntity( ent );
- return;
- }
- ent->attackDebounceTime = level.time;
- if ( !target->classname || Q_stricmp( "info_null", target->classname ) )
- {//don't want to set enemy to something that's going to free itself... actually, this could be bad in other ways, too... ent pointer could be freed up and re-used by the time we check it next
- G_SetEnemy( ent, target );
- }
- VectorSubtract( target->s.origin, ent->s.origin, dir );//er, does it ever use dir?
- len = VectorNormalize( dir );//er, does it use len or dir?
- vectoangles( dir, ent->s.angles );//er, does it use s.angles?
-
- VectorCopy( target->s.origin, ent->s.origin2 );
- if ( ent->spawnflags & 1 )
- {
- // Do nothing
- ent->e_ThinkFunc = thinkF_NULL;
- }
- else
- {
- if ( !(ent->spawnflags & 8 )) // one_shot, only calls when used
- {
- // switch think functions to avoid doing the bolt_link every time
- ent->e_ThinkFunc = thinkF_fx_target_beam_think;
- ent->nextthink = level.time + FRAMETIME;
- }
- }
- ent->e_UseFunc = useF_fx_target_beam_use;
- gi.linkentity( ent );
- }
- /*QUAKED fx_target_beam (1 0.5 0.5) (-8 -8 -8) (8 8 8) STARTOFF OPEN NO_KNOCKBACK ONE_SHOT NO_IMPACT
- Emits specified effect file, doing damage if required
- STARTOFF - must be used before it's on
- OPEN - will draw all the way to the target, regardless of where the trace hits
- NO_KNOCKBACK - beam damage does no knockback
- "fxFile" - Effect used to draw the beam, ( default "env/targ_beam" )
- "fxFile2" - Effect used for the beam impact effect, ( default "env/targ_beam_impact" )
- "targetname" - Fires only when used
- "duration" - How many seconds each burst lasts, -1 will make it stay on forever
- "wait" - If always on, how long to wait between blasts, in MILLISECONDS - default/min is 100 (1 frame at 10 fps), -1 means it will never fire again
- "random" - random amount of seconds added to/subtracted from "wait" each firing
- "damage" - How much damage to inflict PER FRAME (so probably want it kind of low), default is none
- "target" - ent to point at- you MUST have this. This can be anything you want, including a moving ent - for static beams, just use info_null
- */
- //------------------------------------------
- void SP_fx_target_beam( gentity_t *ent )
- {
- G_SetOrigin( ent, ent->s.origin );
- ent->speed *= 1000;
- ent->wait *= 1000;
- ent->random *= 1000;
- if ( ent->speed < FRAMETIME )
- {
- ent->speed = FRAMETIME;
- }
- G_SpawnInt( "damage", "0", &ent->damage );
- G_SpawnString( "fxFile", "env/targ_beam", &ent->fxFile );
- if ( ent->spawnflags & 16 ) // NO_IMPACT FX
- {
- ent->delay = 0;
- }
- else
- {
- G_SpawnString( "fxFile2", "env/targ_beam_impact", &ent->cameraGroup );
- ent->delay = G_EffectIndex( ent->cameraGroup );
- }
- ent->fxID = G_EffectIndex( ent->fxFile );
- ent->activator = ent;
- ent->owner = NULL;
- ent->e_ThinkFunc = thinkF_fx_target_beam_link;
- ent->nextthink = level.time + START_TIME_LINK_ENTS;
- VectorSet( ent->maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
- VectorScale( ent->maxs, -1, ent->mins );
- gi.linkentity( ent );
- }
- /*QUAKED fx_cloudlayer (1 0.3 0.5) (-8 -8 -8) (8 8 8) TUBE ALT
- Creates a scalable scrolling cloud layer, mostly for bespin undercity but could be used other places
- TUBE - creates cloud layer with tube opening in the middle, must an INNER radius also
- ALT - uses slightly different shader, good if using two layers sort of close together
- "radius" - outer radius of cloud layer, (default 2048)
- "random" - inner radius of cloud layer, (default 128) only works for TUBE type
- "wait" - adds curvature as it moves out to the edge of the layer. ( default 0 ), 1 = small up, 3 = up more, -1 = small down, -3 = down more, etc.
- */
- void SP_fx_cloudlayer( gentity_t *ent )
- {
- // HACK: this effect is never played, rather it just caches the shaders I need cgame side
- G_EffectIndex( "world/haze_cache" );
- G_SpawnFloat( "radius", "2048", &ent->radius );
- G_SpawnFloat( "random", "128", &ent->random );
- G_SpawnFloat( "wait", "0", &ent->wait );
- ent->s.eType = ET_CLOUD; // dumb
- G_SetOrigin( ent, ent->s.origin );
- ent->contents = 0;
- VectorSet( ent->maxs, 200, 200, 200 );
- VectorScale( ent->maxs, -1, ent->mins );
- gi.linkentity( ent );
- }
|