/* 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 "client.h" #include "localent.h" typedef enum { ex_free, ex_explosion, ex_misc, ex_flash, ex_mflash, ex_poly, ex_poly2 } 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 64 explosion_t cl_explosions[MAX_EXPLOSIONS]; #define MAX_BEAMS 64 typedef struct { int entity; int dest_entity; struct model_s *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]; #define MAX_LASERS 64 typedef struct { entity_t ent; int endtime; } laser_t; laser_t cl_lasers[MAX_LASERS]; //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); static struct sfx_s *cl_sfx_ric1; static struct sfx_s *cl_sfx_ric2; static struct sfx_s *cl_sfx_ric3; static struct sfx_s *cl_sfx_lashit; static struct sfx_s *cl_sfx_spark5; static struct sfx_s *cl_sfx_spark6; static struct sfx_s *cl_sfx_spark7; static struct sfx_s *cl_sfx_railg; static struct sfx_s *cl_sfx_rockexp; static struct sfx_s *cl_sfx_grenexp; static struct sfx_s *cl_sfx_watrexp; // RAFAEL //static struct sfx_s *cl_sfx_plasexp; struct sfx_s *cl_sfx_footsteps[4]; static struct sfx_s *le_sfx_gibimp1; static struct sfx_s *le_sfx_gibimp2; static struct sfx_s *le_sfx_gibimp3; static struct model_s *cl_mod_explode; static struct model_s *cl_mod_smoke; static struct model_s *cl_mod_flash; static struct model_s *cl_mod_parasite_segment; static struct model_s *cl_mod_grapple_cable; static struct model_s *cl_mod_parasite_tip; static struct model_s *cl_mod_explo4; static struct model_s *cl_mod_bfg_explo; struct model_s *cl_mod_powerscreen; // RAFAEL //static struct model_s *cl_mod_plasmaexplo; //R1: static struct model_s *le_mod_debris1; static struct model_s *le_mod_gibs; //ROGUE static struct sfx_s *cl_sfx_lightning; static struct sfx_s *cl_sfx_disrexp; static struct model_s *cl_mod_lightning; static struct model_s *cl_mod_heatbeam; //static struct model_s *cl_mod_monster_heatbeam; static struct model_s *cl_mod_explo4_big; //ROGUE /* ================= CL_RegisterTEntSounds ================= */ void CL_RegisterTEntSounds (void) { int i; char name[MAX_QPATH]; 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"); //r1: localents le_sfx_gibimp1 = S_RegisterSound ("gibs/gibimp1.wav"); le_sfx_gibimp2 = S_RegisterSound ("gibs/gibimp2.wav"); le_sfx_gibimp3 = S_RegisterSound ("gibs/gibimp3.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 = re.RegisterModel ("models/objects/explode/tris.md2"); cl_mod_smoke = re.RegisterModel ("models/objects/smoke/tris.md2"); cl_mod_flash = re.RegisterModel ("models/objects/flash/tris.md2"); cl_mod_parasite_segment = re.RegisterModel ("models/monsters/parasite/segment/tris.md2"); cl_mod_grapple_cable = re.RegisterModel ("models/ctf/segment/tris.md2"); cl_mod_parasite_tip = re.RegisterModel ("models/monsters/parasite/tip/tris.md2"); cl_mod_explo4 = re.RegisterModel ("models/objects/r_explode/tris.md2"); cl_mod_bfg_explo = re.RegisterModel ("sprites/s_bfg2.sp2"); cl_mod_powerscreen = re.RegisterModel ("models/items/armor/effect/tris.md2"); //r1: also register localent models le_mod_debris1 = re.RegisterModel ("models/objects/debris2/tris.md2"); le_mod_gibs = re.RegisterModel ("models/objects/gibs/sm_meat/tris.md2"); //le_mod_debris2 = re.RegisterModel ("models/objects/debris2/tris.md2"); //le_mod_debris3 = re.RegisterModel ("models/objects/debris3/tris.md2"); //r1: why are these being reigstered? /* re.RegisterModel ("models/objects/laser/tris.md2"); re.RegisterModel ("models/objects/grenade2/tris.md2"); re.RegisterModel ("models/weapons/v_machn/tris.md2"); re.RegisterModel ("models/weapons/v_handgr/tris.md2"); re.RegisterModel ("models/weapons/v_shotg2/tris.md2"); re.RegisterModel ("models/objects/gibs/bone/tris.md2"); re.RegisterModel ("models/objects/gibs/sm_meat/tris.md2"); re.RegisterModel ("models/objects/gibs/bone2/tris.md2"); // RAFAEL // re.RegisterModel ("models/objects/blaser/tris.md2"); re.RegisterPic ("w_machinegun"); re.RegisterPic ("a_bullets"); re.RegisterPic ("i_health"); re.RegisterPic ("a_grenades");*/ //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 //r1: reset dynamically loaded models cl_mod_heatbeam = cl_mod_explo4_big = cl_mod_lightning = NULL; } /* ================= CL_AllocExplosion ================= */ explosion_t *CL_AllocExplosion (void) { int i; int index; float time; for (i=0 ; ient.origin); ex->type = ex_misc; ex->frames = 4; ex->ent.flags = RF_TRANSLUCENT; ex->start = cl.frame.servertime - 100.0f; ex->ent.model = cl_mod_smoke; ex = CL_AllocExplosion (); FastVectorCopy (*origin, ex->ent.origin); ex->type = ex_flash; ex->ent.flags = RF_FULLBRIGHT; ex->frames = 2; ex->start = cl.frame.servertime - 100.0f; ex->ent.model = cl_mod_flash; } /* ================= CL_ParseParticles ================= */ void CL_ParseParticles (void) { int color, count; vec3_t pos, dir; MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); color = MSG_ReadByte (&net_message); count = MSG_ReadByte (&net_message); CL_ParticleEffect (pos, dir, color, count); } /* ================= CL_ParseBeam ================= */ void CL_ParseBeam (struct model_s *model) { int ent; vec3_t start, end; beam_t *b; int i; ent = MSG_ReadShort (&net_message); //if (ent < 1 || ent > MAX_EDICTS) // Com_Error (ERR_DROP, "CL_ParseBeam: bad entity %d", ent); MSG_ReadPos (&net_message, start); MSG_ReadPos (&net_message, 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; FastVectorCopy (start, b->start); FastVectorCopy (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; FastVectorCopy (start, b->start); FastVectorCopy (end, b->end); VectorClear (b->offset); return; } } Com_Printf ("beam list overflow!\n", LOG_CLIENT); return; } /* ================= CL_ParseBeam2 ================= */ void CL_ParseBeam2 (struct model_s *model) { int ent; vec3_t start, end, offset; beam_t *b; int i; ent = MSG_ReadShort (&net_message); MSG_ReadPos (&net_message, start); MSG_ReadPos (&net_message, end); MSG_ReadPos (&net_message, 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; FastVectorCopy (start, b->start); FastVectorCopy (end, b->end); FastVectorCopy (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; FastVectorCopy (start, b->start); FastVectorCopy (end, b->end); FastVectorCopy (offset, b->offset); return; } } Com_Printf ("beam list overflow!\n", LOG_CLIENT); return; } // ROGUE /* ================= CL_ParsePlayerBeam - adds to the cl_playerbeam array instead of the cl_beams array ================= */ void CL_ParsePlayerBeam (int tempent) { struct model_s *model; int ent; vec3_t start, end, offset; beam_t *b; int i; ent = MSG_ReadShort (&net_message); MSG_ReadPos (&net_message, start); MSG_ReadPos (&net_message, end); // PMM - network optimization if (!cl_mod_heatbeam) { cl_mod_heatbeam = re.RegisterModel ("models/proj/beam/tris.md2"); //if (!cl_mod_heatbeam) //{ // Com_Error (ERR_DROP, "Couldn't load models/proj/beam/tris.md2"); //} } model = cl_mod_heatbeam; if (tempent == TE_HEATBEAM) { VectorSet(offset, 2, 7, -3); } else if (tempent == TE_MONSTER_HEATBEAM) { VectorSet(offset, 0, 0, 0); } else { MSG_ReadPos (&net_message, offset); } //if we don't have the model, don't do anything if (!model) return; // 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; FastVectorCopy (start, b->start); FastVectorCopy (end, b->end); FastVectorCopy (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 FastVectorCopy (start, b->start); FastVectorCopy (end, b->end); FastVectorCopy (offset, b->offset); return; } } Com_Printf ("beam list overflow!\n", LOG_CLIENT); return; } //rogue /* ================= CL_ParseLightning ================= */ int CL_ParseLightning (struct model_s *model) { int srcEnt, destEnt; vec3_t start, end; beam_t *b; int i; srcEnt = MSG_ReadShort (&net_message); destEnt = MSG_ReadShort (&net_message); MSG_ReadPos (&net_message, start); MSG_ReadPos (&net_message, end); // 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; FastVectorCopy (start, b->start); FastVectorCopy (end, b->end); VectorClear (b->offset); return srcEnt; } } // 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; FastVectorCopy (start, b->start); FastVectorCopy (end, b->end); VectorClear (b->offset); return srcEnt; } } Com_Printf ("beam list overflow!\n", LOG_CLIENT); return srcEnt; } /* ================= CL_ParseLaser ================= */ void CL_ParseLaser (int colors, vec3_t start, vec3_t end) { laser_t *l; int i; for (i=0, l=cl_lasers ; i< MAX_LASERS ; i++, l++) { if (l->endtime < cl.time) { l->ent.flags = RF_TRANSLUCENT | RF_BEAM; FastVectorCopy (*start, l->ent.origin); FastVectorCopy (*end, l->ent.oldorigin); l->ent.alpha = 0.30f; l->ent.skinnum = (colors >> ((randomMT() % 4)*8)) & 0xff; l->ent.model = NULL; l->ent.frame = 4; l->endtime = cl.time + 100; return; } } } //============= //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; id = MSG_ReadShort (&net_message); // an id of -1 is an instant effect if (id != -1) // sustains { free_sustain = NULL; for (i=0, s=cl_sustains; iid == 0) { free_sustain = s; break; } } if (free_sustain) { s->id = id; s->count = MSG_ReadByte (&net_message); MSG_ReadPos (&net_message, s->org); MSG_ReadDir (&net_message, s->dir); r = MSG_ReadByte (&net_message); s->color = r & 0xff; s->magnitude = MSG_ReadShort (&net_message); s->endtime = cl.time + MSG_ReadLong (&net_message); s->think = CL_ParticleSteamEffect2; s->thinkinterval = 100; s->nextthink = cl.time; } else { // FIXME - read the stuff anyway cnt = MSG_ReadByte (&net_message); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); r = MSG_ReadByte (&net_message); magnitude = MSG_ReadShort (&net_message); magnitude = MSG_ReadLong (&net_message); // really interval } } else // instant { cnt = MSG_ReadByte (&net_message); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); r = MSG_ReadByte (&net_message); magnitude = MSG_ReadShort (&net_message); color = r & 0xff; CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude); // S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); } } void CL_ParseWidow (void) { vec3_t pos; int id, i; cl_sustain_t *s, *free_sustain; id = MSG_ReadShort (&net_message); free_sustain = NULL; for (i=0, s=cl_sustains; iid == 0) { free_sustain = s; break; } } if (free_sustain) { s->id = id; MSG_ReadPos (&net_message, 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 (&net_message, 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; iid == 0) { free_sustain = s; break; } } if (free_sustain) { s->id = 21000; MSG_ReadPos (&net_message, 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 (&net_message, pos); } } //ROGUE //============= void CL_Gib_Touch (localent_t *le, cplane_t *p, csurface_t *s) { struct sfx_s *sfx; // char buff[64]; int i = randomMT() % 4; if (!p) return; switch (i) { case 1: sfx = le_sfx_gibimp1; break; case 2: sfx = le_sfx_gibimp2; break; default: sfx = le_sfx_gibimp3; break; } if (le->velocity[2] <= 0) le->touch = NULL; S_StartSound (le->ent.origin, 0, 0, sfx, 1.0, ATTN_IDLE, 0); } void CL_Gibs (vec3_t origin) { localent_t *le; le = Le_Alloc (); if (le) { le->classname = "gibs"; le->movetype = MOVETYPE_TOSS; le->nextthink = cl.time + 14000 + crand() * 16000; le->think = Le_Free; le->touch = CL_Gib_Touch; le->ent.model = le_mod_gibs; FastVectorCopy (*origin, le->ent.origin); FastVectorCopy (*origin, le->ent.oldorigin); le->velocity[0] = crand() * 15; le->velocity[1] = crand() * 15; le->velocity[2] = crand() * 15 + 25; le->ent.angles[0] = crand() * 360; le->ent.angles[1] = crand() * 360; le->ent.angles[2] = crand() * 360; } } //r1: throw some local ent debris void CL_ExplosionDebris (vec3_t origin) { int r; localent_t *le; for (r = 0; r < 10; r++) { le = Le_Alloc (); if (le) { le->classname = "explosion debris"; le->movetype = MOVETYPE_BOUNCE; le->nextthink = cl.time + 2000 + crand() * 2000; le->think = Le_Free; le->ent.model = le_mod_debris1; FastVectorCopy (*origin, le->ent.origin); FastVectorCopy (*origin, le->ent.oldorigin); le->velocity[0] = crand() * 25; le->velocity[1] = crand() * 25; le->velocity[2] = crand() * 25 + 25; le->ent.angles[0] = crand() * 360; le->ent.angles[1] = crand() * 360; le->ent.angles[2] = crand() * 360; } else { break; } } } /* ================= 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; int magnitude; type = MSG_ReadByte (&net_message); switch (type) { case TE_BLOOD: // bullet hitting flesh if (net_message.readcount + 7 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_BLOOD"); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); CL_ParticleEffect (pos, dir, 0xe8, 60); CL_Gibs (pos); break; case TE_GUNSHOT: // bullet hitting wall case TE_SPARKS: case TE_BULLET_SPARKS: if (net_message.readcount + 7 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_GUNSHOT/TE_SPARKS/TE_BULLET_SPARKS"); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, 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 = randomMT()&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: if (net_message.readcount + 7 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_SCREEN_SPARKS/TE_SHIELD_SPARKS"); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, 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 if (net_message.readcount + 7 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_SHOTGUN"); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); CL_ParticleEffect (pos, dir, 0, 20); CL_SmokeAndFlash(pos); break; case TE_SPLASH: // bullet hitting water if (net_message.readcount + 9 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_SPLASH"); cnt = MSG_ReadByte (&net_message); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); r = MSG_ReadByte (&net_message); if (r > 6) color = 0x00; else color = splash_color[r]; CL_ParticleEffect (pos, dir, color, cnt); if (r == SPLASH_SPARKS) { r = randomMT() & 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: if (net_message.readcount + 9 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_LASER_SPARKS"); cnt = MSG_ReadByte (&net_message); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); color = MSG_ReadByte (&net_message); CL_ParticleEffect2 (pos, dir, color, cnt); break; // RAFAEL case TE_BLUEHYPERBLASTER: if (net_message.readcount + 12 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_BLUEHYPERBLASTER"); MSG_ReadPos (&net_message, pos); MSG_ReadPos (&net_message, dir); CL_BlasterParticles (pos, dir); break; case TE_BLASTER: // blaster hitting wall if (net_message.readcount + 7 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_BLASTER"); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); CL_BlasterParticles (pos, dir); ex = CL_AllocExplosion (); FastVectorCopy (pos, ex->ent.origin); ex->ent.angles[0] = (float)acos(dir[2])/M_PI*180; // PMM - fixed to correct for pitch of 0 if (dir[0]) ex->ent.angles[1] = (float)atan2(dir[1], dir[0])/M_PI*180; else if (FLOAT_GT_ZERO(dir[1])) ex->ent.angles[1] = 90; else if (FLOAT_LT_ZERO(dir[1])) 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.frame.servertime - 100.0f; 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_IDLE, 0); break; case TE_RAILTRAIL: // railgun effect if (net_message.readcount + 12 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_RAILTRAIL"); MSG_ReadPos (&net_message, pos); MSG_ReadPos (&net_message, pos2); switch (cl_railtrail->intvalue) { case 0: CL_RailTrail (pos, pos2, 179); break; case 1: CL_ParseLaser (0xf2f2f0f0, pos, pos2); break; case 2: CL_ParseLaser (0xd0d1d2d3, pos, pos2); break; case 3: CL_ParseLaser (0xf3f3f1f1, pos, pos2); break; case 4: CL_ParseLaser (0xdcdddedf, pos, pos2); break; case 5: CL_ParseLaser (0xe0e1e2e3, pos, pos2); break; default: CL_RailTrail (pos, pos2, (byte)(cl_railtrail->intvalue - 6)); break; } S_StartSound (pos2, 0, 0, cl_sfx_railg, 1, ATTN_NORM, 0); break; case TE_EXPLOSION2: case TE_GRENADE_EXPLOSION: case TE_GRENADE_EXPLOSION_WATER: if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_EXPLOSION2/TE_GRENADE_EXPLOSION/TE_GRENADE_EXPLOSION_WATER"); MSG_ReadPos (&net_message, pos); ex = CL_AllocExplosion (); FastVectorCopy (pos, ex->ent.origin); ex->type = ex_poly; ex->ent.flags = RF_FULLBRIGHT; ex->start = cl.frame.servertime - 100.0f; 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] = (float)(randomMT() % 360); 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; // RAFAEL case TE_PLASMA_EXPLOSION: if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_PLASMA_EXPLOSION"); MSG_ReadPos (&net_message, pos); ex = CL_AllocExplosion (); FastVectorCopy (pos, ex->ent.origin); ex->type = ex_poly; ex->ent.flags = RF_FULLBRIGHT; ex->start = cl.frame.servertime - 100.0f; ex->light = 350; ex->lightcolor[0] = 1.0; ex->lightcolor[1] = 0.5; ex->lightcolor[2] = 0.5; ex->ent.angles[1] = (float)(randomMT() % 360); ex->ent.model = cl_mod_explo4; if (frand() < 0.5f) ex->baseframe = 15; ex->frames = 15; CL_ExplosionParticles (pos); S_StartSound (pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0); break; case TE_EXPLOSION1: case TE_EXPLOSION1_BIG: // PMM case TE_ROCKET_EXPLOSION: case TE_ROCKET_EXPLOSION_WATER: case TE_EXPLOSION1_NP: // PMM if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_EXPLOSION %d", type); MSG_ReadPos (&net_message, pos); ex = CL_AllocExplosion (); FastVectorCopy (pos, ex->ent.origin); ex->type = ex_poly; ex->ent.flags = RF_FULLBRIGHT; ex->start = cl.frame.servertime - 100.0f; ex->light = 350; ex->lightcolor[0] = 1.0; ex->lightcolor[1] = 0.5; ex->lightcolor[2] = 0.5; ex->ent.angles[1] = (float)(randomMT() % 360); if (type != TE_EXPLOSION1_BIG) { // PMM ex->ent.model = cl_mod_explo4; // PMM } else { if (!cl_mod_explo4_big) { cl_mod_explo4_big = re.RegisterModel ("models/objects/r_explode2/tris.md2"); //if (!cl_mod_explo4_big) // Com_Error (ERR_DROP, "Couldn't load models/objects/r_explode2/tris.md2"); } ex->ent.model = cl_mod_explo4_big; } if (frand() < 0.5f) ex->baseframe = 15; ex->frames = 15; if ((type != TE_EXPLOSION1_BIG) && (type != TE_EXPLOSION1_NP)) // PMM CL_ExplosionParticles (pos); // PMM 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); CL_ExplosionDebris (pos); break; case TE_BFG_EXPLOSION: if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_BFG_EXPLOSION"); MSG_ReadPos (&net_message, pos); ex = CL_AllocExplosion (); FastVectorCopy (pos, ex->ent.origin); ex->type = ex_poly; ex->ent.flags = RF_FULLBRIGHT; ex->start = cl.frame.servertime - 100.0f; 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.30f; ex->frames = 4; break; case TE_BFG_BIGEXPLOSION: if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_BFG_BIGEXPLOSION"); MSG_ReadPos (&net_message, pos); CL_BFGExplosionParticles (pos); break; case TE_BFG_LASER: if (net_message.readcount + 12 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_BFG_LASER"); MSG_ReadPos (&net_message, pos); MSG_ReadPos (&net_message, pos2); CL_ParseLaser (0xd0d1d2d3, pos, pos2); break; case TE_BUBBLETRAIL: if (net_message.readcount + 12 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_BUBBLETRAIL"); MSG_ReadPos (&net_message, pos); MSG_ReadPos (&net_message, pos2); CL_BubbleTrail (pos, pos2); break; case TE_PARASITE_ATTACK: case TE_MEDIC_CABLE_ATTACK: if (net_message.readcount + 12 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_PARASITE_ATTACK/TE_MEDIC_CABLE_ATTACK"); CL_ParseBeam (cl_mod_parasite_segment); break; case TE_BOSSTPORT: // boss teleporting to station if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_BOSSTPORT"); MSG_ReadPos (&net_message, pos); CL_BigTeleportParticles (pos); S_StartSound (pos, 0, 0, S_RegisterSound ("misc/bigtele.wav"), 1, ATTN_NONE, 0); break; case TE_GRAPPLE_CABLE: if (net_message.readcount + 20 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_GRAPPLE_CABLE"); CL_ParseBeam2 (cl_mod_grapple_cable); break; // RAFAEL case TE_WELDING_SPARKS: if (net_message.readcount + 9 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_WELDING_SPARKS"); cnt = MSG_ReadByte (&net_message); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); color = MSG_ReadByte (&net_message); CL_ParticleEffect2 (pos, dir, color, cnt); ex = CL_AllocExplosion (); FastVectorCopy (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.frame.servertime - 0.1f; ex->light = 100 + (float)(randomMT()%75); ex->lightcolor[0] = 1.0f; ex->lightcolor[1] = 1.0f; ex->lightcolor[2] = 0.3f; ex->ent.model = cl_mod_flash; ex->frames = 2; break; case TE_GREENBLOOD: if (net_message.readcount + 7 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_GREENBLOOD"); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); CL_ParticleEffect2 (pos, dir, 0xdf, 30); break; // RAFAEL case TE_TUNNEL_SPARKS: if (net_message.readcount + 9 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_TUNNEL_SPARKS"); cnt = MSG_ReadByte (&net_message); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); color = MSG_ReadByte (&net_message); 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 if (net_message.readcount + 7 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_BLASTER2/TE_FLECHETTE"); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); // PMM if (type == TE_BLASTER2) CL_BlasterParticles2 (pos, dir, 0xd0); else CL_BlasterParticles2 (pos, dir, 0x6f); // 75 ex = CL_AllocExplosion (); FastVectorCopy (pos, ex->ent.origin); ex->ent.angles[0] = (float)acos(dir[2])/M_PI*180; // PMM - fixed to correct for pitch of 0 if (dir[0]) ex->ent.angles[1] = (float)atan2(dir[1], dir[0])/M_PI*180; else if (FLOAT_GT_ZERO(dir[1])) ex->ent.angles[1] = 90; else if (FLOAT_LT_ZERO(dir[1])) 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.frame.servertime - 100.0f; ex->light = 150; // PMM if (type == TE_BLASTER2) ex->lightcolor[1] = 1; else // flechette { ex->lightcolor[0] = 0.19f; ex->lightcolor[1] = 0.41f; ex->lightcolor[2] = 0.75f; } 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: if (net_message.readcount + 16 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_LIGHTNING"); if (!cl_mod_lightning) { cl_mod_lightning = re.RegisterModel ("models/proj/lightning/tris.md2"); //if (!cl_mod_lightning) // Com_Error (ERR_DROP, "Couldn't load models/proj/lightning/tris.md2"); } ent = CL_ParseLightning (cl_mod_lightning); S_StartSound (NULL, ent, CHAN_WEAPON, cl_sfx_lightning, 1, ATTN_NORM, 0); break; case TE_DEBUGTRAIL: if (net_message.readcount + 12 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_DEBUGTRAIL"); MSG_ReadPos (&net_message, pos); MSG_ReadPos (&net_message, pos2); CL_DebugTrail (pos, pos2); break; case TE_PLAIN_EXPLOSION: if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_PLAIN_EXPLOSION"); MSG_ReadPos (&net_message, pos); ex = CL_AllocExplosion (); FastVectorCopy (pos, ex->ent.origin); ex->type = ex_poly; ex->ent.flags = RF_FULLBRIGHT; ex->start = cl.frame.servertime - 100.0f; ex->light = 350; ex->lightcolor[0] = 1.0; ex->lightcolor[1] = 0.5; ex->lightcolor[2] = 0.5; ex->ent.angles[1] = (float)(randomMT() % 360); ex->ent.model = cl_mod_explo4; if (frand() < 0.5f) ex->baseframe = 15; ex->frames = 15; 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_FLASHLIGHT: if (net_message.readcount + 8 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_FLASHLIGHT"); MSG_ReadPos(&net_message, pos); ent = MSG_ReadShort(&net_message); CL_Flashlight(ent, pos); break; case TE_FORCEWALL: if (net_message.readcount + 13 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_FORCEWALL"); MSG_ReadPos(&net_message, pos); MSG_ReadPos(&net_message, pos2); color = MSG_ReadByte (&net_message); CL_ForceWall(pos, pos2, color); break; case TE_HEATBEAM: CL_ParsePlayerBeam (TE_HEATBEAM); break; case TE_MONSTER_HEATBEAM: CL_ParsePlayerBeam (TE_MONSTER_HEATBEAM); break; case TE_HEATBEAM_SPARKS: if (net_message.readcount + 7 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_HEATBEAM_SPARKS"); cnt = 50; MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); r = 8; magnitude = 60; color = r & 0xff; CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude); S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_HEATBEAM_STEAM: if (net_message.readcount + 7 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_HEATBEAM_STEAM"); cnt = 20; MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); color = 0xe0; magnitude = 60; CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude); S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_STEAM: CL_ParseSteam(); break; case TE_BUBBLETRAIL2: if (net_message.readcount + 12 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_BUBBLETRAIL2"); MSG_ReadPos (&net_message, pos); MSG_ReadPos (&net_message, pos2); CL_BubbleTrail2 (pos, pos2, 8); S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_MOREBLOOD: if (net_message.readcount + 7 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_MOREBLOOD"); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); CL_ParticleEffect (pos, dir, 0xe8, 250); break; case TE_CHAINFIST_SMOKE: if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_CHAINFIST_SMOKE"); dir[0]=0; dir[1]=0; dir[2]=1; MSG_ReadPos(&net_message, pos); CL_ParticleSmokeEffect (pos, dir, 0, 20, 20); break; case TE_ELECTRIC_SPARKS: if (net_message.readcount + 7 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_ELECTRIC_SPARKS"); MSG_ReadPos (&net_message, pos); MSG_ReadDir (&net_message, dir); // CL_ParticleEffect (pos, dir, 109, 40); 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: if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_TRACKER_EXPLOSION"); MSG_ReadPos (&net_message, pos); CL_ColorFlash (pos, 0, 150, -1, -1, -1); CL_ColorExplosionParticles (pos, 0, 1); // CL_Tracker_Explode (pos); S_StartSound (pos, 0, 0, cl_sfx_disrexp, 1, ATTN_NORM, 0); break; case TE_TELEPORT_EFFECT: case TE_DBALL_GOAL: if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_TELEPORT_EFFECT/TE_DBALL_GOAL"); MSG_ReadPos (&net_message, pos); CL_TeleportParticles (pos); break; case TE_WIDOWBEAMOUT: if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_WIDOWBEAMOUT"); CL_ParseWidow (); break; case TE_NUKEBLAST: if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_NUKEBLAST"); CL_ParseNuke (); break; case TE_WIDOWSPLASH: if (net_message.readcount + 6 > net_message.cursize) Com_Error (ERR_DROP, "CL_ParseTEnt: Insufficient bytes for TE_WIDOWSPLASH"); MSG_ReadPos (&net_message, pos); CL_WidowSplash (pos); break; //PGM //============== default: #ifdef _DEBUG Com_Printf ("Tempent bytes: %d,%d,%d,%d,%d,%d,%d\n", LOG_CLIENT, MSG_ReadByte (&net_message), MSG_ReadByte (&net_message), MSG_ReadByte (&net_message), MSG_ReadByte (&net_message), MSG_ReadByte (&net_message), MSG_ReadByte (&net_message), MSG_ReadByte (&net_message)); #endif Com_Error (ERR_DROP, "CL_ParseTEnt: bad type (0x%x)", 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.playernum+1) // entity 0 is the world { FastVectorCopy (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 (FLOAT_GT_ZERO(dist[2])) pitch = 90; else pitch = 270; } else { // PMM - fixed to correct for pitch of 0 if (FLOAT_NE_ZERO(dist[0])) yaw = ((float)atan2(dist[1], dist[0]) * 180 / M_PI); else if (FLOAT_GT_ZERO(dist[1])) yaw = 90; else yaw = 270; if (FLOAT_LT_ZERO(yaw)) yaw += 360; forward = (float)sqrt (dist[0]*dist[0] + dist[1]*dist[1]); pitch = ((float)atan2(dist[2], forward) * -180.0f / M_PI); if (FLOAT_LT_ZERO(pitch)) 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 = (float)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)) { FastVectorCopy (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] = (float)(randomMT()%360); V_AddEntity (&ent); return; } while (FLOAT_GT_ZERO(d)) { FastVectorCopy (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.0f; ent.angles[2] = (float)(randomMT()%360); } else { ent.angles[0] = pitch; ent.angles[1] = yaw; ent.angles[2] = (float)(randomMT()%360); } 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_new *ps, *ops; //PMM if (hand) { if (hand->intvalue == 2) hand_multiplier = 0; else if (hand->intvalue == 1) hand_multiplier = -1; else 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.playernum+1) // entity 0 is the world { // set up gun position // code straight out of CL_AddViewWeapon ps = &cl.frame.playerstate; j = (cl.frame.serverframe - 1) & UPDATE_MASK; oldframe = &cl.frames[j]; if (oldframe->serverframe != cl.frame.serverframe-1 || !oldframe->valid) oldframe = &cl.frame; // previous frame was dropped or involid ops = &oldframe->playerstate; 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 ((hand) && (hand->intvalue == 2)) { VectorMA (org, -1, cl.v_up, org); } // FIXME - take these out when final FastVectorCopy (cl.v_right, r); FastVectorCopy (cl.v_forward, f); FastVectorCopy (cl.v_up, u); } else FastVectorCopy (b->start, org); } else { // if coming from the player, update the start position if (b->entity == cl.playernum+1) // entity 0 is the world { FastVectorCopy (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.playernum+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 ((hand) && (hand->intvalue == 2)) { VectorMA (org, -1, cl.v_up, org); } } //PMM if (FLOAT_EQ_ZERO(dist[1]) && FLOAT_EQ_ZERO(dist[0])) { yaw = 0; if (FLOAT_GT_ZERO(dist[2])) pitch = 90; else pitch = 270; } else { // PMM - fixed to correct for pitch of 0 if (FLOAT_NE_ZERO(dist[0])) yaw = ((float)atan2(dist[1], dist[0]) * 180 / M_PI); else if (FLOAT_GT_ZERO(dist[1])) yaw = 90; else yaw = 270; if (FLOAT_LT_ZERO(yaw)) yaw += 360; forward = (float)sqrt (dist[0]*dist[0] + dist[1]*dist[1]); pitch = ((float)atan2(dist[2], forward) * -180.0f / M_PI); if (FLOAT_LT_ZERO(pitch)) pitch += 360.0; } if (cl_mod_heatbeam && (b->model == cl_mod_heatbeam)) { if (b->entity != cl.playernum+1) { framenum = 2; ent.angles[0] = -pitch; ent.angles[1] = yaw + 180.0f; ent.angles[2] = 0; 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.playernum+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.0f; } else if (b->model == cl_mod_lightning) { model_length = 35.0f; d-= 20.0f; // correction so it doesn't end in middle of tesla } else { model_length = 30.0f; } steps = (float)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"); FastVectorCopy (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] = (float)(randomMT()%360); V_AddEntity (&ent); return; } while (FLOAT_GT_ZERO(d)) { FastVectorCopy (org, ent.origin); ent.model = b->model; if(cl_mod_heatbeam && (b->model == cl_mod_heatbeam)) { ent.flags = RF_FULLBRIGHT; ent.angles[0] = -pitch; ent.angles[1] = yaw + 180.0f; ent.angles[2] = (float)((cl.time) % 360); ent.frame = framenum; } else if (b->model == cl_mod_lightning) { ent.flags = RF_FULLBRIGHT; ent.angles[0] = -pitch; ent.angles[1] = yaw + 180.0f; ent.angles[2] = (float)(randomMT() % 360); } else { ent.angles[0] = pitch; ent.angles[1] = yaw; ent.angles[2] = (float)(randomMT() % 360); } V_AddEntity (&ent); org[0] += dist[0]*len; org[1] += dist[1]*len; org[2] += dist[2]*len; d -= model_length; } } } /* ================= CL_AddLasers ================= */ void CL_AddLasers (void) { laser_t *l; int i; for (i=0, l=cl_lasers ; i< MAX_LASERS ; i++, l++) { if (l->endtime >= cl.time) V_AddEntity (&l->ent); } } /* PMM - CL_Sustains */ void CL_ProcessSustain () { 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)) { s->think (s); } else if (s->endtime < cl.time) { s->id = 0; } } } } /* ================= CL_AddExplosions ================= */ void CL_AddExplosions (void) { entity_t *ent; int i, f; explosion_t *ex; float frac; 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.0f; f = (int)floor(frac); ent = &ex->ent; switch (ex->type) { case ex_free: case ex_explosion: default: break; case ex_misc: if (f >= ex->frames-1) { ex->type = ex_free; continue; } ent->alpha = 1.0f - frac/(ex->frames-1); break; case ex_flash: if (f >= 1) { ex->type = ex_free; continue; } ent->alpha = 1.0; break; case ex_mflash: if (f >= ex->frames-1) ex->type = ex_free; break; case ex_poly: if (f >= ex->frames-1) { ex->type = ex_free; continue; } ent->alpha = (16.0f - (float)f)/16.0f; 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; continue; } ent->alpha = (5.0f - (float)f)/5.0f; ent->skinnum = 0; ent->flags |= RF_TRANSLUCENT; break; } if (FLOAT_NE_ZERO(ex->light)) { V_AddLight (ent->origin, ex->light*ent->alpha, ex->lightcolor[0], ex->lightcolor[1], ex->lightcolor[2]); } FastVectorCopy (ent->origin, ent->oldorigin); if (f < 0) f = 0; ent->frame = ex->baseframe + f + 1; ent->oldframe = ex->baseframe + f; ent->backlerp = 1.0f - cl.lerpfrac; V_AddEntity (ent); } } /* ================= CL_AddTEnts ================= */ void CL_AddTEnts (void) { CL_AddBeams (); // PMM - draw plasma beams CL_AddPlayerBeams (); CL_AddExplosions (); CL_AddLasers (); // PMM - set up sustain CL_ProcessSustain(); }