summaryrefslogtreecommitdiff
path: root/source/cl_tent.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/cl_tent.c')
-rw-r--r--source/cl_tent.c1763
1 files changed, 1763 insertions, 0 deletions
diff --git a/source/cl_tent.c b/source/cl_tent.c
new file mode 100644
index 0000000..1bef312
--- /dev/null
+++ b/source/cl_tent.c
@@ -0,0 +1,1763 @@
+/*
+Copyright (C) 1997-2001 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+// cl_tent.c -- client side temporary entities
+
+#include "cl_local.h"
+
+typedef enum
+{
+ ex_free, ex_explosion, ex_misc, ex_flash, ex_mflash, ex_poly, ex_poly2, ex_light
+} exptype_t;
+
+typedef struct
+{
+ exptype_t type;
+ entity_t ent;
+
+ int frames;
+ float light;
+ vec3_t lightcolor;
+ float start;
+ int baseframe;
+} explosion_t;
+
+
+
+#define MAX_EXPLOSIONS 32
+explosion_t cl_explosions[MAX_EXPLOSIONS];
+
+
+#define MAX_BEAMS 32
+typedef struct
+{
+ int entity;
+ int dest_entity;
+ qhandle_t model;
+ int endtime;
+ vec3_t offset;
+ vec3_t start, end;
+} beam_t;
+beam_t cl_beams[MAX_BEAMS];
+//PMM - added this for player-linked beams. Currently only used by the plasma beam
+beam_t cl_playerbeams[MAX_BEAMS];
+
+//ROGUE
+cl_sustain_t cl_sustains[MAX_SUSTAINS];
+//ROGUE
+
+//PGM
+extern void CL_TeleportParticles (vec3_t org);
+//PGM
+
+void CL_BlasterParticles (vec3_t org, vec3_t dir);
+void CL_ExplosionParticles (vec3_t org);
+void CL_BFGExplosionParticles (vec3_t org);
+// RAFAEL
+void CL_BlueBlasterParticles (vec3_t org, vec3_t dir);
+
+qhandle_t cl_sfx_ric1;
+qhandle_t cl_sfx_ric2;
+qhandle_t cl_sfx_ric3;
+qhandle_t cl_sfx_lashit;
+qhandle_t cl_sfx_spark5;
+qhandle_t cl_sfx_spark6;
+qhandle_t cl_sfx_spark7;
+qhandle_t cl_sfx_railg;
+qhandle_t cl_sfx_rockexp;
+qhandle_t cl_sfx_grenexp;
+qhandle_t cl_sfx_watrexp;
+// RAFAEL
+qhandle_t cl_sfx_plasexp;
+qhandle_t cl_sfx_footsteps[4];
+
+qhandle_t cl_mod_explode;
+qhandle_t cl_mod_smoke;
+qhandle_t cl_mod_flash;
+qhandle_t cl_mod_parasite_segment;
+qhandle_t cl_mod_grapple_cable;
+qhandle_t cl_mod_parasite_tip;
+qhandle_t cl_mod_explo4;
+qhandle_t cl_mod_bfg_explo;
+qhandle_t cl_mod_powerscreen;
+// RAFAEL
+qhandle_t cl_mod_plasmaexplo;
+
+//ROGUE
+qhandle_t cl_sfx_lightning;
+qhandle_t cl_sfx_disrexp;
+qhandle_t cl_mod_lightning;
+qhandle_t cl_mod_heatbeam;
+qhandle_t cl_mod_monster_heatbeam;
+qhandle_t cl_mod_explo4_big;
+
+
+#define MAX_LASERS 32
+
+laser_t cl_lasers[MAX_LASERS];
+
+/*
+=================
+CL_AllocLaser
+=================
+*/
+laser_t *CL_AllocLaser( void ) {
+ laser_t *l;
+ int i;
+
+ for( i=0, l=cl_lasers ; i<MAX_LASERS ; i++, l++ ) {
+ if( cl.time - l->startTime >= l->lifeTime ) {
+ memset( l, 0, sizeof( *l ) );
+ l->startTime = cl.time;
+ return l;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+=================
+CL_AddLasers
+=================
+*/
+void CL_AddLasers( void ) {
+ laser_t *l;
+ entity_t ent;
+ int i;
+ color_t color;
+ int time;
+ float f;
+
+ memset( &ent, 0, sizeof( ent ) );
+
+ for( i = 0, l = cl_lasers; i < MAX_LASERS; i++, l++ ) {
+ time = l->lifeTime - ( cl.time - l->startTime );
+ if( time < 0 ) {
+ continue;
+ }
+
+ ent.alpha = l->color[3] / 255.0f;
+
+ if( l->fadeType != LASER_FADE_NOT ) {
+ f = (float)time / (float)l->lifeTime;
+
+ ent.alpha *= f;
+ if( l->fadeType == LASER_FADE_RGBA ) {
+ *(int *)color = *(int *)l->color;
+ color[0] *= f;
+ color[1] *= f;
+ color[2] *= f;
+ ent.skinnum = *(int *)color;
+ }
+ } else {
+ ent.skinnum = *(int *)l->color;
+ }
+
+ ent.flags = RF_TRANSLUCENT|RF_BEAM;
+ VectorCopy( l->start, ent.origin );
+ VectorCopy( l->end, ent.oldorigin );
+ ent.frame = l->width;
+ ent.lightstyle = !l->indexed;
+
+ V_AddEntity( &ent );
+ }
+}
+
+//ROGUE
+/*
+=================
+CL_RegisterTEntSounds
+=================
+*/
+void CL_RegisterTEntSounds (void)
+{
+ int i;
+ char name[MAX_QPATH];
+
+ // PMM - version stuff
+// Com_Printf ("%s\n", ROGUE_VERSION_STRING);
+ // PMM
+ cl_sfx_ric1 = S_RegisterSound ("world/ric1.wav");
+ cl_sfx_ric2 = S_RegisterSound ("world/ric2.wav");
+ cl_sfx_ric3 = S_RegisterSound ("world/ric3.wav");
+ cl_sfx_lashit = S_RegisterSound("weapons/lashit.wav");
+ cl_sfx_spark5 = S_RegisterSound ("world/spark5.wav");
+ cl_sfx_spark6 = S_RegisterSound ("world/spark6.wav");
+ cl_sfx_spark7 = S_RegisterSound ("world/spark7.wav");
+ cl_sfx_railg = S_RegisterSound ("weapons/railgf1a.wav");
+ cl_sfx_rockexp = S_RegisterSound ("weapons/rocklx1a.wav");
+ cl_sfx_grenexp = S_RegisterSound ("weapons/grenlx1a.wav");
+ cl_sfx_watrexp = S_RegisterSound ("weapons/xpld_wat.wav");
+ // RAFAEL
+ // cl_sfx_plasexp = S_RegisterSound ("weapons/plasexpl.wav");
+ S_RegisterSound ("player/land1.wav");
+
+ S_RegisterSound ("player/fall2.wav");
+ S_RegisterSound ("player/fall1.wav");
+
+ for (i=0 ; i<4 ; i++)
+ {
+ Com_sprintf (name, sizeof(name), "player/step%i.wav", i+1);
+ cl_sfx_footsteps[i] = S_RegisterSound (name);
+ }
+
+//PGM
+ cl_sfx_lightning = S_RegisterSound ("weapons/tesla.wav");
+ cl_sfx_disrexp = S_RegisterSound ("weapons/disrupthit.wav");
+ // version stuff
+// sprintf (name, "weapons/sound%d.wav", ROGUE_VERSION_ID);
+// if (name[0] == 'w')
+// name[0] = 'W';
+//PGM
+}
+
+/*
+=================
+CL_RegisterTEntModels
+=================
+*/
+void CL_RegisterTEntModels (void)
+{
+ cl_mod_explode = ref.RegisterModel ("models/objects/explode/tris.md2");
+ cl_mod_smoke = ref.RegisterModel ("models/objects/smoke/tris.md2");
+ cl_mod_flash = ref.RegisterModel ("models/objects/flash/tris.md2");
+ cl_mod_parasite_segment = ref.RegisterModel ("models/monsters/parasite/segment/tris.md2");
+ cl_mod_grapple_cable = ref.RegisterModel ("models/ctf/segment/tris.md2");
+ cl_mod_parasite_tip = ref.RegisterModel ("models/monsters/parasite/tip/tris.md2");
+ cl_mod_explo4 = ref.RegisterModel ("models/objects/r_explode/tris.md2");
+ cl_mod_bfg_explo = ref.RegisterModel ("sprites/s_bfg2.sp2");
+ cl_mod_powerscreen = ref.RegisterModel ("models/items/armor/effect/tris.md2");
+
+ ref.RegisterModel ("models/objects/laser/tris.md2");
+ ref.RegisterModel ("models/objects/grenade2/tris.md2");
+ ref.RegisterModel ("models/weapons/v_machn/tris.md2");
+ ref.RegisterModel ("models/weapons/v_handgr/tris.md2");
+ ref.RegisterModel ("models/weapons/v_shotg2/tris.md2");
+ ref.RegisterModel ("models/objects/gibs/bone/tris.md2");
+ ref.RegisterModel ("models/objects/gibs/sm_meat/tris.md2");
+ ref.RegisterModel ("models/objects/gibs/bone2/tris.md2");
+ // RAFAEL
+ // ref.RegisterModel ("models/objects/blaser/tris.md2");
+
+ ref.RegisterPic ("w_machinegun");
+ ref.RegisterPic ("a_bullets");
+ ref.RegisterPic ("i_health");
+ ref.RegisterPic ("a_grenades");
+
+//ROGUE
+ cl_mod_explo4_big = ref.RegisterModel ("models/objects/r_explode2/tris.md2");
+ cl_mod_lightning = ref.RegisterModel ("models/proj/lightning/tris.md2");
+ cl_mod_heatbeam = ref.RegisterModel ("models/proj/beam/tris.md2");
+ cl_mod_monster_heatbeam = ref.RegisterModel ("models/proj/widowbeam/tris.md2");
+//ROGUE
+}
+
+/*
+=================
+CL_ClearTEnts
+=================
+*/
+void CL_ClearTEnts (void)
+{
+ memset (cl_beams, 0, sizeof(cl_beams));
+ memset (cl_explosions, 0, sizeof(cl_explosions));
+ memset (cl_lasers, 0, sizeof(cl_lasers));
+
+//ROGUE
+ memset (cl_playerbeams, 0, sizeof(cl_playerbeams));
+ memset (cl_sustains, 0, sizeof(cl_sustains));
+//ROGUE
+}
+
+/*
+=================
+CL_AllocExplosion
+=================
+*/
+explosion_t *CL_AllocExplosion (void)
+{
+ int i;
+ int time;
+ int index;
+
+ for (i=0 ; i<MAX_EXPLOSIONS ; i++)
+ {
+ if (cl_explosions[i].type == ex_free)
+ {
+ memset (&cl_explosions[i], 0, sizeof (cl_explosions[i]));
+ return &cl_explosions[i];
+ }
+ }
+// find the oldest explosion
+ time = cl.time;
+ index = 0;
+
+ for (i=0 ; i<MAX_EXPLOSIONS ; i++)
+ if (cl_explosions[i].start < time)
+ {
+ time = cl_explosions[i].start;
+ index = i;
+ }
+ memset (&cl_explosions[index], 0, sizeof (cl_explosions[index]));
+ return &cl_explosions[index];
+}
+
+/*
+=================
+CL_SmokeAndFlash
+=================
+*/
+void CL_SmokeAndFlash(vec3_t origin)
+{
+ explosion_t *ex;
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (origin, ex->ent.origin);
+ ex->type = ex_misc;
+ ex->frames = 4;
+ ex->ent.flags = RF_TRANSLUCENT;
+ ex->start = cl.serverTime - 100;
+ ex->ent.model = cl_mod_smoke;
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (origin, ex->ent.origin);
+ ex->type = ex_flash;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->frames = 2;
+ ex->start = cl.serverTime - 100;
+ ex->ent.model = cl_mod_flash;
+}
+
+/*
+=================
+CL_ParseBeam
+=================
+*/
+static void CL_ParseBeam (qhandle_t model)
+{
+ int ent;
+ vec3_t start, end;
+ beam_t *b;
+ int i;
+
+ ent = MSG_ReadShort ();
+
+ MSG_ReadPos (start);
+ MSG_ReadPos (end);
+
+// override any beam with the same entity
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ if (b->entity == ent)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorClear (b->offset);
+ return;
+ }
+
+// find a free beam
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorClear (b->offset);
+ return;
+ }
+ }
+
+}
+
+/*
+=================
+CL_ParseBeam2
+=================
+*/
+static void CL_ParseBeam2 (qhandle_t model)
+{
+ int ent;
+ vec3_t start, end, offset;
+ beam_t *b;
+ int i;
+
+ ent = MSG_ReadShort ();
+
+ MSG_ReadPos (start);
+ MSG_ReadPos (end);
+ MSG_ReadPos (offset);
+
+// override any beam with the same entity
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ if (b->entity == ent)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorCopy (offset, b->offset);
+ return;
+ }
+
+// find a free beam
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorCopy (offset, b->offset);
+ return;
+ }
+ }
+}
+
+// ROGUE
+/*
+=================
+CL_ParsePlayerBeam
+ - adds to the cl_playerbeam array instead of the cl_beams array
+=================
+*/
+static void CL_ParsePlayerBeam (qhandle_t model)
+{
+ int ent;
+ vec3_t start, end, offset;
+ beam_t *b;
+ int i;
+
+ ent = MSG_ReadShort ();
+
+ MSG_ReadPos (start);
+ MSG_ReadPos (end);
+ // PMM - network optimization
+ if (model == cl_mod_heatbeam)
+ VectorSet(offset, 2, 7, -3);
+ else if (model == cl_mod_monster_heatbeam)
+ {
+ model = cl_mod_heatbeam;
+ VectorSet(offset, 0, 0, 0);
+ }
+ else
+ MSG_ReadPos (offset);
+
+// override any beam with the same entity
+// PMM - For player beams, we only want one per player (entity) so..
+ for (i=0, b=cl_playerbeams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (b->entity == ent)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorCopy (offset, b->offset);
+ return;
+ }
+ }
+
+// find a free beam
+ for (i=0, b=cl_playerbeams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ {
+ b->entity = ent;
+ b->model = model;
+ b->endtime = cl.time + 100; // PMM - this needs to be 100 to prevent multiple heatbeams
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorCopy (offset, b->offset);
+ return;
+ }
+ }
+
+}
+//rogue
+
+/*
+=================
+CL_ParseLightning
+=================
+*/
+static void CL_ParseLightning (qhandle_t model)
+{
+ int srcEnt, destEnt;
+ vec3_t start, end;
+ beam_t *b;
+ int i;
+
+ srcEnt = MSG_ReadShort ();
+ destEnt = MSG_ReadShort ();
+
+ MSG_ReadPos (start);
+ MSG_ReadPos (end);
+
+ S_StartSound (NULL, srcEnt, CHAN_WEAPON, cl_sfx_lightning, 1, ATTN_NORM, 0);
+
+// override any beam with the same source AND destination entities
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ if (b->entity == srcEnt && b->dest_entity == destEnt)
+ {
+ b->entity = srcEnt;
+ b->dest_entity = destEnt;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorClear (b->offset);
+ return;
+ }
+
+// find a free beam
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ {
+ b->entity = srcEnt;
+ b->dest_entity = destEnt;
+ b->model = model;
+ b->endtime = cl.time + 200;
+ VectorCopy (start, b->start);
+ VectorCopy (end, b->end);
+ VectorClear (b->offset);
+ return;
+ }
+ }
+}
+
+/*
+=================
+CL_ParseLaser
+=================
+*/
+void CL_ParseLaser( int colors ) {
+ vec3_t start;
+ vec3_t end;
+ laser_t *l;
+
+ MSG_ReadPos( start );
+ MSG_ReadPos( end );
+
+ if( !( l = CL_AllocLaser() ) ) {
+ return;
+ }
+
+ VectorCopy( start, l->start );
+ VectorCopy( end, l->end );
+ l->fadeType = LASER_FADE_NOT;
+ l->lifeTime = 100;
+ l->indexed = qtrue;
+ l->color[0] = ( colors >> ( ( rand() % 4 ) * 8 ) ) & 0xff;
+ l->color[1] = 0;
+ l->color[2] = 0;
+ l->color[3] = 77;
+ l->width = 4;
+}
+
+//=============
+//ROGUE
+void CL_ParseSteam (void)
+{
+ vec3_t pos, dir;
+ int id, i;
+ int r;
+ int cnt;
+ int color;
+ int magnitude;
+ cl_sustain_t *s, *free_sustain;
+ int time;
+
+ id = MSG_ReadShort (); // an id of -1 is an instant effect
+ cnt = MSG_ReadByte ();
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ r = MSG_ReadByte ();
+ magnitude = MSG_ReadShort ();
+
+ if( id == -1 ) {
+ color = r & 0xff;
+ CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude);
+ return;
+ }
+
+
+ time = MSG_ReadLong (); // really interval
+
+ // sustains
+
+ free_sustain = NULL;
+ for (i=0, s=cl_sustains; i<MAX_SUSTAINS; i++, s++)
+ {
+ if (s->id == 0)
+ {
+ free_sustain = s;
+ break;
+ }
+ }
+ if (free_sustain)
+ {
+ s->id = id;
+ s->count = cnt;
+ VectorCopy( pos, s->org );
+ VectorCopy( dir, s->dir );
+ s->color = r & 0xff;
+ s->magnitude = magnitude;
+ s->endtime = cl.time + time;
+ s->think = CL_ParticleSteamEffect2;
+ s->thinkinterval = 100;
+ s->nextthink = cl.time;
+ }
+
+
+
+}
+
+void CL_ParseWidow (void)
+{
+ vec3_t pos;
+ int id, i;
+ cl_sustain_t *s, *free_sustain;
+
+ id = MSG_ReadShort ();
+
+ free_sustain = NULL;
+ for (i=0, s=cl_sustains; i<MAX_SUSTAINS; i++, s++)
+ {
+ if (s->id == 0)
+ {
+ free_sustain = s;
+ break;
+ }
+ }
+ if (free_sustain)
+ {
+ s->id = id;
+ MSG_ReadPos (s->org);
+ s->endtime = cl.time + 2100;
+ s->think = CL_Widowbeamout;
+ s->thinkinterval = 1;
+ s->nextthink = cl.time;
+ }
+ else // no free sustains
+ {
+ // FIXME - read the stuff anyway
+ MSG_ReadPos (pos);
+ }
+}
+
+void CL_ParseNuke (void)
+{
+ vec3_t pos;
+ int i;
+ cl_sustain_t *s, *free_sustain;
+
+ free_sustain = NULL;
+ for (i=0, s=cl_sustains; i<MAX_SUSTAINS; i++, s++)
+ {
+ if (s->id == 0)
+ {
+ free_sustain = s;
+ break;
+ }
+ }
+ if (free_sustain)
+ {
+ s->id = 21000;
+ MSG_ReadPos (s->org);
+ s->endtime = cl.time + 1000;
+ s->think = CL_Nukeblast;
+ s->thinkinterval = 1;
+ s->nextthink = cl.time;
+ }
+ else // no free sustains
+ {
+ // FIXME - read the stuff anyway
+ MSG_ReadPos (pos);
+ }
+}
+
+//ROGUE
+//=============
+
+static explosion_t *CL_RocketExplosion( vec3_t pos, qhandle_t hModel ) {
+ explosion_t *ex;
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_poly;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->start = cl.serverTime - 100;
+ ex->light = 350;
+ ex->lightcolor[0] = 1.0;
+ ex->lightcolor[1] = 0.5;
+ ex->lightcolor[2] = 0.5;
+ ex->ent.angles[1] = rand() % 360;
+ ex->ent.model = hModel;
+ if (frand() < 0.5)
+ ex->baseframe = 15;
+ ex->frames = 15;
+
+ return ex;
+}
+
+/*
+=================
+CL_ParseTEnt
+=================
+*/
+static byte splash_color[] = {0x00, 0xe0, 0xb0, 0x50, 0xd0, 0xe0, 0xe8};
+
+void CL_ParseTEnt (void)
+{
+ int type;
+ vec3_t pos, pos2, dir;
+ explosion_t *ex;
+ int cnt;
+ int color;
+ int r;
+ int ent;
+
+ type = MSG_ReadByte ();
+
+ switch (type)
+ {
+ case TE_BLOOD: // bullet hitting flesh
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ CL_ParticleEffect (pos, dir, 0xe8, 60);
+ break;
+
+ case TE_GUNSHOT: // bullet hitting wall
+ case TE_SPARKS:
+ case TE_BULLET_SPARKS:
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ if (type == TE_GUNSHOT)
+ CL_ParticleEffect (pos, dir, 0, 40);
+ else
+ CL_ParticleEffect (pos, dir, 0xe0, 6);
+
+ if (type != TE_SPARKS)
+ {
+ CL_SmokeAndFlash(pos);
+
+ // impact sound
+ cnt = rand()&15;
+ if (cnt == 1)
+ S_StartSound (pos, 0, 0, cl_sfx_ric1, 1, ATTN_NORM, 0);
+ else if (cnt == 2)
+ S_StartSound (pos, 0, 0, cl_sfx_ric2, 1, ATTN_NORM, 0);
+ else if (cnt == 3)
+ S_StartSound (pos, 0, 0, cl_sfx_ric3, 1, ATTN_NORM, 0);
+ }
+
+ break;
+
+ case TE_SCREEN_SPARKS:
+ case TE_SHIELD_SPARKS:
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ if (type == TE_SCREEN_SPARKS)
+ CL_ParticleEffect (pos, dir, 0xd0, 40);
+ else
+ CL_ParticleEffect (pos, dir, 0xb0, 40);
+ //FIXME : replace or remove this sound
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_SHOTGUN: // bullet hitting wall
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ CL_ParticleEffect (pos, dir, 0, 20);
+ CL_SmokeAndFlash(pos);
+ break;
+
+ case TE_SPLASH: // bullet hitting water
+ cnt = MSG_ReadByte ();
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ r = MSG_ReadByte ();
+ if (r > 6)
+ color = 0x00;
+ else
+ color = splash_color[r];
+ CL_ParticleEffect (pos, dir, color, cnt);
+
+ if (r == SPLASH_SPARKS)
+ {
+ r = rand() & 3;
+ if (r == 0)
+ S_StartSound (pos, 0, 0, cl_sfx_spark5, 1, ATTN_STATIC, 0);
+ else if (r == 1)
+ S_StartSound (pos, 0, 0, cl_sfx_spark6, 1, ATTN_STATIC, 0);
+ else
+ S_StartSound (pos, 0, 0, cl_sfx_spark7, 1, ATTN_STATIC, 0);
+ }
+ break;
+
+ case TE_LASER_SPARKS:
+ cnt = MSG_ReadByte ();
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ color = MSG_ReadByte ();
+ CL_ParticleEffect2 (pos, dir, color, cnt);
+ break;
+
+ // RAFAEL
+ case TE_BLUEHYPERBLASTER:
+ MSG_ReadPos (pos);
+ MSG_ReadPos (dir);
+ CL_BlasterParticles (pos, dir);
+ break;
+
+ case TE_BLASTER: // blaster hitting wall
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ CL_BlasterParticles( pos, dir );
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->ent.angles[0] = acos(dir[2])/M_PI*180;
+ // PMM - fixed to correct for pitch of 0
+ if (dir[0])
+ ex->ent.angles[1] = atan2(dir[1], dir[0])/M_PI*180;
+ else if (dir[1] > 0)
+ ex->ent.angles[1] = 90;
+ else if (dir[1] < 0)
+ ex->ent.angles[1] = 270;
+ else
+ ex->ent.angles[1] = 0;
+
+ ex->type = ex_misc;
+ ex->ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
+ ex->start = cl.serverTime - 100;
+ ex->light = 150;
+ ex->lightcolor[0] = 1;
+ ex->lightcolor[1] = 1;
+ ex->ent.model = cl_mod_explode;
+ ex->frames = 4;
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_RAILTRAIL: // railgun effect
+ MSG_ReadPos (pos);
+ MSG_ReadPos (pos2);
+ CL_RailTrail (pos, pos2);
+ S_StartSound (pos2, 0, 0, cl_sfx_railg, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_GRENADE_EXPLOSION:
+ case TE_GRENADE_EXPLOSION_WATER:
+ MSG_ReadPos( pos );
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_poly;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->start = cl.serverTime - 100;
+ ex->light = 350;
+ ex->lightcolor[0] = 1.0;
+ ex->lightcolor[1] = 0.5;
+ ex->lightcolor[2] = 0.5;
+ ex->ent.model = cl_mod_explo4;
+ ex->frames = 19;
+ ex->baseframe = 30;
+ ex->ent.angles[1] = rand() % 360;
+
+ if( cl_disable_explosions->integer & NOEXP_GRENADE ) {
+ ex->type = ex_light;
+ }
+
+ if( !( cl_disable_particles->integer & NOPART_GRENADE_EXPLOSION ) ) {
+ CL_ExplosionParticles( pos );
+ }
+ if (type == TE_GRENADE_EXPLOSION_WATER)
+ S_StartSound (pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0);
+ else
+ S_StartSound (pos, 0, 0, cl_sfx_grenexp, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_EXPLOSION2:
+ MSG_ReadPos( pos );
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_poly;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->start = cl.serverTime - 100;
+ ex->light = 350;
+ ex->lightcolor[0] = 1.0;
+ ex->lightcolor[1] = 0.5;
+ ex->lightcolor[2] = 0.5;
+ ex->ent.model = cl_mod_explo4;
+ ex->frames = 19;
+ ex->baseframe = 30;
+ ex->ent.angles[1] = rand() % 360;
+
+ CL_ExplosionParticles( pos );
+ S_StartSound (pos, 0, 0, cl_sfx_grenexp, 1, ATTN_NORM, 0);
+ break;
+
+
+ // RAFAEL
+ case TE_PLASMA_EXPLOSION:
+ MSG_ReadPos (pos);
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_poly;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->start = cl.serverTime - 100;
+ ex->light = 350;
+ ex->lightcolor[0] = 1.0;
+ ex->lightcolor[1] = 0.5;
+ ex->lightcolor[2] = 0.5;
+ ex->ent.angles[1] = rand() % 360;
+ ex->ent.model = cl_mod_explo4;
+ if (frand() < 0.5)
+ ex->baseframe = 15;
+ ex->frames = 15;
+ CL_ExplosionParticles (pos);
+ S_StartSound (pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_ROCKET_EXPLOSION:
+ case TE_ROCKET_EXPLOSION_WATER:
+ MSG_ReadPos( pos );
+
+ ex = CL_RocketExplosion( pos, cl_mod_explo4 );
+ if( cl_disable_explosions->integer & NOEXP_ROCKET ) {
+ ex->type = ex_light;
+ }
+ if( !( cl_disable_particles->integer & NOPART_ROCKET_EXPLOSION ) ) {
+ CL_ExplosionParticles( pos );
+ }
+
+ if( type == TE_ROCKET_EXPLOSION_WATER )
+ S_StartSound( pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0 );
+ else
+ S_StartSound( pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0 );
+ break;
+
+ case TE_EXPLOSION1:
+ MSG_ReadPos( pos );
+ CL_RocketExplosion( pos, cl_mod_explo4 );
+ CL_ExplosionParticles( pos );
+ S_StartSound( pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0 );
+ break;
+
+ case TE_EXPLOSION1_NP: // PMM
+ MSG_ReadPos( pos );
+ CL_RocketExplosion( pos, cl_mod_explo4 );
+ S_StartSound( pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0 );
+ break;
+
+ case TE_EXPLOSION1_BIG: // PMM
+ MSG_ReadPos( pos );
+ CL_RocketExplosion( pos, cl_mod_explo4_big );
+ S_StartSound( pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0 );
+ break;
+
+ case TE_BFG_EXPLOSION:
+ MSG_ReadPos (pos);
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_poly;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->start = cl.serverTime - 100;
+ ex->light = 350;
+ ex->lightcolor[0] = 0.0;
+ ex->lightcolor[1] = 1.0;
+ ex->lightcolor[2] = 0.0;
+ ex->ent.model = cl_mod_bfg_explo;
+ ex->ent.flags |= RF_TRANSLUCENT;
+ ex->ent.alpha = 0.30;
+ ex->frames = 4;
+ break;
+
+ case TE_BFG_BIGEXPLOSION:
+ MSG_ReadPos (pos);
+ CL_BFGExplosionParticles (pos);
+ break;
+
+ case TE_BFG_LASER:
+ CL_ParseLaser (0xd0d1d2d3);
+ break;
+
+ case TE_BUBBLETRAIL:
+ MSG_ReadPos (pos);
+ MSG_ReadPos (pos2);
+ CL_BubbleTrail (pos, pos2);
+ break;
+
+ case TE_PARASITE_ATTACK:
+ case TE_MEDIC_CABLE_ATTACK:
+ CL_ParseBeam (cl_mod_parasite_segment);
+ break;
+
+ case TE_BOSSTPORT: // boss teleporting to station
+ MSG_ReadPos (pos);
+ CL_BigTeleportParticles (pos);
+ S_StartSound (pos, 0, 0, S_RegisterSound ("misc/bigtele.wav"), 1, ATTN_NONE, 0);
+ break;
+
+ case TE_GRAPPLE_CABLE:
+ CL_ParseBeam2 (cl_mod_grapple_cable);
+ break;
+
+ // RAFAEL
+ case TE_WELDING_SPARKS:
+ cnt = MSG_ReadByte ();
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ color = MSG_ReadByte ();
+ CL_ParticleEffect2 (pos, dir, color, cnt);
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_flash;
+ // note to self
+ // we need a better no draw flag
+ ex->ent.flags = RF_BEAM;
+ ex->start = cl.serverTime - 0.1;
+ ex->light = 100 + (rand()%75);
+ ex->lightcolor[0] = 1.0;
+ ex->lightcolor[1] = 1.0;
+ ex->lightcolor[2] = 0.3;
+ ex->ent.model = cl_mod_flash;
+ ex->frames = 2;
+ break;
+
+ case TE_GREENBLOOD:
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ CL_ParticleEffect2 (pos, dir, 0xdf, 30);
+ break;
+
+ // RAFAEL
+ case TE_TUNNEL_SPARKS:
+ cnt = MSG_ReadByte ();
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ color = MSG_ReadByte ();
+ CL_ParticleEffect3 (pos, dir, color, cnt);
+ break;
+
+//=============
+//PGM
+ // PMM -following code integrated for flechette (different color)
+ case TE_BLASTER2: // green blaster hitting wall
+ case TE_FLECHETTE: // flechette
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+
+ // PMM
+ if (type == TE_BLASTER2)
+ CL_BlasterParticles2 (pos, dir, 0xd0);
+ else
+ CL_BlasterParticles2 (pos, dir, 0x6f); // 75
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->ent.angles[0] = acos(dir[2])/M_PI*180;
+ // PMM - fixed to correct for pitch of 0
+ if (dir[0])
+ ex->ent.angles[1] = atan2(dir[1], dir[0])/M_PI*180;
+ else if (dir[1] > 0)
+ ex->ent.angles[1] = 90;
+ else if (dir[1] < 0)
+ ex->ent.angles[1] = 270;
+ else
+ ex->ent.angles[1] = 0;
+
+ ex->type = ex_misc;
+ ex->ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
+
+ // PMM
+ if (type == TE_BLASTER2)
+ ex->ent.skinnum = 1;
+ else // flechette
+ ex->ent.skinnum = 2;
+
+ ex->start = cl.serverTime - 100;
+ ex->light = 150;
+ // PMM
+ if (type == TE_BLASTER2)
+ ex->lightcolor[1] = 1;
+ else // flechette
+ {
+ ex->lightcolor[0] = 0.19;
+ ex->lightcolor[1] = 0.41;
+ ex->lightcolor[2] = 0.75;
+ }
+ ex->ent.model = cl_mod_explode;
+ ex->frames = 4;
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+
+ case TE_LIGHTNING:
+ CL_ParseLightning (cl_mod_lightning);
+ break;
+
+ case TE_DEBUGTRAIL:
+ MSG_ReadPos (pos);
+ MSG_ReadPos (pos2);
+ CL_DebugTrail (pos, pos2);
+ break;
+
+ case TE_PLAIN_EXPLOSION:
+ MSG_ReadPos (pos);
+
+ ex = CL_AllocExplosion ();
+ VectorCopy (pos, ex->ent.origin);
+ ex->type = ex_poly;
+ ex->ent.flags = RF_FULLBRIGHT;
+ ex->start = cl.serverTime - 100;
+ ex->light = 350;
+ ex->lightcolor[0] = 1.0;
+ ex->lightcolor[1] = 0.5;
+ ex->lightcolor[2] = 0.5;
+ ex->ent.angles[1] = rand() % 360;
+ ex->ent.model = cl_mod_explo4;
+ if (frand() < 0.5)
+ ex->baseframe = 15;
+ ex->frames = 15;
+ S_StartSound (pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_FLASHLIGHT:
+ MSG_ReadPos(pos);
+ ent = MSG_ReadShort();
+ CL_Flashlight(ent, pos);
+ break;
+
+ case TE_FORCEWALL:
+ MSG_ReadPos(pos);
+ MSG_ReadPos(pos2);
+ color = MSG_ReadByte ();
+ CL_ForceWall(pos, pos2, color);
+ break;
+
+ case TE_HEATBEAM:
+ CL_ParsePlayerBeam (cl_mod_heatbeam);
+ break;
+
+ case TE_MONSTER_HEATBEAM:
+ CL_ParsePlayerBeam (cl_mod_monster_heatbeam);
+ break;
+
+ case TE_HEATBEAM_SPARKS:
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ CL_ParticleSteamEffect (pos, dir, 0x8, 50, 60);
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_HEATBEAM_STEAM:
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ CL_ParticleSteamEffect (pos, dir, 0xE0, 20, 60);
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_STEAM:
+ CL_ParseSteam();
+ break;
+
+ case TE_BUBBLETRAIL2:
+ MSG_ReadPos (pos);
+ MSG_ReadPos (pos2);
+ CL_BubbleTrail2 (pos, pos2, 8);
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_MOREBLOOD:
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ CL_ParticleEffect (pos, dir, 0xe8, 250);
+ break;
+
+ case TE_CHAINFIST_SMOKE:
+ MSG_ReadPos(pos);
+ VectorSet( dir, 0, 0, 1 );
+ CL_ParticleSmokeEffect (pos, dir, 0, 20, 20);
+ break;
+
+ case TE_ELECTRIC_SPARKS:
+ MSG_ReadPos (pos);
+ MSG_ReadDir (dir);
+ CL_ParticleEffect (pos, dir, 0x75, 40);
+ //FIXME : replace or remove this sound
+ S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_TRACKER_EXPLOSION:
+ MSG_ReadPos (pos);
+ CL_ColorFlash (pos, 0, 150, -1, -1, -1);
+ CL_ColorExplosionParticles (pos, 0, 1);
+ S_StartSound (pos, 0, 0, cl_sfx_disrexp, 1, ATTN_NORM, 0);
+ break;
+
+ case TE_TELEPORT_EFFECT:
+ case TE_DBALL_GOAL:
+ MSG_ReadPos (pos);
+ CL_TeleportParticles (pos);
+ break;
+
+ case TE_WIDOWBEAMOUT:
+ CL_ParseWidow ();
+ break;
+
+ case TE_NUKEBLAST:
+ CL_ParseNuke ();
+ break;
+
+ case TE_WIDOWSPLASH:
+ MSG_ReadPos (pos);
+ CL_WidowSplash (pos);
+ break;
+//PGM
+//==============
+
+ default:
+ Com_Error (ERR_DROP, "CL_ParseTEnt: bad type");
+ }
+}
+
+/*
+=================
+CL_AddBeams
+=================
+*/
+void CL_AddBeams (void)
+{
+ int i,j;
+ beam_t *b;
+ vec3_t dist, org;
+ float d;
+ entity_t ent;
+ float yaw, pitch;
+ float forward;
+ float len, steps;
+ float model_length;
+
+// update beams
+ for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
+ {
+ if (!b->model || b->endtime < cl.time)
+ continue;
+
+ // if coming from the player, update the start position
+ if (b->entity == cl.frame.clientNum+1) // entity 0 is the world
+ {
+ VectorCopy (cl.refdef.vieworg, b->start);
+ b->start[2] -= 22; // adjust for view height
+ }
+ VectorAdd (b->start, b->offset, org);
+
+ // calculate pitch and yaw
+ VectorSubtract (b->end, org, dist);
+
+ if (dist[1] == 0 && dist[0] == 0)
+ {
+ yaw = 0;
+ if (dist[2] > 0)
+ pitch = 90;
+ else
+ pitch = 270;
+ }
+ else
+ {
+ // PMM - fixed to correct for pitch of 0
+ if (dist[0])
+ yaw = (atan2(dist[1], dist[0]) * 180 / M_PI);
+ else if (dist[1] > 0)
+ yaw = 90;
+ else
+ yaw = 270;
+ if (yaw < 0)
+ yaw += 360;
+
+ forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
+ pitch = (atan2(dist[2], forward) * -180.0 / M_PI);
+ if (pitch < 0)
+ pitch += 360.0;
+ }
+
+ // add new entities for the beams
+ d = VectorNormalize(dist);
+
+ memset (&ent, 0, sizeof(ent));
+ if (b->model == cl_mod_lightning)
+ {
+ model_length = 35.0;
+ d-= 20.0; // correction so it doesn't end in middle of tesla
+ }
+ else
+ {
+ model_length = 30.0;
+ }
+ steps = ceil(d/model_length);
+ len = (d-model_length)/(steps-1);
+
+ // PMM - special case for lightning model .. if the real length is shorter than the model,
+ // flip it around & draw it from the end to the start. This prevents the model from going
+ // through the tesla mine (instead it goes through the target)
+ if ((b->model == cl_mod_lightning) && (d <= model_length))
+ {
+// Com_Printf ("special case\n");
+ VectorCopy (b->end, ent.origin);
+ // offset to push beam outside of tesla model (negative because dist is from end to start
+ // for this beam)
+// for (j=0 ; j<3 ; j++)
+// ent.origin[j] -= dist[j]*10.0;
+ ent.model = b->model;
+ ent.flags = RF_FULLBRIGHT;
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = rand()%360;
+ V_AddEntity (&ent);
+ return;
+ }
+ while (d > 0)
+ {
+ VectorCopy (org, ent.origin);
+ ent.model = b->model;
+ if (b->model == cl_mod_lightning)
+ {
+ ent.flags = RF_FULLBRIGHT;
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0;
+ ent.angles[2] = rand()%360;
+ }
+ else
+ {
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = rand()%360;
+ }
+
+// Com_Printf("B: %d -> %d\n", b->entity, b->dest_entity);
+ V_AddEntity (&ent);
+
+ for (j=0 ; j<3 ; j++)
+ org[j] += dist[j]*len;
+ d -= model_length;
+ }
+ }
+}
+
+extern cvar_t *hand;
+
+/*
+=================
+ROGUE - draw player locked beams
+CL_AddPlayerBeams
+=================
+*/
+void CL_AddPlayerBeams (void)
+{
+ int i,j;
+ beam_t *b;
+ vec3_t dist, org;
+ float d;
+ entity_t ent;
+ float yaw, pitch;
+ float forward;
+ float len, steps;
+ int framenum = 0;
+ float model_length;
+
+ float hand_multiplier;
+// frame_t *oldframe;
+ player_state_t *ps, *ops;
+
+//PMM
+ if (info_hand->integer == 2)
+ hand_multiplier = 0;
+ else if (info_hand->integer == 1)
+ hand_multiplier = -1;
+ else
+ hand_multiplier = 1;
+//PMM
+
+// update beams
+ for (i=0, b=cl_playerbeams ; i< MAX_BEAMS ; i++, b++)
+ {
+ vec3_t f,r,u;
+ if (!b->model || b->endtime < cl.time)
+ continue;
+
+ if(cl_mod_heatbeam && (b->model == cl_mod_heatbeam))
+ {
+
+ // if coming from the player, update the start position
+ if (b->entity == cl.frame.clientNum+1) // entity 0 is the world
+ {
+ // set up gun position
+ ps = &cl.frame.ps;
+ ops = &cl.oldframe.ps;
+
+ if( !cl.oldframe.valid || cl.oldframe.number != cl.frame.number - 1 )
+ ops = ps; // previous frame was dropped or invalid
+
+ for (j=0 ; j<3 ; j++)
+ {
+ b->start[j] = cl.refdef.vieworg[j] + ops->gunoffset[j]
+ + cl.lerpfrac * (ps->gunoffset[j] - ops->gunoffset[j]);
+ }
+ VectorMA (b->start, (hand_multiplier * b->offset[0]), cl.v_right, org);
+ VectorMA ( org, b->offset[1], cl.v_forward, org);
+ VectorMA ( org, b->offset[2], cl.v_up, org);
+ if (info_hand->integer == 2) {
+ VectorMA (org, -1, cl.v_up, org);
+ }
+ // FIXME - take these out when final
+ VectorCopy (cl.v_right, r);
+ VectorCopy (cl.v_forward, f);
+ VectorCopy (cl.v_up, u);
+
+ }
+ else
+ VectorCopy (b->start, org);
+ }
+ else
+ {
+ // if coming from the player, update the start position
+ if (b->entity == cl.frame.clientNum+1) // entity 0 is the world
+ {
+ VectorCopy (cl.refdef.vieworg, b->start);
+ b->start[2] -= 22; // adjust for view height
+ }
+ VectorAdd (b->start, b->offset, org);
+ }
+
+ // calculate pitch and yaw
+ VectorSubtract (b->end, org, dist);
+
+//PMM
+ if(cl_mod_heatbeam && (b->model == cl_mod_heatbeam) && (b->entity == cl.frame.clientNum+1))
+ {
+ vec_t len;
+
+ len = VectorLength (dist);
+ VectorScale (f, len, dist);
+ VectorMA (dist, (hand_multiplier * b->offset[0]), r, dist);
+ VectorMA (dist, b->offset[1], f, dist);
+ VectorMA (dist, b->offset[2], u, dist);
+ if (info_hand->value == 2) {
+ VectorMA (org, -1, cl.v_up, org);
+ }
+ }
+//PMM
+
+ if (dist[1] == 0 && dist[0] == 0)
+ {
+ yaw = 0;
+ if (dist[2] > 0)
+ pitch = 90;
+ else
+ pitch = 270;
+ }
+ else
+ {
+ // PMM - fixed to correct for pitch of 0
+ if (dist[0])
+ yaw = (atan2(dist[1], dist[0]) * 180 / M_PI);
+ else if (dist[1] > 0)
+ yaw = 90;
+ else
+ yaw = 270;
+ if (yaw < 0)
+ yaw += 360;
+
+ forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
+ pitch = (atan2(dist[2], forward) * -180.0 / M_PI);
+ if (pitch < 0)
+ pitch += 360.0;
+ }
+
+ if (cl_mod_heatbeam && (b->model == cl_mod_heatbeam))
+ {
+ if (b->entity != cl.frame.clientNum+1)
+ {
+ framenum = 2;
+// Com_Printf ("Third person\n");
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0;
+ ent.angles[2] = 0;
+// Com_Printf ("%f %f - %f %f %f\n", -pitch, yaw+180.0, b->offset[0], b->offset[1], b->offset[2]);
+ AngleVectors(ent.angles, f, r, u);
+
+ // if it's a non-origin offset, it's a player, so use the hardcoded player offset
+ if (!VectorCompare (b->offset, vec3_origin))
+ {
+ VectorMA (org, -(b->offset[0])+1, r, org);
+ VectorMA (org, -(b->offset[1]), f, org);
+ VectorMA (org, -(b->offset[2])-10, u, org);
+ }
+ else
+ {
+ // if it's a monster, do the particle effect
+ CL_MonsterPlasma_Shell(b->start);
+ }
+ }
+ else
+ {
+ framenum = 1;
+ }
+ }
+
+ // if it's the heatbeam, draw the particle effect
+ if ((cl_mod_heatbeam && (b->model == cl_mod_heatbeam) && (b->entity == cl.frame.clientNum+1)))
+ {
+ CL_Heatbeam (org, dist);
+ }
+
+ // add new entities for the beams
+ d = VectorNormalize(dist);
+
+ memset (&ent, 0, sizeof(ent));
+ if (b->model == cl_mod_heatbeam)
+ {
+ model_length = 32.0;
+ }
+ else if (b->model == cl_mod_lightning)
+ {
+ model_length = 35.0;
+ d-= 20.0; // correction so it doesn't end in middle of tesla
+ }
+ else
+ {
+ model_length = 30.0;
+ }
+ steps = ceil(d/model_length);
+ len = (d-model_length)/(steps-1);
+
+ // PMM - special case for lightning model .. if the real length is shorter than the model,
+ // flip it around & draw it from the end to the start. This prevents the model from going
+ // through the tesla mine (instead it goes through the target)
+ if ((b->model == cl_mod_lightning) && (d <= model_length))
+ {
+// Com_Printf ("special case\n");
+ VectorCopy (b->end, ent.origin);
+ // offset to push beam outside of tesla model (negative because dist is from end to start
+ // for this beam)
+// for (j=0 ; j<3 ; j++)
+// ent.origin[j] -= dist[j]*10.0;
+ ent.model = b->model;
+ ent.flags = RF_FULLBRIGHT;
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = rand()%360;
+ V_AddEntity (&ent);
+ return;
+ }
+ while (d > 0)
+ {
+ VectorCopy (org, ent.origin);
+ ent.model = b->model;
+ if(cl_mod_heatbeam && (b->model == cl_mod_heatbeam))
+ {
+// ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
+// ent.alpha = 0.3;
+ ent.flags = RF_FULLBRIGHT;
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0;
+ ent.angles[2] = (cl.time) % 360;
+// ent.angles[2] = rand()%360;
+ ent.frame = framenum;
+ }
+ else if (b->model == cl_mod_lightning)
+ {
+ ent.flags = RF_FULLBRIGHT;
+ ent.angles[0] = -pitch;
+ ent.angles[1] = yaw + 180.0;
+ ent.angles[2] = rand()%360;
+ }
+ else
+ {
+ ent.angles[0] = pitch;
+ ent.angles[1] = yaw;
+ ent.angles[2] = rand()%360;
+ }
+
+// Com_Printf("B: %d -> %d\n", b->entity, b->dest_entity);
+ V_AddEntity (&ent);
+
+ for (j=0 ; j<3 ; j++)
+ org[j] += dist[j]*len;
+ d -= model_length;
+ }
+ }
+}
+
+/*
+=================
+CL_AddExplosions
+=================
+*/
+void CL_AddExplosions (void)
+{
+ entity_t *ent;
+ int i;
+ explosion_t *ex;
+ float frac;
+ int f;
+
+ memset (&ent, 0, sizeof(ent));
+
+ for (i=0, ex=cl_explosions ; i< MAX_EXPLOSIONS ; i++, ex++)
+ {
+ if (ex->type == ex_free)
+ continue;
+ frac = (cl.time - ex->start)/100.0;
+ f = floor(frac);
+
+ ent = &ex->ent;
+
+ switch (ex->type) {
+ case ex_mflash:
+ if (f >= ex->frames-1)
+ ex->type = ex_free;
+ break;
+ case ex_misc:
+ case ex_light:
+ if (f >= ex->frames-1) {
+ ex->type = ex_free;
+ break;
+ }
+ ent->alpha = 1.0 - frac/(ex->frames-1);
+ break;
+ case ex_flash:
+ if (f >= 1) {
+ ex->type = ex_free;
+ break;
+ }
+ ent->alpha = 1.0;
+ break;
+ case ex_poly:
+ if (f >= ex->frames-1) {
+ ex->type = ex_free;
+ break;
+ }
+
+ ent->alpha = (16.0 - (float)f)/16.0;
+
+ if (f < 10) {
+ ent->skinnum = (f>>1);
+ if (ent->skinnum < 0)
+ ent->skinnum = 0;
+ } else {
+ ent->flags |= RF_TRANSLUCENT;
+ if (f < 13)
+ ent->skinnum = 5;
+ else
+ ent->skinnum = 6;
+ }
+ break;
+ case ex_poly2:
+ if (f >= ex->frames-1) {
+ ex->type = ex_free;
+ break;
+ }
+
+ ent->alpha = (5.0 - (float)f)/5.0;
+ ent->skinnum = 0;
+ ent->flags |= RF_TRANSLUCENT;
+ break;
+ default:
+ break;
+ }
+
+ if (ex->type == ex_free)
+ continue;
+ if (ex->light) {
+ V_AddLight (ent->origin, ex->light*ent->alpha,
+ ex->lightcolor[0], ex->lightcolor[1], ex->lightcolor[2]);
+ }
+
+ if( ex->type != ex_light ) {
+ VectorCopy (ent->origin, ent->oldorigin);
+
+ if (f < 0)
+ f = 0;
+ ent->frame = ex->baseframe + f + 1;
+ ent->oldframe = ex->baseframe + f;
+ ent->backlerp = 1.0 - cl.lerpfrac;
+
+ V_AddEntity (ent);
+ }
+ }
+}
+
+
+
+
+/* PMM - CL_Sustains */
+void CL_ProcessSustain (void)
+{
+ cl_sustain_t *s;
+ int i;
+
+ for (i=0, s=cl_sustains; i< MAX_SUSTAINS; i++, s++)
+ {
+ if (s->id)
+ {
+ if ((s->endtime >= cl.time) && (cl.time >= s->nextthink))
+ {
+// Com_Printf ("think %d %d %d\n", cl.time, s->nextthink, s->thinkinterval);
+ s->think (s);
+ }
+ else if (s->endtime < cl.time)
+ s->id = 0;
+ }
+ }
+}
+
+/*
+=================
+CL_AddTEnts
+=================
+*/
+void CL_AddTEnts (void)
+{
+ CL_AddBeams ();
+ // PMM - draw plasma beams
+ CL_AddPlayerBeams ();
+ CL_AddExplosions ();
+ CL_AddLasers ();
+ // PMM - set up sustain
+ CL_ProcessSustain();
+}
+
+
+