960 lines
21 KiB
C
960 lines
21 KiB
C
/*
|
|
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"
|
|
|
|
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 32
|
|
explosion_t cl_explosions[MAX_EXPLOSIONS];
|
|
|
|
|
|
#define MAX_BEAMS 32
|
|
typedef struct
|
|
{
|
|
int entity;
|
|
int dest_entity;
|
|
model_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];
|
|
|
|
|
|
#define MAX_LASERS 32
|
|
typedef struct
|
|
{
|
|
entity_t ent;
|
|
int endtime;
|
|
} laser_t;
|
|
laser_t cl_lasers[MAX_LASERS];
|
|
|
|
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);
|
|
|
|
sound_t cl_sfx_ric1;
|
|
sound_t cl_sfx_ric2;
|
|
sound_t cl_sfx_ric3;
|
|
sound_t cl_sfx_lashit;
|
|
sound_t cl_sfx_spark5;
|
|
sound_t cl_sfx_spark6;
|
|
sound_t cl_sfx_spark7;
|
|
sound_t cl_sfx_railg;
|
|
sound_t cl_sfx_rockexp;
|
|
sound_t cl_sfx_grenexp;
|
|
sound_t cl_sfx_watrexp;
|
|
// RAFAEL
|
|
sound_t cl_sfx_plasexp;
|
|
sound_t cl_sfx_footsteps[4];
|
|
|
|
model_t *cl_mod_explode;
|
|
model_t *cl_mod_smoke;
|
|
model_t *cl_mod_flash;
|
|
model_t *cl_mod_laser;
|
|
model_t *cl_mod_explo2;
|
|
model_t *cl_mod_explo4;
|
|
model_t *cl_mod_bfg_explo;
|
|
model_t *cl_mod_plasmaexplo;
|
|
|
|
/*
|
|
=================
|
|
CL_RegisterTEntSounds
|
|
=================
|
|
*/
|
|
void CL_RegisterTEntSounds (void)
|
|
{
|
|
int i;
|
|
char name[MAX_QPATH];
|
|
|
|
// PMM - version stuff
|
|
// 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++)
|
|
{
|
|
sprintf (name, "player/step%i.wav", i+1);
|
|
cl_sfx_footsteps[i] = S_RegisterSound (name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_RegisterTEntModels
|
|
=================
|
|
*/
|
|
void CL_RegisterTEntModels (void)
|
|
{
|
|
cl_mod_explode = re->RegisterModel ("sprites/explode01.spr");
|
|
cl_mod_smoke = re->RegisterModel ("sprites/blacksmoke.spr");
|
|
cl_mod_flash = re->RegisterModel ("sprites/s_flash.spr");
|
|
cl_mod_laser = re->RegisterModel ("sprite/laserbeam.spr");
|
|
cl_mod_explo2 = re->RegisterModel ("sprites/s_explo2.spr");
|
|
cl_mod_explo4 = re->RegisterModel ("sprites/s_explod.spr");
|
|
cl_mod_bfg_explo = re->RegisterModel ("sprites/s_bfg2.spr");
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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));
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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.frame.servertime - 100;
|
|
ex->ent.model = cl_mod_smoke;
|
|
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (origin, ex->ent.origin);
|
|
ex->type = ex_flash;
|
|
ex->ent.flags = RF_TRANSLUCENT;
|
|
ex->frames = 2;
|
|
ex->start = cl.frame.servertime - 100;
|
|
ex->ent.model = cl_mod_flash;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_ParseParticles
|
|
=================
|
|
*/
|
|
void CL_ParseParticles (void)
|
|
{
|
|
int color, count;
|
|
vec3_t pos, dir;
|
|
|
|
MSG_ReadPos32(&net_message, pos);
|
|
MSG_ReadPos32(&net_message, dir);
|
|
|
|
color = MSG_ReadByte(&net_message);
|
|
count = MSG_ReadByte(&net_message);
|
|
|
|
CL_ParticleEffect(pos, dir, color, count);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_ParseBeam
|
|
=================
|
|
*/
|
|
int CL_ParseBeam (model_t *model)
|
|
{
|
|
int ent;
|
|
vec3_t start, end;
|
|
beam_t *b;
|
|
int i;
|
|
|
|
ent = MSG_ReadShort (&net_message);
|
|
|
|
MSG_ReadPos32(&net_message, start);
|
|
MSG_ReadPos32(&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;
|
|
VectorCopy (start, b->start);
|
|
VectorCopy (end, b->end);
|
|
VectorClear (b->offset);
|
|
return ent;
|
|
}
|
|
|
|
// 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 ent;
|
|
}
|
|
}
|
|
Msg ("beam list overflow!\n");
|
|
return ent;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_ParseBeam2
|
|
=================
|
|
*/
|
|
int CL_ParseBeam2 (model_t *model)
|
|
{
|
|
int ent;
|
|
vec3_t start, end, offset;
|
|
beam_t *b;
|
|
int i;
|
|
|
|
ent = MSG_ReadShort (&net_message);
|
|
|
|
MSG_ReadPos32(&net_message, start);
|
|
MSG_ReadPos32(&net_message, end);
|
|
MSG_ReadPos32(&net_message, offset);
|
|
|
|
// Msg ("end- %f %f %f\n", end[0], end[1], end[2]);
|
|
|
|
// 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 ent;
|
|
}
|
|
|
|
// 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 ent;
|
|
}
|
|
}
|
|
Msg ("beam list overflow!\n");
|
|
return ent;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_ParseLightning
|
|
=================
|
|
*/
|
|
int CL_ParseLightning (model_t *model)
|
|
{
|
|
int srcEnt, destEnt;
|
|
vec3_t start, end;
|
|
beam_t *b;
|
|
int i;
|
|
|
|
srcEnt = MSG_ReadShort (&net_message);
|
|
destEnt = MSG_ReadShort (&net_message);
|
|
|
|
MSG_ReadPos32(&net_message, start);
|
|
MSG_ReadPos32(&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)
|
|
{
|
|
// Msg("%d: OVERRIDE %d -> %d\n", cl.time, srcEnt, 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 srcEnt;
|
|
}
|
|
|
|
// find a free beam
|
|
for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++)
|
|
{
|
|
if (!b->model || b->endtime < cl.time)
|
|
{
|
|
// Msg("%d: NORMAL %d -> %d\n", cl.time, srcEnt, 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 srcEnt;
|
|
}
|
|
}
|
|
Msg ("beam list overflow!\n");
|
|
return srcEnt;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_ParseLaser
|
|
=================
|
|
*/
|
|
void CL_ParseLaser (int colors)
|
|
{
|
|
vec3_t start;
|
|
vec3_t end;
|
|
laser_t *l;
|
|
int i;
|
|
|
|
MSG_ReadPos32(&net_message, start);
|
|
MSG_ReadPos32(&net_message, end);
|
|
|
|
for (i=0, l=cl_lasers ; i< MAX_LASERS ; i++, l++)
|
|
{
|
|
if (l->endtime < cl.time)
|
|
{
|
|
l->ent.flags = RF_TRANSLUCENT | RF_BEAM;
|
|
VectorCopy (start, l->ent.origin);
|
|
VectorCopy (end, l->ent.oldorigin);
|
|
l->ent.alpha = 0.30;
|
|
l->ent.skinnum = (colors >> ((rand() % 4)*8)) & 0xff;
|
|
l->ent.model = NULL;
|
|
l->ent.frame = 4;
|
|
l->endtime = cl.time + 100;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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 (&net_message);
|
|
|
|
switch (type)
|
|
{
|
|
case TE_BLOOD: // bullet hitting flesh
|
|
MSG_ReadPos32(&net_message, pos);
|
|
MSG_ReadPos32(&net_message, dir);
|
|
CL_ParticleEffect (pos, dir, 0xe8, 60);
|
|
break;
|
|
|
|
case TE_GUNSHOT: // bullet hitting wall
|
|
case TE_SPARKS:
|
|
case TE_BULLET_SPARKS:
|
|
MSG_ReadPos32(&net_message, pos);
|
|
MSG_ReadPos32(&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 = rand()&15;
|
|
if (cnt == 1)
|
|
S_StartSound (pos, 0, 0, cl_sfx_ric1);
|
|
else if (cnt == 2)
|
|
S_StartSound (pos, 0, 0, cl_sfx_ric2);
|
|
else if (cnt == 3)
|
|
S_StartSound (pos, 0, 0, cl_sfx_ric3);
|
|
}
|
|
|
|
break;
|
|
|
|
case TE_SCREEN_SPARKS:
|
|
case TE_SHIELD_SPARKS:
|
|
MSG_ReadPos32(&net_message, pos);
|
|
MSG_ReadPos32(&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);
|
|
break;
|
|
|
|
case TE_SHOTGUN: // bullet hitting wall
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
MSG_ReadPos32 (&net_message, dir);
|
|
CL_ParticleEffect (pos, dir, 0, 20);
|
|
CL_SmokeAndFlash(pos);
|
|
break;
|
|
|
|
case TE_SPLASH: // bullet hitting water
|
|
cnt = MSG_ReadByte (&net_message);
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
MSG_ReadPos32 (&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 = rand() & 3;
|
|
if (r == 0)
|
|
S_StartSound (pos, 0, 0, cl_sfx_spark5);
|
|
else if (r == 1)
|
|
S_StartSound (pos, 0, 0, cl_sfx_spark6);
|
|
else
|
|
S_StartSound (pos, 0, 0, cl_sfx_spark7);
|
|
}
|
|
break;
|
|
|
|
case TE_LASER_SPARKS:
|
|
cnt = MSG_ReadByte (&net_message);
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
MSG_ReadPos32 (&net_message, dir);
|
|
color = MSG_ReadByte (&net_message);
|
|
CL_ParticleEffect2 (pos, dir, color, cnt);
|
|
break;
|
|
|
|
// RAFAEL
|
|
case TE_BLUEHYPERBLASTER:
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
MSG_ReadPos32 (&net_message, dir);
|
|
CL_BlasterParticles (pos, dir);
|
|
break;
|
|
|
|
case TE_BLASTER: // blaster hitting wall
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
MSG_ReadPos32 (&net_message, 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.frame.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);
|
|
break;
|
|
|
|
case TE_RAILTRAIL: // railgun effect
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
MSG_ReadPos32 (&net_message, pos2);
|
|
CL_RailTrail (pos, pos2);
|
|
S_StartSound (pos2, 0, 0, cl_sfx_railg);
|
|
break;
|
|
|
|
case TE_EXPLOSION2:
|
|
case TE_GRENADE_EXPLOSION:
|
|
case TE_GRENADE_EXPLOSION_WATER:
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->ent.origin);
|
|
ex->type = ex_poly;
|
|
ex->ent.flags = RF_FULLBRIGHT;
|
|
ex->start = cl.frame.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_explo2;
|
|
ex->frames = 11;
|
|
ex->baseframe = 0;
|
|
ex->ent.angles[1] = rand() % 360;
|
|
CL_ExplosionParticles (pos);
|
|
if (type == TE_GRENADE_EXPLOSION_WATER)
|
|
S_StartSound (pos, 0, 0, cl_sfx_watrexp);
|
|
else
|
|
S_StartSound (pos, 0, 0, cl_sfx_grenexp);
|
|
break;
|
|
|
|
// RAFAEL
|
|
case TE_PLASMA_EXPLOSION:
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->ent.origin);
|
|
ex->type = ex_poly;
|
|
ex->ent.flags = RF_FULLBRIGHT;
|
|
ex->start = cl.frame.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;
|
|
ex->baseframe = 0;
|
|
ex->frames = 6;
|
|
CL_ExplosionParticles (pos);
|
|
S_StartSound (pos, 0, 0, cl_sfx_rockexp);
|
|
break;
|
|
|
|
case TE_EXPLOSION1:
|
|
case TE_ROCKET_EXPLOSION:
|
|
case TE_ROCKET_EXPLOSION_WATER:
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->ent.origin);
|
|
ex->type = ex_poly;
|
|
ex->ent.flags = RF_FULLBRIGHT;
|
|
ex->start = cl.frame.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; // PMM
|
|
ex->baseframe = 0;
|
|
ex->frames = 8;
|
|
CL_ExplosionParticles (pos); // PMM
|
|
if (type == TE_ROCKET_EXPLOSION_WATER)
|
|
S_StartSound (pos, 0, 0, cl_sfx_watrexp);
|
|
else
|
|
S_StartSound (pos, 0, 0, cl_sfx_rockexp);
|
|
break;
|
|
|
|
case TE_BFG_EXPLOSION:
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->ent.origin);
|
|
ex->type = ex_poly;
|
|
ex->ent.flags = RF_FULLBRIGHT;
|
|
ex->start = cl.frame.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_ReadPos32 (&net_message, pos);
|
|
CL_BFGExplosionParticles (pos);
|
|
break;
|
|
|
|
case TE_BFG_LASER:
|
|
CL_ParseLaser (0xd0d1d2d3);
|
|
break;
|
|
|
|
case TE_BUBBLETRAIL:
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
MSG_ReadPos32 (&net_message, pos2);
|
|
CL_BubbleTrail (pos, pos2);
|
|
break;
|
|
|
|
case TE_PARASITE_ATTACK:
|
|
case TE_MEDIC_CABLE_ATTACK:
|
|
ent = CL_ParseBeam (cl_mod_laser);
|
|
break;
|
|
|
|
case TE_BOSSTPORT: // boss teleporting to station
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
CL_BigTeleportParticles (pos);
|
|
S_StartSound (pos, 0, 0, S_RegisterSound ("misc/bigtele.wav"));
|
|
break;
|
|
|
|
case TE_GRAPPLE_CABLE:
|
|
break;
|
|
|
|
// RAFAEL
|
|
case TE_WELDING_SPARKS:
|
|
cnt = MSG_ReadByte (&net_message);
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
MSG_ReadPos32 (&net_message, dir);
|
|
color = MSG_ReadByte (&net_message);
|
|
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.frame.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_ReadPos32 (&net_message, pos);
|
|
MSG_ReadPos32 (&net_message, dir);
|
|
CL_ParticleEffect2 (pos, dir, 0xdf, 30);
|
|
break;
|
|
|
|
case TE_FLASHLIGHT:
|
|
MSG_ReadPos32(&net_message, pos);
|
|
ent = MSG_ReadShort(&net_message);
|
|
CL_Flashlight(ent, pos);
|
|
break;
|
|
|
|
case TE_DEBUGTRAIL:
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
MSG_ReadPos32 (&net_message, pos2);
|
|
CL_DebugTrail (pos, pos2);
|
|
break;
|
|
|
|
// RAFAEL
|
|
case TE_TUNNEL_SPARKS:
|
|
cnt = MSG_ReadByte (&net_message);
|
|
MSG_ReadPos32 (&net_message, pos);
|
|
MSG_ReadPos32 (&net_message, dir);
|
|
color = MSG_ReadByte (&net_message);
|
|
CL_ParticleEffect3 (pos, dir, color, cnt);
|
|
break;
|
|
default:
|
|
Host_Error("CL_ParseTEnt: bad type\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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
|
|
{
|
|
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));
|
|
model_length = 30.0;
|
|
steps = ceil(d/model_length);
|
|
len = (d-model_length)/(steps-1);
|
|
|
|
while (d > 0)
|
|
{
|
|
VectorCopy (org, ent.origin);
|
|
ent.model = b->model;
|
|
ent.angles[0] = pitch;
|
|
ent.angles[1] = yaw;
|
|
ent.angles[2] = rand()%360;
|
|
|
|
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:
|
|
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;
|
|
}
|
|
|
|
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]);
|
|
}
|
|
|
|
VectorCopy (ent->origin, ent->oldorigin);
|
|
|
|
if (f < 0) f = 0;
|
|
ent->frame = ex->baseframe + f + 1;
|
|
ent->prev.frame = ex->baseframe + f;
|
|
ent->backlerp = 1.0 - cl.lerpfrac;
|
|
|
|
V_AddEntity (ent);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_AddTEnts
|
|
=================
|
|
*/
|
|
void CL_AddTEnts (void)
|
|
{
|
|
CL_AddBeams ();
|
|
CL_AddExplosions ();
|
|
CL_AddLasers ();
|
|
}
|