thirtyflightsofloving/awaken2/g_target.c
Knightmare66 2b1aff408d Added target_effect entity from Lazarus to Awakening2 game DLL.
Minor formatting change to assigns and comapares for moveinfo.ratio in 3ZB2, Awakening, and missionpack game DLLs.
2021-03-11 04:07:29 -05:00

977 lines
26 KiB
C

// g_target.c
#include "g_local.h"
/*QUAKED target_temp_entity (1 0 0) (-8 -8 -8) (8 8 8)
Fire an origin based temp entity event to the clients.
"style" type byte
*/
void Use_Target_Tent(edict_t *ent, edict_t *other, edict_t *activator)
{
gi.WriteByte(svc_temp_entity);
gi.WriteByte(ent->style);
gi.WritePosition(ent->s.origin);
gi.multicast(ent->s.origin, MULTICAST_PVS);
}
void SP_target_temp_entity(edict_t *ent)
{
ent->use = Use_Target_Tent;
//CW++
ent->svflags |= SVF_NOCLIENT;
//CW--
}
//==========================================================
/*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off reliable
"noise" wav file to play
"attenuation"
-1 = none, send to whole level
1 = normal fighting sounds
2 = idle sound level
3 = ambient sound level
"volume" 0.0 to 1.0
Normal sounds play each time the target is used. The reliable flag can be set for crucial voiceovers.
Looped sounds are allways atten 3 / vol 1, and the use function toggles it on/off.
Multiple identical looping sounds will just increase volume without any speed cost.
*/
void Use_Target_Speaker(edict_t *ent, edict_t *other, edict_t *activator)
{
int chan;
if (ent->spawnflags & 3)
{ // looping sound toggles
if (ent->s.sound)
ent->s.sound = 0; // turn it off
else
ent->s.sound = ent->noise_index; // start it
}
else
{ // normal sound
if (ent->spawnflags & 4)
chan = CHAN_VOICE | CHAN_RELIABLE;
else
chan = CHAN_VOICE;
// use a positioned_sound, because this entity won't normally be
// sent to any clients because it is invisible
gi.positioned_sound(ent->s.origin, ent, chan, ent->noise_index, ent->volume, ent->attenuation, 0);
}
}
void SP_target_speaker(edict_t *ent)
{
char buffer[MAX_QPATH];
if (!st.noise)
{
gi.dprintf("target_speaker with no noise set at %s\n", vtos(ent->s.origin));
return;
}
if (!strstr (st.noise, ".wav"))
Com_sprintf(buffer, sizeof(buffer), "%s.wav", st.noise);
else
strncpy(buffer, st.noise, sizeof(buffer));
ent->noise_index = gi.soundindex (buffer);
if (!ent->volume)
ent->volume = 1.0;
if (!ent->attenuation)
ent->attenuation = 1.0;
else if (ent->attenuation == -1) // use -1 so 0 defaults to 1
ent->attenuation = 0;
// check for prestarted looping sound
if (ent->spawnflags & 1)
ent->s.sound = ent->noise_index;
ent->use = Use_Target_Speaker;
// must link the entity so we get areas and clusters so
// the server can determine who to send updates to
gi.linkentity(ent);
}
//==========================================================
/*QUAKED target_explosion (1 0 0) (-8 -8 -8) (8 8 8)
Spawns an explosion temporary entity when used.
"delay" wait this long before going off
"dmg" how much radius damage should be done, defaults to 0
*/
void target_explosion_explode(edict_t *self)
{
float save;
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_EXPLOSION1);
gi.WritePosition(self->s.origin);
gi.multicast(self->s.origin, MULTICAST_PHS);
T_RadiusDamage(self, self->activator, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE);
save = self->delay;
self->delay = 0.0;
G_UseTargets(self, self->activator);
self->delay = save;
}
void use_target_explosion(edict_t *self, edict_t *other, edict_t *activator)
{
self->activator = activator;
if (!self->delay)
{
target_explosion_explode(self);
return;
}
self->think = target_explosion_explode;
self->nextthink = level.time + self->delay;
}
void SP_target_explosion(edict_t *ent)
{
ent->use = use_target_explosion;
ent->svflags = SVF_NOCLIENT;
}
//==========================================================
/*QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8)
Changes level to "map" when fired
*/
void use_target_changelevel(edict_t *self, edict_t *other, edict_t *activator)
{
// ignore if already activated
if (level.intermissiontime)
return;
// if noexit, do a ton of damage to other
if (!((int)dmflags->value & DF_ALLOW_EXIT) && (other != world)) //CW
{
T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 10 * other->max_health, 1000, 0, MOD_EXIT);
return;
}
// let everyone know who hit the exit
if (activator && activator->client) //CW
gi_bprintf (PRINT_HIGH, "%s exited the level.\n", activator->client->pers.netname);
// if going to a new unit, clear cross triggers
if (strstr(self->map, "*"))
game.serverflags &= ~(SFL_CROSS_TRIGGER_MASK);
BeginIntermission(self);
}
void SP_target_changelevel(edict_t *ent)
{
if (!ent->map)
{
gi.dprintf("target_changelevel with no map at %s\n", vtos(ent->s.origin));
G_FreeEdict(ent);
return;
}
// ugly hack because *SOMEBODY* screwed up their map
if((Q_stricmp(level.mapname, "fact1") == 0) && (Q_stricmp(ent->map, "fact3") == 0))
ent->map = "fact3$secret1";
ent->use = use_target_changelevel;
ent->svflags = SVF_NOCLIENT;
}
//==========================================================
/*QUAKED target_splash (1 0 0) (-8 -8 -8) (8 8 8)
Creates a particle splash effect when used.
Set "sounds" to one of the following:
1) sparks
2) blue water
3) brown water
4) slime
5) lava
6) blood
"count" how many pixels in the splash
"dmg" if set, does a radius damage at this location when it splashes
useful for lava/sparks
*/
void use_target_splash(edict_t *self, edict_t *other, edict_t *activator)
{
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_SPLASH);
gi.WriteByte(self->count);
gi.WritePosition(self->s.origin);
gi.WriteDir(self->movedir);
gi.WriteByte(self->sounds);
gi.multicast(self->s.origin, MULTICAST_PVS);
if (self->dmg)
T_RadiusDamage(self, activator, self->dmg, NULL, self->dmg+40, MOD_SPLASH);
}
void SP_target_splash(edict_t *self)
{
self->use = use_target_splash;
G_SetMovedir(self->s.angles, self->movedir);
if (!self->count)
self->count = 32;
self->svflags = SVF_NOCLIENT;
}
//==========================================================
/*QUAKED target_spawner (1 0 0) (-8 -8 -8) (8 8 8)
Set target to the type of entity you want spawned.
Useful for spawning monsters and gibs in the factory levels.
For monsters:
Set direction to the facing you want it to have.
For gibs:
Set direction if you want it moving and
speed how fast it should be moving otherwise it
will just be dropped
*/
void ED_CallSpawn(edict_t *ent);
void use_target_spawner(edict_t *self, edict_t *other, edict_t *activator)
{
edict_t *ent;
ent = G_Spawn();
ent->classname = self->target;
VectorCopy(self->s.origin, ent->s.origin);
VectorCopy(self->s.angles, ent->s.angles);
ED_CallSpawn(ent);
gi.unlinkentity(ent);
KillBox(ent);
gi.linkentity(ent);
if (self->speed)
VectorCopy(self->movedir, ent->velocity);
}
void SP_target_spawner(edict_t *self)
{
self->use = use_target_spawner;
self->svflags = SVF_NOCLIENT;
if (self->speed)
{
G_SetMovedir(self->s.angles, self->movedir);
VectorScale(self->movedir, self->speed, self->movedir);
}
}
//==========================================================
/*QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS
Fires a blaster bolt in the set direction when triggered.
dmg default is 15
speed default is 1000
*/
void use_target_blaster(edict_t *self, edict_t *other, edict_t *activator)
{
int effect;
if (self->spawnflags & 2)
effect = 0;
else if (self->spawnflags & 1)
effect = EF_HYPERBLASTER;
else
effect = EF_BLASTER;
Fire_Blaster(self, self->s.origin, self->movedir, self->dmg, self->speed, effect); //CW
gi.sound(self, CHAN_VOICE, self->noise_index, 1, ATTN_NORM, 0);
}
void SP_target_blaster(edict_t *self)
{
self->use = use_target_blaster;
G_SetMovedir(self->s.angles, self->movedir);
self->noise_index = gi.soundindex("weapons/laser2.wav");
if (!self->dmg)
self->dmg = 15;
if (!self->speed)
self->speed = 1000.0;
self->svflags = SVF_NOCLIENT;
}
//==========================================================
/*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT
When triggered, fires a laser. You can either set a target
or a direction.
*/
void target_laser_think(edict_t *self)
{
edict_t *ignore;
trace_t tr;
vec3_t start;
vec3_t end;
vec3_t point;
vec3_t last_movedir;
int count;
if (self->spawnflags & 0x80000000)
count = 8;
else
count = 4;
if (self->enemy)
{
VectorCopy(self->movedir, last_movedir);
VectorMA(self->enemy->absmin, 0.5, self->enemy->size, point);
VectorSubtract(point, self->s.origin, self->movedir);
VectorNormalize(self->movedir);
if (!VectorCompare(self->movedir, last_movedir))
self->spawnflags |= 0x80000000;
}
ignore = self;
VectorCopy(self->s.origin, start);
VectorMA(start, 2048.0, self->movedir, end);
while (1)
{
tr = gi.trace(start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
if (!tr.ent)
break;
// hurt it if we can
if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER))
//CW++
{
if (tr.ent->client && (tr.ent->client->agm_enemy != NULL))
T_Damage(tr.ent, self, tr.ent->client->agm_enemy, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_AGM_TARG_LASER);
else
//CW--
T_Damage(tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
}
// if we hit something that's not a monster or player or is immune to lasers, we're done
if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
{
if (self->spawnflags & 0x80000000)
{
self->spawnflags &= ~0x80000000;
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_LASER_SPARKS);
gi.WriteByte(count);
gi.WritePosition(tr.endpos);
gi.WriteDir(tr.plane.normal);
gi.WriteByte(self->s.skinnum);
gi.multicast(tr.endpos, MULTICAST_PVS);
}
break;
}
ignore = tr.ent;
VectorCopy(tr.endpos, start);
}
VectorCopy(tr.endpos, self->s.old_origin);
self->nextthink = level.time + FRAMETIME;
}
void target_laser_on(edict_t *self)
{
if (!self->activator)
self->activator = self;
self->spawnflags |= 0x80000001;
self->svflags &= ~SVF_NOCLIENT;
target_laser_think(self);
}
void target_laser_off(edict_t *self)
{
self->spawnflags &= ~1;
self->svflags |= SVF_NOCLIENT;
self->nextthink = 0.0;
}
void target_laser_use(edict_t *self, edict_t *other, edict_t *activator)
{
self->activator = activator;
if (self->spawnflags & 1)
target_laser_off (self);
else
target_laser_on (self);
}
void target_laser_start(edict_t *self)
{
edict_t *ent;
self->movetype = MOVETYPE_NONE;
self->solid = SOLID_NOT;
self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
self->s.modelindex = 1; // must be non-zero
// set the beam diameter
if (self->spawnflags & 64)
self->s.frame = 16;
else
self->s.frame = 4;
// set the color
if (self->spawnflags & 2)
self->s.skinnum = 0xf2f2f0f0;
else if (self->spawnflags & 4)
self->s.skinnum = 0xd0d1d2d3;
else if (self->spawnflags & 8)
self->s.skinnum = 0xf3f3f1f1;
else if (self->spawnflags & 16)
self->s.skinnum = 0xdcdddedf;
else if (self->spawnflags & 32)
self->s.skinnum = 0xe0e1e2e3;
if (!self->enemy)
{
if (self->target)
{
ent = G_Find(NULL, FOFS(targetname), self->target);
if (!ent)
gi.dprintf("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
self->enemy = ent;
}
else
G_SetMovedir(self->s.angles, self->movedir);
}
self->use = target_laser_use;
self->think = target_laser_think;
if (!self->dmg)
self->dmg = 1;
VectorSet(self->mins, -8.0, -8.0, -8.0);
VectorSet(self->maxs, 8.0, 8.0, 8.0);
gi.linkentity(self);
if (self->spawnflags & 1)
target_laser_on(self);
else
target_laser_off(self);
}
void SP_target_laser (edict_t *self)
{
self->think = target_laser_start;
self->nextthink = level.time + 1.0; // let everything else get spawned before we start firing
}
//==========================================================
/*QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8)
When triggered, this initiates a level-wide earthquake.
All players and monsters are affected.
"speed" severity of the quake (default:200)
"count" duration of the quake (default:5)
*/
void target_earthquake_think(edict_t *self)
{
edict_t *e;
int i;
if (self->last_move_time < level.time)
{
gi.positioned_sound(self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
self->last_move_time = level.time + 0.5;
}
for (i = 1, e = g_edicts + i; i < globals.num_edicts; i++, e++)
{
if (!e->inuse)
continue;
if (!e->client)
continue;
if (!e->groundentity)
continue;
e->groundentity = NULL;
e->velocity[0] += crandom()* 150.0;
e->velocity[1] += crandom()* 150.0;
e->velocity[2] = self->speed * (100.0 / e->mass);
}
if (level.time < self->timestamp)
self->nextthink = level.time + FRAMETIME;
}
void target_earthquake_use(edict_t *self, edict_t *other, edict_t *activator)
{
self->timestamp = level.time + self->count;
self->nextthink = level.time + FRAMETIME;
self->activator = activator;
self->last_move_time = 0.0;
}
void SP_target_earthquake(edict_t *self)
{
if (!self->targetname)
gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
if (!self->count)
self->count = 5;
if (!self->speed)
self->speed = 200.0;
self->svflags |= SVF_NOCLIENT;
self->think = target_earthquake_think;
self->use = target_earthquake_use;
self->noise_index = gi.soundindex("world/quake.wav");
}
//==========================================================
/*QUAKED target_victory (1 0 0) (-8 -8 -8) (8 8 8)
ASSAULT: When triggered, ends the game with a victory for the attacking team.
*/
void target_victory_use(edict_t *self, edict_t *other, edict_t *activator)
{
G_UseTargets(self, activator);
asltgame.victory = true;
}
void SP_target_victory(edict_t *self)
{
if (sv_gametype->value != G_ASLT)
{
G_FreeEdict(self);
return;
}
self->classname = "assault_victory";
self->svflags |= SVF_NOCLIENT;
self->use = target_victory_use;
}
//CW--
/*====================================================================================
TARGET_EFFECT
======================================================================================*/
/* Unknowns or not supported
TE_FLAME, 32 Rogue flamethrower, never implemented
TE_FORCEWALL, 37 ??
*/
//=========================================================================
/* Spawns an effect at the entity origin
TE_FLASHLIGHT 36
*/
void target_effect_at (edict_t *self, edict_t *activator)
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (self->style);
gi.WritePosition (self->s.origin);
gi.WriteShort (self - g_edicts);
gi.multicast (self->s.origin, MULTICAST_PVS);
}
/* Poor man's target_steam
TE_STEAM 40
*/
void target_effect_steam (edict_t *self, edict_t *activator)
{
static int nextid;
int wait;
if (self->wait)
wait = self->wait*1000;
else
wait = 0;
if (nextid > 20000)
nextid = nextid %20000;
nextid++;
gi.WriteByte (svc_temp_entity);
gi.WriteByte (self->style);
gi.WriteShort (nextid);
gi.WriteByte (self->count);
gi.WritePosition (self->s.origin);
gi.WriteDir (self->movedir);
gi.WriteByte (self->sounds&0xff);
gi.WriteShort ( (int)(self->speed) );
gi.WriteLong ( (int)(wait) );
gi.multicast (self->s.origin, MULTICAST_PVS);
// if(level.num_reflectors)
// ReflectSteam (self->s.origin,self->movedir,self->count,self->sounds,(int)(self->speed),wait,nextid);
}
//=========================================================================
/*
Spawns (style) Splash with (count) particles of (sounds) color at (origin)
moving in (movedir) direction.
TE_SPLASH 10 Randomly shaded shower of particles
TE_LASER_SPARKS 15 Splash particles obey gravity
TE_WELDING_SPARKS 25 Splash particles with flash of light at {origin}
*/
//=========================================================================
void target_effect_splash (edict_t *self, edict_t *activator)
{
gi.WriteByte(svc_temp_entity);
gi.WriteByte(self->style);
gi.WriteByte(self->count);
gi.WritePosition(self->s.origin);
gi.WriteDir(self->movedir);
gi.WriteByte(self->sounds);
gi.multicast(self->s.origin, MULTICAST_PVS);
}
//======================================================
/*
Spawns a trail of (type) from (start) to (end) and Broadcasts to all
in Potentially Visible Set from vector (origin)
TE_RAILTRAIL 3 Spawns a blue spiral trail filled with white smoke
TE_BUBBLETRAIL 11 Spawns a trail of bubbles
TE_PARASITE_ATTACK 16
TE_MEDIC_CABLE_ATTACK 19
TE_BFG_LASER 23 Spawns a green laser
TE_GRAPPLE_CABLE 24
TE_RAILTRAIL2 31 NOT IMPLEMENTED IN ENGINE
TE_DEBUGTRAIL 34
TE_HEATBEAM, 38 Requires Rogue model
TE_MONSTER_HEATBEAM, 39 Requires Rogue model
TE_BUBBLETRAIL2 41
*/
//======================================================
void target_effect_trail (edict_t *self, edict_t *activator)
{
edict_t *target;
if (!self->target) return;
target = G_Find(NULL,FOFS(targetname),self->target);
if (!target) return;
gi.WriteByte(svc_temp_entity);
gi.WriteByte(self->style);
if ((self->style == TE_PARASITE_ATTACK) || (self->style==TE_MEDIC_CABLE_ATTACK) ||
(self->style == TE_HEATBEAM) || (self->style==TE_MONSTER_HEATBEAM) ||
(self->style == TE_GRAPPLE_CABLE) )
gi.WriteShort(self-g_edicts);
gi.WritePosition(self->s.origin);
gi.WritePosition(target->s.origin);
if (self->style == TE_GRAPPLE_CABLE) {
gi.WritePosition(vec3_origin);
}
gi.multicast(self->s.origin, MULTICAST_PVS);
/* if(level.num_reflectors)
{
if((self->style == TE_RAILTRAIL) || (self->style == TE_BUBBLETRAIL) ||
(self->style == TE_BFG_LASER) || (self->style == TE_DEBUGTRAIL) ||
(self->style == TE_BUBBLETRAIL2))
ReflectTrail(self->style,self->s.origin,target->s.origin);
} */
}
//===========================================================================
/* TE_LIGHTNING 33 Lightning bolt
Similar but slightly different syntax to trail stuff */
void target_effect_lightning (edict_t *self, edict_t *activator)
{
edict_t *target;
if (!self->target) return;
target = G_Find(NULL,FOFS(targetname),self->target);
if (!target) return;
gi.WriteByte (svc_temp_entity);
gi.WriteByte (self->style);
gi.WriteShort (target - g_edicts); // destination entity
gi.WriteShort (self - g_edicts); // source entity
gi.WritePosition (target->s.origin);
gi.WritePosition (self->s.origin);
gi.multicast (self->s.origin, MULTICAST_PVS);
}
//===========================================================================
/*
Spawns sparks of (type) from (start) in direction of (movdir) and
Broadcasts to all in Potentially Visible Set from vector (origin)
TE_GUNSHOT 0 Spawns a grey splash of particles, with a bullet puff
TE_BLOOD 1 Spawns a spurt of red blood
TE_BLASTER 2 Spawns a blaster sparks
TE_SHOTGUN 4 Spawns a small grey splash of spark particles, with a bullet puff
TE_SPARKS 9 Spawns a red/gold splash of spark particles
TE_SCREEN_SPARKS 12 Spawns a large green/white splash of sparks
TE_SHIELD_SPARKS 13 Spawns a large blue/violet splash of sparks
TE_BULLET_SPARKS 14 Same as TE_SPARKS, with a bullet puff and richochet sound
TE_GREENBLOOD 26 Spurt of green (actually kinda yellow) blood
TE_BLUEHYPERBLASTER 27 NOT IMPLEMENTED
TE_BLASTER2 30 Green/white sparks with a yellow/white flash
TE_MOREBLOOD 42
TE_HEATBEAM_SPARKS 43
TE_HEATBEAM_STEAM 44
TE_CHAINFIST_SMOKE 45
TE_ELECTRIC_SPARKS 46
TE_FLECHETTE 55
*/
//======================================================
void target_effect_sparks (edict_t *self, edict_t *activator)
{
gi.WriteByte(svc_temp_entity);
gi.WriteByte(self->style);
gi.WritePosition(self->s.origin);
if (self->style != TE_CHAINFIST_SMOKE)
gi.WriteDir(self->movedir);
gi.multicast(self->s.origin, MULTICAST_PVS);
// if (level.num_reflectors)
// ReflectSparks(self->style,self->s.origin,self->movedir);
}
//======================================================
/*
Spawns a (type) effect at (start} and Broadcasts to all in the
Potentially Hearable set from vector (origin)
TE_EXPLOSION1 5 airburst
TE_EXPLOSION2 6 ground burst
TE_ROCKET_EXPLOSION 7 rocket explosion
TE_GRENADE_EXPLOSION 8 grenade explosion
TE_ROCKET_EXPLOSION_WATER 17 underwater rocket explosion
TE_GRENADE_EXPLOSION_WATER 18 underwater grenade explosion
TE_BFG_EXPLOSION 20 BFG explosion sprite
TE_BFG_BIGEXPLOSION 21 BFG particle explosion
TE_BOSSTPORT 22
TE_PLASMA_EXPLOSION 28
TE_PLAIN_EXPLOSION 35
TE_TRACKER_EXPLOSION 47
TE_TELEPORT_EFFECT 48
TE_DBALL_GOAL 49 Identical to TE_TELEPORT_EFFECT?
TE_NUKEBLAST 51
TE_WIDOWSPLASH 52
TE_EXPLOSION1_BIG 53 Works, but requires Rogue models/objects/r_explode2
TE_EXPLOSION1_NP 54
*/
//==============================================================================
void target_effect_explosion (edict_t *self, edict_t *activator)
{
gi.WriteByte(svc_temp_entity);
gi.WriteByte(self->style);
gi.WritePosition(self->s.origin);
gi.multicast(self->s.origin, MULTICAST_PHS);
// if (level.num_reflectors)
// ReflectExplosion (self->style, self->s.origin);
}
//===============================================================================
/* TE_TUNNEL_SPARKS 29
Similar to other splash effects, but Xatrix does some funky things with
the origin so we'll do the same */
void target_effect_tunnel_sparks (edict_t *self, edict_t *activator)
{
vec3_t origin;
int i;
VectorCopy(self->s.origin,origin);
for (i=0; i<self->count; i++)
{
origin[2] += (self->speed * 0.01) * (i + random());
gi.WriteByte (svc_temp_entity);
gi.WriteByte (self->style);
gi.WriteByte (1);
gi.WritePosition (origin);
gi.WriteDir (vec3_origin);
gi.WriteByte (self->sounds + (rand()&7)); // color
gi.multicast (self->s.origin, MULTICAST_PVS);
}
}
//===============================================================================
/* TE_WIDOWBEAMOUT 50
*/
void target_effect_widowbeam (edict_t *self, edict_t *activator)
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_WIDOWBEAMOUT);
gi.WriteShort (20001);
gi.WritePosition (self->s.origin);
gi.multicast (self->s.origin, MULTICAST_PVS);
}
//===============================================================================
void target_effect_use (edict_t *self, edict_t *other, edict_t *activator)
{
if (self->spawnflags & 1)
{
// currently looped on - turn it off
self->spawnflags &= ~1;
self->spawnflags |= 2;
self->nextthink = 0;
return;
}
if (self->spawnflags & 2)
{
// currently looped off - turn it on
self->spawnflags &= ~2;
self->spawnflags |= 1;
self->nextthink = level.time + self->wait;
}
/* if (self->spawnflags & 4)
{
// "if_moving" set. If movewith target isn't moving,
// don't play
edict_t *mover;
if (!self->movewith) return;
mover = G_Find(NULL,FOFS(targetname), self->movewith);
if (!mover) return;
if (!VectorLength(mover->velocity)) return;
}*/
self->play(self,activator);
}
void target_effect_think (edict_t *self)
{
self->play(self,NULL);
self->nextthink = level.time + self->wait;
}
//===============================================================================
void SP_target_effect (edict_t *self)
{
// self->class_id = ENTITY_TARGET_EFFECT;
/* if (self->movewith)
self->movetype = MOVETYPE_PUSH;
else*/
self->movetype = MOVETYPE_NONE;
switch (self->style )
{
case TE_FLASHLIGHT:
self->play = target_effect_at;
break;
case TE_STEAM:
self->play = target_effect_steam;
G_SetMovedir (self->s.angles, self->movedir);
if (!self->count)
self->count = 32;
if (!self->sounds)
self->sounds = 8;
if (!self->speed)
self->speed = 75;
break;
case TE_SPLASH:
case TE_LASER_SPARKS:
case TE_WELDING_SPARKS:
self->play = target_effect_splash;
G_SetMovedir (self->s.angles, self->movedir);
if(!self->count)
self->count = 32;
break;
case TE_RAILTRAIL:
case TE_BUBBLETRAIL:
case TE_PARASITE_ATTACK:
case TE_MEDIC_CABLE_ATTACK:
case TE_BFG_LASER:
case TE_GRAPPLE_CABLE:
case TE_DEBUGTRAIL:
case TE_HEATBEAM:
case TE_MONSTER_HEATBEAM:
case TE_BUBBLETRAIL2:
if (!self->target) {
gi.dprintf("%s at %s with style=%d needs a target\n",self->classname,vtos(self->s.origin),self->style);
G_FreeEdict(self);
} else
self->play = target_effect_trail;
break;
case TE_LIGHTNING:
if (!self->target) {
gi.dprintf("%s at %s with style=%d needs a target\n",self->classname,vtos(self->s.origin),self->style);
G_FreeEdict(self);
} else
self->play = target_effect_lightning;
break;
case TE_GUNSHOT:
case TE_BLOOD:
case TE_BLASTER:
case TE_SHOTGUN:
case TE_SPARKS:
case TE_SCREEN_SPARKS:
case TE_SHIELD_SPARKS:
case TE_BULLET_SPARKS:
case TE_GREENBLOOD:
case TE_BLASTER2:
case TE_MOREBLOOD:
case TE_HEATBEAM_SPARKS:
case TE_HEATBEAM_STEAM:
case TE_CHAINFIST_SMOKE:
case TE_ELECTRIC_SPARKS:
case TE_FLECHETTE:
self->play = target_effect_sparks;
G_SetMovedir (self->s.angles, self->movedir);
break;
case TE_EXPLOSION1:
case TE_EXPLOSION2:
case TE_ROCKET_EXPLOSION:
case TE_GRENADE_EXPLOSION:
case TE_ROCKET_EXPLOSION_WATER:
case TE_GRENADE_EXPLOSION_WATER:
case TE_BFG_EXPLOSION:
case TE_BFG_BIGEXPLOSION:
case TE_BOSSTPORT:
case TE_PLASMA_EXPLOSION:
case TE_PLAIN_EXPLOSION:
case TE_TRACKER_EXPLOSION:
case TE_TELEPORT_EFFECT:
case TE_DBALL_GOAL:
case TE_NUKEBLAST:
case TE_WIDOWSPLASH:
case TE_EXPLOSION1_BIG:
case TE_EXPLOSION1_NP:
self->play = target_effect_explosion;
break;
case TE_TUNNEL_SPARKS:
if (!self->count)
self->count = 32;
if (!self->sounds)
self->sounds = 116; // Light blue, same color used by Xatrix
self->play = target_effect_tunnel_sparks;
break;
case TE_WIDOWBEAMOUT:
self->play = target_effect_widowbeam;
G_SetMovedir (self->s.angles, self->movedir);
break;
default:
gi.dprintf ("%s at %s: bad style %d\n",self->classname,vtos(self->s.origin),self->style);
}
self->use = target_effect_use;
self->think = target_effect_think;
if (self->spawnflags & 1)
self->nextthink = level.time + 1;
}