2013-04-04 14:52:42 +00:00
// leave this line at the top for all g_xxxx.cpp files...
# include "g_headers.h"
# include "g_local.h"
# include "g_functions.h"
2013-04-04 18:24:26 +00:00
extern int G_FindConfigstringIndex ( const char * name , int start , int max , qboolean create ) ;
2013-04-04 14:52:42 +00:00
# define FX_ENT_RADIUS 32
2013-04-04 18:24:26 +00:00
extern int BMS_START ;
extern int BMS_MID ;
extern int BMS_END ;
2013-04-04 14:52:42 +00:00
//----------------------------------------------------------
2013-04-04 18:02:27 +00:00
/*QUAKED fx_runner (0 0 1) (-8 -8 -8) (8 8 8) STARTOFF ONESHOT DAMAGE
2013-04-04 14:52:42 +00:00
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
2013-04-04 18:02:27 +00:00
DAMAGE - does radius damage around effect every " delay " milliseonds
2013-04-04 14:52:42 +00:00
" 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 = 0 ms to 200 ms )
2013-04-04 18:02:27 +00:00
" splashRadius " - only works when damage is checked ( default 16 )
" splashDamage " - only works when damage is checked ( default 5 )
2013-04-04 18:24:26 +00:00
" 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
2013-04-04 14:52:42 +00:00
*/
# define FX_RUNNER_RESERVED 0x800000
//----------------------------------------------------------
void fx_runner_think ( gentity_t * ent )
{
2013-04-04 18:02:27 +00:00
vec3_t temp ;
2013-04-04 14:52:42 +00:00
EvaluateTrajectory ( & ent - > s . pos , level . time , ent - > currentOrigin ) ;
2013-04-04 18:02:27 +00:00
EvaluateTrajectory ( & ent - > s . apos , level . time , ent - > currentAngles ) ;
2013-04-04 14:52:42 +00:00
// 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
2013-04-04 18:02:27 +00:00
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...
2013-04-04 14:52:42 +00:00
ent - > nextthink = level . time + ent - > delay + random ( ) * ent - > random ;
2013-04-04 18:02:27 +00:00
if ( ent - > spawnflags & 4 ) // damage
{
G_RadiusDamage ( ent - > currentOrigin , ent , ent - > splashDamage , ent - > splashRadius , ent , MOD_UNKNOWN ) ;
}
2013-04-04 14:52:42 +00:00
if ( ent - > target2 )
{
// let our target know that we have spawned an effect
G_UseTargets2 ( ent , ent , ent - > target2 ) ;
}
2013-04-04 18:24:26 +00:00
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 ;
}
}
}
2013-04-04 14:52:42 +00:00
}
//----------------------------------------------------------
void fx_runner_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
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.
2013-04-04 18:02:27 +00:00
fx_runner_think ( self ) ;
2013-04-04 14:52:42 +00:00
self - > nextthink = - 1 ;
if ( self - > target2 )
{
// let our target know that we have spawned an effect
G_UseTargets2 ( self , self , self - > target2 ) ;
}
2013-04-04 18:24:26 +00:00
if ( VALIDSTRING ( self - > soundSet ) = = true )
{
G_AddEvent ( self , EV_BMODEL_SOUND , CAS_GetBModelSound ( self - > soundSet , BMS_START ) ) ;
}
2013-04-04 14:52:42 +00:00
}
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 ) ;
2013-04-04 18:24:26 +00:00
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 ;
}
}
2013-04-04 14:52:42 +00:00
}
else
{
// turn off for now
self - > nextthink = - 1 ;
2013-04-04 18:24:26 +00:00
if ( VALIDSTRING ( self - > soundSet ) = = true )
{
G_AddEvent ( self , EV_BMODEL_SOUND , CAS_GetBModelSound ( self - > soundSet , BMS_END ) ) ;
self - > s . loopSound = 0 ;
}
2013-04-04 14:52:42 +00:00
}
}
}
//----------------------------------------------------------
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 ) ;
2013-04-04 18:02:27 +00:00
vectoangles ( dir , ent - > s . angles ) ;
2013-04-04 14:52:42 +00:00
}
}
// 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 ) ;
}
}
2013-04-04 18:02:27 +00:00
G_SetAngles ( ent , ent - > s . angles ) ;
2013-04-04 14:52:42 +00:00
if ( ent - > spawnflags & 1 | | ent - > spawnflags & 2 ) // STARTOFF || ONESHOT
{
// We won't even consider thinking until we are used
ent - > nextthink = - 1 ;
}
else
{
2013-04-04 18:24:26 +00:00
if ( VALIDSTRING ( ent - > soundSet ) = = true )
{
ent - > s . loopSound = CAS_GetBModelSound ( ent - > soundSet , BMS_MID ) ;
if ( ent - > s . loopSound < 0 )
{
ent - > s . loopSound = 0 ;
}
}
2013-04-04 14:52:42 +00:00
// Let's get to work right now!
ent - > e_ThinkFunc = thinkF_fx_runner_think ;
2013-04-04 18:24:26 +00:00
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 ;
2013-04-04 14:52:42 +00:00
}
}
//----------------------------------------------------------
void SP_fx_runner ( gentity_t * ent )
{
// Get our defaults
G_SpawnInt ( " delay " , " 200 " , & ent - > delay ) ;
G_SpawnFloat ( " random " , " 0 " , & ent - > random ) ;
2013-04-04 18:02:27 +00:00
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 ) ;
}
2013-04-04 14:52:42 +00:00
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 ) ;
2013-04-04 18:24:26 +00:00
ent - > s . eType = ET_MOVER ;
2013-04-04 14:52:42 +00:00
// 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 ;
2013-04-04 18:24:26 +00:00
ent - > nextthink = level . time + 400 ;
2013-04-04 14:52:42 +00:00
// 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)
This world effect will spawn snow globally into the level .
" count " the number of snow particles ( default of 1000 )
*/
//----------------------------------------------------------
void SP_CreateSnow ( gentity_t * ent )
{
char temp [ 256 ] ;
G_SpawnInt ( " count " , " 1000 " , & ent - > count ) ;
2013-04-04 21:05:53 +00:00
cvar_t * r_weatherScale = gi . cvar ( " r_weatherScale " , " 1 " , CVAR_ARCHIVE ) ;
2013-04-04 18:24:26 +00:00
2013-04-04 21:05:53 +00:00
if ( r_weatherScale - > value > 0.0f )
{
sprintf ( temp , " snow init %i " , ( int ) ( ent - > count * r_weatherScale - > value ) ) ;
G_FindConfigstringIndex ( temp , CS_WORLD_FX , MAX_WORLD_FX , qtrue ) ;
2013-04-04 14:52:42 +00:00
2013-04-04 21:05:53 +00:00
level . worldFlags | = WF_SNOWING ;
}
2013-04-04 14:52:42 +00:00
}
/*QUAKED fx_rain (1 0 0) (-16 -16 -16) (16 16 16)
This world effect will spawn rain globally into the level .
" count " the number of rain particles ( default of 500 )
*/
//----------------------------------------------------------
void SP_CreateRain ( gentity_t * ent )
{
char temp [ 256 ] ;
G_SpawnInt ( " count " , " 500 " , & ent - > count ) ;
2013-04-04 21:05:53 +00:00
cvar_t * r_weatherScale = gi . cvar ( " r_weatherScale " , " 1 " , CVAR_ARCHIVE ) ;
2013-04-04 18:24:26 +00:00
2013-04-04 21:05:53 +00:00
if ( r_weatherScale - > value > 0.0f )
{
sprintf ( temp , " rain init %i " , ( int ) ( ent - > count * r_weatherScale - > value ) ) ;
G_FindConfigstringIndex ( temp , CS_WORLD_FX , MAX_WORLD_FX , qtrue ) ;
2013-04-04 14:52:42 +00:00
2013-04-04 21:05:53 +00:00
level . worldFlags | = WF_RAINING ;
}
2013-04-04 14:52:42 +00:00
}
//-----------------
// 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 - > fullName )
{
// fxFile2....in other words, impact fx
G_PlayEffect ( ent - > fullName , tr . endpos , tr . plane . normal ) ;
}
2013-04-04 18:24:26 +00:00
if ( VALIDSTRING ( ent - > soundSet ) = = true )
{
G_AddEvent ( ent , EV_BMODEL_SOUND , CAS_GetBModelSound ( ent - > soundSet , BMS_END ) ) ;
}
2013-04-04 14:52:42 +00:00
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 ;
2013-04-04 18:24:26 +00:00
missile - > s . eType = ET_MOVER ;
2013-04-04 14:52:42 +00:00
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 - > fullName = self - > fullName ;
missile - > clipmask = MASK_SHOT ;
gi . linkentity ( missile ) ;
2013-04-04 18:24:26 +00:00
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 = self - > soundSet ;
if ( missile - > s . loopSound < 0 )
{
missile - > s . loopSound = 0 ;
}
}
2013-04-04 14:52:42 +00:00
}
}
//----------------------------------------------------------
void fx_explosion_trail_link ( gentity_t * ent )
{
vec3_t dir ;
gentity_t * target = NULL ;
2013-04-04 18:24:26 +00:00
// we ony activate when used
ent - > e_UseFunc = useF_fx_explosion_trail_use ;
2013-04-04 14:52:42 +00:00
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 )
2013-04-04 18:24:26 +00:00
" soundset " - soundset to use , start sound plays when explosion trail starts , loop sound plays on explosion trail , end sound plays when it impacts
2013-04-04 14:52:42 +00:00
*/
//----------------------------------------------------------
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 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 ) ;
if ( ent - > fullName )
{
G_EffectIndex ( ent - > fullName ) ;
}
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 ;
2013-04-04 18:24:26 +00:00
ent - > nextthink = level . time + 500 ;
2013-04-04 14:52:42 +00:00
// 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 - > fullName ) ;
ent - > delay = G_EffectIndex ( ent - > fullName ) ;
}
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 ) ;
}