// This file contains game side effects that the designers can place throughout the maps #include "g_local.h" #define SPARK_STARTOFF 1 /*QUAKED fx_spark (0 0 1) (-8 -8 -8) (8 8 8) STARTOFF Emits sparks at the specified point in the specified direction "target" - ( optional ) direction to aim the sparks in, otherwise, uses the angles set in the editor. "wait(2000)" - interval between events (randomly twice as long) */ //------------------------------------------ void spark_think( gentity_t *ent ) { G_AddEvent( ent, EV_FX_SPARK, 0 ); ent->nextthink = level.time + 10000.0; // send a refresh message every 10 seconds } //T3h TiM-zor was here void spark_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { if ( self->count ) { self->think = 0; self->nextthink = -1; } else { self->think = spark_think; self->nextthink = level.time + 10000.0; } self->count = !(self->count); } //------------------------------------------ void spark_link( gentity_t *ent ) { ent->s.time2 = ent->wait; if ( ent->target ) { // try to use the target to orient me. gentity_t *target = NULL; vec3_t dir; target = G_Find (target, FOFS(targetname), ent->target); if (!target) { Com_Printf("spark_link: target specified but not found: %s\n", ent->target ); ent->think = 0; ent->nextthink = -1; return; } VectorSubtract( target->s.origin, ent->s.origin, dir ); VectorNormalize( dir ); vectoangles( dir, ent->r.currentAngles ); VectorCopy( ent->r.currentAngles, ent->s.angles2 ); //SnapVector( ent->s.angles ); VectorShort(ent->s.angles2); VectorCopy( ent->r.currentAngles, ent->s.apos.trBase ); SnapVector(ent->s.apos.trBase); } //TiM : for optional length in other functions ent->s.time = 10000; if ( !( ent->spawnflags & 1 ) ) { G_AddEvent( ent, EV_FX_SPARK, 0 ); ent->count = 1; ent->think = spark_think; ent->nextthink = level.time + 10000.0; } else { ent->count = 0; ent->think = 0; ent->nextthink = -1; } } //------------------------------------------ void SP_fx_spark( gentity_t *ent ) { if (!ent->wait) { ent->wait = 2000; } SnapVector(ent->s.origin); VectorCopy( ent->s.origin, ent->s.pos.trBase ); // The thing that this is targetting may not be spawned in yet, so wait a bit to try and link to it ent->think = spark_link; ent->nextthink = level.time + 2000; ent->use = spark_use; trap_LinkEntity( ent ); } /*QUAKED fx_steam (0 0 1) (-8 -8 -8) (8 8 8) STARTOFF Emits steam at the specified point in the specified direction. will point at a target if one is specified. Use toggleable steam with caution as updates every second instead of every 10 seconds, which means it sends 10 times the information that an untoggleable steam will send. STARTOFF steam is of at spawn "targetname" - toggles on/off whenever used "damage" - damage to apply when caught in steam vent, default - zero damage (no damage). Don't add this unless you really have to. */ #define STEAM_STARTOFF 1 #define STEAM_BURSTS 2 #define STEAM_UNLINKED 999 //------------------------------------------ void steam_think( gentity_t *ent ) { G_AddEvent( ent, EV_FX_STEAM, 0 ); if(ent->targetname) //toggleable steam needs to be updated more often ent->nextthink = level.time + 1000; else ent->nextthink = level.time + 10000.0; // send a refresh message every 10 seconds /* if ( ent->spawnflags & STEAM_BURSTS ) { ent->nextthink = level.time + 1000 + random() * 500; } else { ent->nextthink = level.time + 50; } // FIXME: This may be a bit weird for steam bursts // If a fool gets in the bolt path, zap 'em if ( ent->damage ) { vec3_t start, temp; trace_t trace; VectorSubtract( ent->s.origin2, ent->r.currentOrigin, temp ); VectorNormalize( temp ); VectorMA( ent->r.currentOrigin, 1, temp, start ); trap_Trace( &trace, start, NULL, NULL, ent->s.origin2, -1, MASK_SHOT );//ignore if ( trace.fraction < 1.0 ) { if ( trace.entityNum < ENTITYNUM_WORLD ) { gentity_t *victim = &g_entities[trace.entityNum]; if ( victim && victim->takedamage ) { G_Damage( victim, ent, ent->activator, temp, trace.endpos, ent->damage, 0, MOD_UNKNOWN ); } } } } */ } //------------------------------------------ void steam_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { if(self->count == STEAM_UNLINKED) return; if ( self->count ) { self->nextthink = -1; } else { self->nextthink = level.time + 100; } self->count = !self->count; } //------------------------------------------ void steam_link( gentity_t *ent ) { gentity_t *target = NULL; vec3_t dir; float len; //trace_t *tr; if (ent->target) { target = G_Find (target, FOFS(targetname), ent->target); } if (!target) { Com_Printf("steam_link: unable to find target %s\n", ent->target ); ent->think = 0; ent->nextthink = -1; return; } VectorSubtract( target->s.origin, ent->s.origin, dir ); len = VectorNormalize(dir); VectorCopy(dir, ent->s.angles2); //vectoangles(dir, ent->s.angles2); //GSIO01: haha funny thing this made steam buggy VectorShort(ent->s.angles2); //SnapVector( ent->s.angles2 ); VectorCopy( target->s.origin, ent->s.origin2 ); SnapVector(ent->s.origin2); if(ent->targetname) // toggleable steam needs to be updated more often ent->s.time = 1000; else ent->s.time = 10000; ent->use = steam_use; trap_LinkEntity( ent ); // this actually creates the continuously-spawning steam jet if(!ent->targetname || !(ent->spawnflags & STEAM_STARTOFF)) G_AddEvent( ent, EV_FX_STEAM, 0 ); ent->think = steam_think; if(ent->targetname && !(ent->spawnflags & STEAM_STARTOFF)) // toggleable steam needs to be updated more often ent->nextthink = level.time + 1000; else ent->nextthink = level.time + 10000; // This is used as the toggle switch //ent->count = !(ent->spawnflags & STEAM_STARTOFF); if(ent->targetname) { ent->count = !(ent->spawnflags & STEAM_STARTOFF); } } //------------------------------------------ void SP_fx_steam( gentity_t *ent ) { SnapVector(ent->s.origin); VectorCopy( ent->s.origin, ent->s.pos.trBase ); trap_LinkEntity( ent ); // Try to apply defaults if nothing was set G_SpawnInt( "damage", "0", &ent->damage ); ent->count = STEAM_UNLINKED; // so it can't be used before it's linked ent->think = steam_link; ent->nextthink = level.time + 2000; } /*QUAKED fx_bolt (0 0 1) (-8 -8 -8) (8 8 8) SPARKS BORG TAPER SMOOTH Emits blue ( or borg green ) electric bolts from the specified point to the specified point SPARKS - create impact sparks, probably best used for time delayed bolts BORG - Make the bolts green "wait" - seconds between bolts (0 is always on, default is 2.0, -1 for random number between 0 and 5), bolts are always on for 0.2 seconds "damage" - damage per server frame (default 0) "random" - bolt chaos (0.1 = too calm, 0.5 = default, 1.0 or higher = pretty wicked) */ #define BOLT_SPARKS (1<<0) #define BOLT_BORG (1<<1) //------------------------------------------ void bolt_think( gentity_t *ent ) { vec3_t start, temp; trace_t trace; G_AddEvent( ent, EV_FX_BOLT, ent->spawnflags ); ent->s.time2 = ent->wait; ent->nextthink = level.time + 10000;//(ent->wait + crandom() * ent->wait * 0.25) * 1000; // If a fool gets in the bolt path, zap 'em if ( ent->damage ) { VectorSubtract( ent->s.origin2, ent->r.currentOrigin, temp ); VectorNormalize( temp ); VectorMA( ent->r.currentOrigin, 1, temp, start ); trap_Trace( &trace, start, NULL, NULL, ent->s.origin2, -1, MASK_SHOT );//ignore if ( trace.fraction < 1.0 ) { if ( trace.entityNum < ENTITYNUM_WORLD ) { gentity_t *victim = &g_entities[trace.entityNum]; if ( victim && victim->takedamage ) { G_Damage( victim, ent, ent->activator, temp, trace.endpos, ent->damage, 0, MOD_UNKNOWN ); } } } } // net optimisations SnapVector(ent->s.origin2); } //------------------------------------------ void bolt_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { if ( self->count ) { self->think = 0; } else { self->think = bolt_think; self->nextthink = level.time + 200; } self->count = !self->count; } //------------------------------------------ void bolt_link( gentity_t *ent ) { gentity_t *target = NULL; vec3_t dir; float len; if (ent->target) { target = G_Find (target, FOFS(targetname), ent->target); } if (!target) { Com_Printf("bolt_link: unable to find target %s\n", ent->target ); ent->think = 0; ent->nextthink = -1; return; } VectorSubtract( target->s.origin, ent->s.origin, dir ); len = VectorNormalize( dir ); vectoangles( dir, ent->s.angles ); VectorCopy( target->s.origin, ent->s.origin2 ); SnapVector(ent->s.origin2); if ( ent->targetname ) { ent->use = bolt_use; } G_AddEvent( ent, EV_FX_BOLT, ent->spawnflags ); ent->s.time2 = ent->wait; ent->think = bolt_think; ent->nextthink = level.time + 10000; trap_LinkEntity( ent ); } //------------------------------------------ void SP_fx_bolt( gentity_t *ent ) { G_SpawnInt( "damage", "0", &ent->damage ); G_SpawnFloat( "random", "0.5", &ent->random ); G_SpawnFloat( "speed", "15.0", &ent->speed ); // See if effect is supposed to be delayed G_SpawnFloat( "wait", "2.0", &ent->wait ); SnapVector(ent->s.origin); VectorCopy( ent->s.origin, ent->s.pos.trBase ); ent->s.angles2[0] = ent->speed; ent->s.angles2[1] = ent->random; if (ent->target) { ent->think = bolt_link; ent->nextthink = level.time + 100; return; } trap_LinkEntity( ent ); } //-------------------------------------------------- /*QUAKED fx_transporter (0 0 1) (-8 -8 -8) (8 8 8) Emits transporter pad effect at the specified point. just rest it flush on top of the pad. */ void transporter_link( gentity_t *ent ) { G_AddEvent( ent, EV_FX_TRANSPORTER_PAD, 0 ); } //------------------------------------------ void SP_fx_transporter(gentity_t *ent) { SnapVector(ent->s.origin); VectorCopy( ent->s.origin, ent->s.pos.trBase ); ent->think = transporter_link; ent->nextthink = level.time + 2000; trap_LinkEntity( ent ); } /*QUAKED fx_drip (0 0 1) (-8 -8 -8) (8 8 8) STARTOFF "damage" -- type of drips. 0 = water, 1 = oil, 2 = green "random" -- (0...1) degree of drippiness. 0 = one drip, 1 = Niagara Falls */ //------------------------------------------ void drip_think( gentity_t *ent ) { G_AddEvent( ent, EV_FX_DRIP, 0 ); ent->nextthink = level.time + 10000; // send a refresh message every 10 seconds } //------------------------------------------ void SP_fx_drip( gentity_t *ent ) { ent->s.time2 = ent->damage; ent->s.angles2[0] = ent->random; SnapVector(ent->s.origin); VectorCopy( ent->s.origin, ent->s.pos.trBase ); ent->think = drip_think; ent->nextthink = level.time + 1000; //TiM ent->s.time = 10000; trap_LinkEntity( ent ); } //TiM - RPG-X FX Funcs //Most of these were copied from EF SP, and then modified for compatibility with the EF MP engine //*********************************************************************************** /*QUAKED fx_fountain (0 0 1) (-8 -8 -8) (8 8 8) STARTOFF STARTOFF - Effect spawns in an off state "targetname" - name of entity when used turns this ent on/off "target" - link to a notnull entity to position where the end point of this FX is */ void fountain_think( gentity_t *ent ) { G_AddEvent( ent, EV_FX_GARDEN_FOUNTAIN_SPURT, 0 ); ent->nextthink = level.time + 100; } void fountain_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { if ( self->count ) { self->think = 0; self->nextthink = -1; } else { self->think = fountain_think; self->nextthink = level.time + 100; } self->count = !self->count; } void SP_fx_fountain ( gentity_t *ent ) { gentity_t *target = NULL; if ( ent->target[0] ) { target = G_Find (target, FOFS(targetname), ent->target); } if ( !target ) { Com_Printf( S_COLOR_RED "Unable to find target point: %s\n", ent->target ); ent->think = 0; ent->nextthink = 0; return; } SnapVector( ent->s.origin ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); //Set end point to be origin2 VectorCopy( target->s.origin, ent->s.origin2 ); ent->use = fountain_use; //on and/or off state ent->count = !( ent->spawnflags & 1 ); if ( ent->count ) { ent->think = fountain_think; ent->nextthink = level.time + 100; } else { ent->think = 0; ent->nextthink= -1; } trap_LinkEntity( ent ); } /*QUAKED fx_surface_explosion (0 0 1) (-8 -8 -8) (8 8 8) NO_SMOKE LOUDER NODAMAGE Creates a triggerable explosion aimed at a specific point. Always oriented towards viewer. LOUDER - Cheap hack to make the explosion sound louder. NODAMAGE - Does no damage "target" (optional) If no target is specified, the explosion is oriented up "damage" - Damage per blast, default is 50. Damage falls off based on proximity. "radius" - blast radius (default 20) "speed" - camera shake speed (default 12). Set to zero to turn camera shakes off "targetname" - triggers explosion when used */ //------------------------------------------ void surface_explosion_use( gentity_t *self, gentity_t *other, gentity_t *activator) { G_AddEvent( self, EV_FX_SURFACE_EXPLOSION, 0 ); if ( self->splashDamage ) { G_RadiusDamage( self->r.currentOrigin, self, self->splashDamage, self->splashRadius, self, DAMAGE_RADIUS|DAMAGE_ALL_TEAMS, MOD_EXPLOSION ); } } //------------------------------------------ void surface_explosion_link( gentity_t *ent ) { gentity_t *target = NULL; vec3_t normal; target = G_Find (target, FOFS(targetname), ent->target); if ( target ) { VectorSubtract( target->s.origin, ent->s.origin, normal ); VectorNormalize( normal ); } else { VectorSet(normal, 0, 0, 1); } VectorCopy( normal, ent->s.origin2 ); ent->think = 0; ent->nextthink = -1; ent->use = surface_explosion_use; trap_LinkEntity( ent ); } //------------------------------------------ void SP_fx_surface_explosion( gentity_t *ent ) { if ( !(ent->spawnflags&4) ) { G_SpawnInt( "damage", "50", &ent->splashDamage ); G_SpawnFloat( "radius", "20", &ent->distance ); // was: ent->radius ent->splashRadius = 160; } G_SpawnFloat( "speed", "12", &ent->speed ); //TiM: Insert relevant params into the entityState struct so it gets passed to the client //Hacky this is... mmm yes ent->s.angles2[0] = ent->distance; // was: ent->radius ent->s.angles2[1] = ent->speed; ent->s.time2 = ent->spawnflags; // Precaching sounds /* if ( ent->spawnflags & 2 ) { G_SoundIndex( "sound/weapons/explosions/explode2.wav" ); } else { G_SoundIndex( "sound/weapons/explosions/cargoexplode.wav" ); } */ VectorCopy( ent->s.origin, ent->s.pos.trBase ); //ent->e_UseFunc = useF_surface_explosion_use; //ent->e_ThinkFunc = thinkF_surface_explosion_link; ent->use = surface_explosion_use; ent->think = surface_explosion_link; ent->nextthink = 1000; trap_LinkEntity( ent ); } /*QUAKED fx_blow_chunks (0 0 1) (-8 -8 -8) (8 8 8) Creates a triggerable chunk spewer that can be aimed at a specific point. "target" - (required) Target to spew chunks at "targetname" - triggers chunks when used "radius" - Average size of a chunk (default 65) "material" - default is "metal" - choose from this list: None = 0, Metal = 1 Glass = 2 Glass Metal = 3 Wood = 4 Stone = 5 (there will be more eventually lol.. I hope) */ //"count" - Number of chunks to spew (default 5) //"speed" - How fast a chunk will move when it get's spewed (default 175) //------------------------------------------ void blow_chunks_use( gentity_t *self, gentity_t *other, gentity_t *activator) { self->r.svFlags |= SVF_BROADCAST; G_AddEvent( self, EV_FX_CHUNKS, 0 ); } //------------------------------------------ void blow_chunks_link( gentity_t *ent ) { gentity_t *target = NULL; ent->think = 0; ent->nextthink = -1; target = G_Find (target, FOFS(targetname), ent->target); if ( !target ) { Com_Printf("blow_chunks_link: unable to find target %s\n", ent->target ); return; } //VectorCopy( target->s.origin, ent->s.origin2 ); VectorCopy( target->s.origin, ent->s.angles2 ); trap_LinkEntity( ent ); } //------------------------------------------ void SP_fx_blow_chunks( gentity_t *ent ) { //G_SpawnInt( "count", "5", &ent->count ); //G_SpawnFloat( "speed", "175", &ent->speed ); if(!ent->distance) // check for spawnTEnt G_SpawnFloat( "radius", "65", &ent->distance ); // was: ent->radius if(!ent->s.powerups) // check for spawnTEnt G_SpawnInt( "material", "0", &ent->s.powerups ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); //TiM: Fill entityState ent->s.time2 = (int)ent->distance; //Hack. :P The client side chunkfunc wants radius to be an int >.< || was: ent->radius //ent->s.angles2[1] = ent->speed; ent->use = blow_chunks_use; ent->think = blow_chunks_link; ent->nextthink = 1000; trap_LinkEntity( ent ); } /*QUAKED fx_smoke (0 0 1) (-8 -8 -8) (8 8 8) STARTOFF Emits cloud of thick black smoke from specified point. "target" (option) If no target is specified, the smoke drifts up "targetname" - fires only when used "radius" - size of the smoke puffs (default 16.0) */ //------------------------------------------ void smoke_think( gentity_t *ent ) { ent->nextthink = level.time + 10000; G_AddEvent( ent, EV_FX_SMOKE, 0 ); } //------------------------------------------ void smoke_use( gentity_t *self, gentity_t *other, gentity_t *activator) { if ( self->count ) { self->think = 0; self->nextthink = -1; } else { self->think = smoke_think; self->nextthink = level.time + 10000; } self->count = !self->count; } //------------------------------------------ void smoke_link( gentity_t *ent ) { // this link func is used because the target ent may not have spawned in yet, this // will give it a bit of extra time for that to happen. //TiM: Ohhhh that's why. I thought this was weird lol... gentity_t *target = NULL; vec3_t dir; target = G_Find (target, FOFS(targetname), ent->target); if (target) { //VectorCopy( target->s.origin, dir ); VectorSubtract( target->s.origin, ent->s.origin, dir ); VectorNormalize( dir ); vectoangles( dir, ent->s.angles2 ); VectorShort(ent->s.angles2); } else { //Hard code to be directly up VectorSet( dir, 0, 0, 10 ); VectorNormalize( dir ); vectoangles( dir, ent->s.angles2 ); VectorShort(ent->s.angles2); //VectorCopy( ent->s.origin, dir ); //dir[2] += 1; // move up } if (ent->targetname) { ent->use = smoke_use; } if (!ent->targetname || !(ent->spawnflags & 1) ) { ent->think = smoke_think; ent->nextthink = level.time + 200; ent->count = 1; } else { ent->think = 0; ent->nextthink = -1; ent->count = 0; } //VectorCopy( dir, ent->s.origin2 ); ent->s.time2 = 10000; trap_LinkEntity( ent ); } //------------------------------------------ void SP_fx_smoke( gentity_t *ent ) { G_SpawnFloat( "radius", "16.0", &ent->distance ); // was: ent->radius VectorCopy( ent->s.origin, ent->s.pos.trBase ); ent->s.angles2[0] = ent->distance; // was: ent->radius ent->think = smoke_link; ent->nextthink = level.time + 1000; trap_LinkEntity( ent ); } /*QUAKED fx_electrical_explosion (0 0 1) (-8 -8 -8) (8 8 8) x x NODAMAGE Creates a triggerable explosion aimed at a specific point NODAMAGE - does no damage "target" (optional) If no target is specified, the explosion is oriented up "damage" - Damage per blast, default is 20. Damage falls off based on proximity. "radius" - blast radius (default 50) "targetname" - explodes each time it's used */ //------------------------------------------ void electrical_explosion_use( gentity_t *self, gentity_t *other, gentity_t *activator) { //self->nextthink = level.time + 100; //trap_LinkEntity( self ); G_AddEvent( self, EV_FX_ELECTRICAL_EXPLOSION, 0 ); if ( self->splashDamage ) { G_RadiusDamage( self->s.origin, self, self->splashDamage, self->splashRadius, self, 0, MOD_UNKNOWN ); } } //------------------------------------------ void electrical_explosion_link( gentity_t *ent ) { gentity_t *target = NULL; vec3_t normal; target = G_Find( target, FOFS(targetname), ent->target ); if ( target ) { VectorSubtract( target->s.pos.trBase, ent->s.origin, normal ); VectorNormalize( normal ); } else { // No target so just shoot up VectorSet( normal, 0, 0, 1 ); } VectorCopy( normal, ent->s.origin2 ); ent->think = 0; ent->nextthink = -1; trap_LinkEntity( ent ); } //------------------------------------------ void SP_fx_electrical_explosion( gentity_t *ent ) { if ( !(ent->spawnflags&4) ) { G_SpawnInt( "damage", "20", &ent->splashDamage ); G_SpawnFloat( "radius", "50", &ent->distance ); // was: ent->radius ent->splashRadius = 80; } // Precaching sounds // G_SoundIndex( "sound/weapons/explosions/cargoexplode.wav" ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); ent->s.angles2[0] = ent->distance; // was: ent->radius ent->think = electrical_explosion_link; ent->nextthink = level.time + 1000; ent->use = electrical_explosion_use; trap_LinkEntity( ent ); } /*QUAKED fx_phaser (0 0 1) (-8 -8 -8) (8 8 8) NO_SOUND A phaser effect. "target" endpoint "wait" how long the phaser fires "scale" adjust the effects scale, default: 20 "customSnd" use a custom sound "delay" delay the effect, but not the sound. Can be used to adhust the timing between effect and customSnd "impact" set to 1 if you want an impact to be drawn */ #define PHASER_FX_UNLINKED 999 void phaser_use(gentity_t *ent, gentity_t *other, gentity_t *activator) { if(ent->count == PHASER_FX_UNLINKED) return; G_AddEvent(ent, EV_FX_PHASER, 0); } void phaser_link(gentity_t *ent) { gentity_t *target = NULL; target = G_Find(target, FOFS(targetname), ent->target); if(!target) { G_Printf("fx_phaser at %s with an unfound target %s!\n", vtos(ent->r.currentOrigin), ent->target); G_FreeEntity(ent); return; } VectorCopy(target->s.origin, ent->s.origin2); SnapVector(ent->s.origin2); ent->use = phaser_use; ent->count = 0; trap_LinkEntity(ent); } void SP_fx_phaser(gentity_t *ent) { float scale; char *sound; int impact; ent->count = PHASER_FX_UNLINKED; if(!ent->target) { G_Printf("fx_phaser at %s without target!\n", vtos(ent->r.currentOrigin)); return; } G_SpawnFloat("scale", "20", &scale); ent->s.angles[0] = scale; G_SpawnFloat("delay", "1", &scale); ent->s.angles[1] = scale * 1000; G_SpawnString("customSnd", "sound/pos_b/phaser.wav", &sound); if(!(ent->spawnflags & 1)) ent->s.time = G_SoundIndex(sound); else ent->s.time = G_SoundIndex("NULL"); if(ent->wait) ent->s.time2 = ent->wait * 1000; else ent->s.time2 = 3000; G_SpawnInt("impact", "0", & impact); ent->s.angles[2] = impact; ent->think = phaser_link; ent->nextthink = level.time + 1000; trap_LinkEntity(ent); } /*QUAKED fx_torpedo (0 0 1) (-8 -8 -8) (8 8 8) QUANTUM NO_SOUND A torpedo effect. QUANTUM set this flag if you whant an quantum fx instead of an photon fx "target" used for the calculation of the direction "wait" time in seconds till fx can be used again "noise" sound to play "soundNoAmmo" sound to play if ammo is depleted "count" ammount of torpedos that can be fired (defaults to -1 = infinite) "speed" a speed modifier (default: 2.5) */ void fx_torpedo_use(gentity_t* ent, gentity_t*other, gentity_t *activator); void fx_torpedo_think(gentity_t *ent) { ent->nextthink = -1; ent->use = fx_torpedo_use; } void fx_torpedo_use(gentity_t *ent, gentity_t *other, gentity_t *activator) { if(ent->count > 0) { ent->count--; trap_SendServerCommand(activator-g_entities, va("print \"Torpedos: %i of %i left.\n\"", ent->count, ent->damage)); G_AddEvent(ent, EV_GENERAL_SOUND, ent->s.time); } else { trap_SendServerCommand(activator-g_entities, "print \"^1Out of Torpedos.\n\""); G_AddEvent(ent, EV_GENERAL_SOUND, ent->n00bCount /*ent->soundLocked*/); return; } G_AddEvent(ent, EV_FX_TORPEDO, ent->spawnflags); ent->use = 0; ent->think = fx_torpedo_think; ent->nextthink = level.time + ent->wait; } void fx_torpedo_link(gentity_t *ent) { vec3_t dir; gentity_t *target = NULL; target = G_Find(target, FOFS(targetname), ent->target); if(!target) { G_Printf("fx_torpedo at %s with unfound target: %s\n", vtos(ent->s.origin), ent->target); G_FreeEntity(ent); return; } VectorSubtract(target->s.origin, ent->s.origin, dir); VectorCopy(target->s.origin, ent->s.origin2); VectorNormalize(dir); VectorCopy(dir, ent->s.angles); trap_LinkEntity(ent); if(ent->wait) ent->wait *= 1000; if(!ent->count) ent->count = -1; else ent->damage = ent->count; if(ent->speed) ent->s.angles2[0] = ent->speed; else ent->s.angles2[0] = 2.5; ent->use = fx_torpedo_use; ent->nextthink = -1; } void SP_fx_torpedo(gentity_t *ent) { char *sound; if(!ent->target) { G_Printf("fx_torpedo at %s without target\n", vtos(ent->s.origin)); G_FreeEntity(ent); return; } G_SpawnString("noise", "sound/rpg_runabout/torp.wav", &sound); if(!(ent->spawnflags & 2)) ent->s.time = G_SoundIndex(sound); else ent->s.time = G_SoundIndex("NULL"); G_SpawnString("soundNoAmmo", "sound/movers/switches/voyneg.mp3", &sound); if(!(ent->spawnflags & 2)) ent->n00bCount = G_SoundIndex(sound); else ent->n00bCount = G_SoundIndex("NULL"); ent->think = fx_torpedo_link; ent->nextthink = level.time + 1000; } /*QUAKED fx_particle_fire (0 0 1) (-8 -8 -8) (8 8 8) A particle based fire effect. Use this sparingly as it is an fps killer. If you want to use a bunch of fires use fx_fire. "size" how big the fire shoud be (default: 10) */ void particleFire_think(gentity_t *ent) { G_AddEvent(ent, EV_FX_PARTICLEFIRE, ent->count); ent->nextthink = level.time + 10000; //refresh every 10 seconds } void SP_fx_particleFire(gentity_t *ent) { int size; G_SpawnInt("size", "10", &size); if(!size) ent->count = 10; else ent->count = size; trap_LinkEntity(ent); ent->nextthink = level.time + 1000; ent->think = particleFire_think; } /*QUAKED fx_fire (0 0 1) (-8 -8 -8) (8 8 8) A fire affect based on the adminguns fire effect. "size" how big the fire shoud be (default: 64) "angles" fires angles (default: 0 0 0 = UP) */ void fire_think(gentity_t *ent) { G_AddEvent(ent, EV_FX_FIRE, 1); ent->nextthink = level.time + 10000; } void SP_fx_fire(gentity_t *ent) { int size; G_SpawnInt("size", "64", &size); if(!size) ent->s.time = 64; else ent->s.time = size; ent->s.angles2[2] = 1; ent->s.time2 = 10000; trap_LinkEntity(ent); ent->think = fire_think; ent->nextthink = level.time + 1000; }