#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;
struct model_s *model;
int endtime;
vec3_t offset;
vec3_t start, end;
} beam_t;
beam_t cl_beams[MAX_BEAMS];
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];
cl_sustain_t cl_sustains[MAX_SUSTAINS];
extern void CL_TeleportParticles(vec3_t org);
void CL_BlasterParticles(vec3_t org, vec3_t dir);
void CL_ExplosionParticles(vec3_t org);
void CL_BFGExplosionParticles(vec3_t org);
void CL_BlueBlasterParticles(vec3_t org, vec3_t dir);
struct sfx_s *cl_sfx_ric1;
struct sfx_s *cl_sfx_ric2;
struct sfx_s *cl_sfx_ric3;
struct sfx_s *cl_sfx_lashit;
struct sfx_s *cl_sfx_spark5;
struct sfx_s *cl_sfx_spark6;
struct sfx_s *cl_sfx_spark7;
struct sfx_s *cl_sfx_railg;
struct sfx_s *cl_sfx_rockexp;
struct sfx_s *cl_sfx_grenexp;
struct sfx_s *cl_sfx_watrexp;
struct sfx_s *cl_sfx_plasexp;
struct sfx_s *cl_sfx_footsteps[4];
struct model_s *cl_mod_explode;
struct model_s *cl_mod_smoke;
struct model_s *cl_mod_flash;
struct model_s *cl_mod_parasite_segment;
struct model_s *cl_mod_grapple_cable;
struct model_s *cl_mod_parasite_tip;
struct model_s *cl_mod_explo4;
struct model_s *cl_mod_bfg_explo;
struct model_s *cl_mod_powerscreen;
struct model_s *cl_mod_plasmaexplo;
struct sfx_s *cl_sfx_lightning;
struct sfx_s *cl_sfx_disrexp;
struct model_s *cl_mod_lightning;
struct model_s *cl_mod_heatbeam;
struct model_s *cl_mod_monster_heatbeam;
struct model_s *cl_mod_explo4_big;
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");
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);
}
cl_sfx_lightning = S_RegisterSound("weapons/tesla.wav");
cl_sfx_disrexp = S_RegisterSound("weapons/disrupthit.wav");
sprintf(name, sizeof(name), "weapons/sound%d.wav", ROGUE_VERSION_ID);
if(name[0] == 'w')
name[0] = 'W';
}
void CL_RegisterTEntModels(void) {
cl_mod_explode = re.RegisterModel(CMODEL_A, "models/objects/explode/tris.md2");
cl_mod_smoke = re.RegisterModel(CMODEL_A, "models/objects/smoke/tris.md2");
cl_mod_flash = re.RegisterModel(CMODEL_A, "models/objects/flash/tris.md2");
cl_mod_parasite_segment = re.RegisterModel(CMODEL_A, "models/monsters/parasite/segment/tris.md2");
cl_mod_grapple_cable = re.RegisterModel(CMODEL_A, "models/ctf/segment/tris.md2");
cl_mod_parasite_tip = re.RegisterModel(CMODEL_A, "models/monsters/parasite/tip/tris.md2");
cl_mod_explo4 = re.RegisterModel(CMODEL_A, "models/objects/r_explode/tris.md2");
cl_mod_bfg_explo = re.RegisterModel(CMODEL_A, "sprites/s_bfg2.sp2");
cl_mod_powerscreen = re.RegisterModel(CMODEL_A, "models/items/armor/effect/tris.md2");
re.RegisterModel(CMODEL_A, "models/objects/laser/tris.md2");
re.RegisterModel(CMODEL_A, "models/objects/grenade2/tris.md2");
re.RegisterModel(CMODEL_A, "models/weapons/v_machn/tris.md2");
re.RegisterModel(CMODEL_A, "models/weapons/v_handgr/tris.md2");
re.RegisterModel(CMODEL_A, "models/weapons/v_shotg2/tris.md2");
re.RegisterModel(CMODEL_A, "models/objects/gibs/bone/tris.md2");
re.RegisterModel(CMODEL_A, "models/objects/gibs/sm_meat/tris.md2");
re.RegisterModel(CMODEL_A, "models/objects/gibs/bone2/tris.md2");
re.RegisterPic("w_machinegun");
re.RegisterPic("a_bullets");
re.RegisterPic("i_health");
re.RegisterPic("a_grenades");
cl_mod_explo4_big = re.RegisterModel(CMODEL_A, "models/objects/r_explode2/tris.md2");
cl_mod_lightning = re.RegisterModel(CMODEL_A, "models/proj/lightning/tris.md2");
cl_mod_heatbeam = re.RegisterModel(CMODEL_A, "models/proj/beam/tris.md2");
cl_mod_monster_heatbeam = re.RegisterModel(CMODEL_A, "models/proj/widowbeam/tris.md2");
}
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));
memset(cl_playerbeams, 0, sizeof(cl_playerbeams));
memset(cl_sustains, 0, sizeof(cl_sustains));
}
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];
}
}
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];
}
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_FULLBRIGHT;
ex->frames = 2;
ex->start = cl.frame.servertime - 100;
ex->ent.model = cl_mod_flash;
}
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);
}
int CL_ParseBeam(struct model_s *model) {
int ent;
vec3_t start, end;
beam_t *b;
int i;
ent = MSG_ReadShort(&net_message);
MSG_ReadPos(&net_message, start);
MSG_ReadPos(&net_message, end);
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;
}
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;
}
}
Com_Printf("beam list overflow!\n");
return ent;
}
int 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);
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;
}
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;
}
}
Com_Printf("beam list overflow!\n");
return ent;
}
int CL_ParsePlayerBeam(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);
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(&net_message, offset);
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 ent;
}
}
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; VectorCopy(start, b->start);
VectorCopy(end, b->end);
VectorCopy(offset, b->offset);
return ent;
}
}
Com_Printf("beam list overflow!\n");
return ent;
}
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);
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 srcEnt;
}
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 srcEnt;
}
}
Com_Printf("beam list overflow!\n");
return srcEnt;
}
void CL_ParseLaser(int colors) {
vec3_t start;
vec3_t end;
laser_t *l;
int i;
MSG_ReadPos(&net_message, start);
MSG_ReadPos(&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;
}
}
}
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); if(id != -1) {
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 = 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 {
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); }
} else {
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);
}
}
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; i < MAX_SUSTAINS; i++, s++) {
if(s->id == 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 {
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; i < MAX_SUSTAINS; i++, s++) {
if(s->id == 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 {
MSG_ReadPos(&net_message, pos);
}
}
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: MSG_ReadPos(&net_message, pos);
MSG_ReadDir(&net_message, dir);
CL_ParticleEffect(pos, dir, 0xe8, 60);
break;
case TE_GUNSHOT: case TE_SPARKS:
case 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);
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(&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);
S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
break;
case 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: 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 = 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(&net_message);
MSG_ReadPos(&net_message, pos);
MSG_ReadDir(&net_message, dir);
color = MSG_ReadByte(&net_message);
CL_ParticleEffect2(pos, dir, color, cnt);
break;
case TE_BLUEHYPERBLASTER:
MSG_ReadPos(&net_message, pos);
MSG_ReadPos(&net_message, dir);
CL_BlasterParticles(pos, dir);
break;
case TE_BLASTER: MSG_ReadPos(&net_message, pos);
MSG_ReadDir(&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;
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, 1, ATTN_NORM, 0);
break;
case TE_RAILTRAIL: MSG_ReadPos(&net_message, pos);
MSG_ReadPos(&net_message, pos2);
CL_RailTrail(pos, pos2);
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:
MSG_ReadPos(&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_explo4;
ex->frames = 19;
ex->baseframe = 30;
ex->ent.angles[1] = rand() % 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;
case TE_PLASMA_EXPLOSION:
MSG_ReadPos(&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;
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_EXPLOSION1:
case TE_EXPLOSION1_BIG: case TE_ROCKET_EXPLOSION:
case TE_ROCKET_EXPLOSION_WATER:
case TE_EXPLOSION1_NP: MSG_ReadPos(&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;
if(type != TE_EXPLOSION1_BIG) ex->ent.model = cl_mod_explo4; else
ex->ent.model = cl_mod_explo4_big;
if(frand() < 0.5)
ex->baseframe = 15;
ex->frames = 15;
if((type != TE_EXPLOSION1_BIG) && (type != TE_EXPLOSION1_NP)) 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_BFG_EXPLOSION:
MSG_ReadPos(&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_ReadPos(&net_message, pos);
CL_BFGExplosionParticles(pos);
break;
case TE_BFG_LASER:
CL_ParseLaser(0xd0d1d2d3);
break;
case 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:
ent = CL_ParseBeam(cl_mod_parasite_segment);
break;
case 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:
ent = CL_ParseBeam2(cl_mod_grapple_cable);
break;
case 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();
VectorCopy(pos, ex->ent.origin);
ex->type = ex_flash;
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_ReadPos(&net_message, pos);
MSG_ReadDir(&net_message, dir);
CL_ParticleEffect2(pos, dir, 0xdf, 30);
break;
case 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;
case TE_BLASTER2: case TE_FLECHETTE: MSG_ReadPos(&net_message, pos);
MSG_ReadDir(&net_message, dir);
if(type == TE_BLASTER2)
CL_BlasterParticles2(pos, dir, 0xd0);
else
CL_BlasterParticles2(pos, dir, 0x6f);
ex = CL_AllocExplosion();
VectorCopy(pos, ex->ent.origin);
ex->ent.angles[0] = acos(dir[2]) / M_PI * 180;
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;
if(type == TE_BLASTER2)
ex->ent.skinnum = 1;
else ex->ent.skinnum = 2;
ex->start = cl.frame.servertime - 100;
ex->light = 150;
if(type == TE_BLASTER2)
ex->lightcolor[1] = 1;
else {
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:
ent = CL_ParseLightning(cl_mod_lightning);
S_StartSound(NULL, ent, CHAN_WEAPON, cl_sfx_lightning, 1, ATTN_NORM, 0);
break;
case TE_DEBUGTRAIL:
MSG_ReadPos(&net_message, pos);
MSG_ReadPos(&net_message, pos2);
CL_DebugTrail(pos, pos2);
break;
case TE_PLAIN_EXPLOSION:
MSG_ReadPos(&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;
if(frand() < 0.5)
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:
MSG_ReadPos(&net_message, pos);
ent = MSG_ReadShort(&net_message);
CL_Flashlight(ent, pos);
break;
case 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:
ent = CL_ParsePlayerBeam(cl_mod_heatbeam);
break;
case TE_MONSTER_HEATBEAM:
ent = CL_ParsePlayerBeam(cl_mod_monster_heatbeam);
break;
case 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:
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:
cnt = 8;
MSG_ReadPos(&net_message, pos);
MSG_ReadPos(&net_message, pos2);
CL_BubbleTrail2(pos, pos2, cnt);
S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
break;
case TE_MOREBLOOD:
MSG_ReadPos(&net_message, pos);
MSG_ReadDir(&net_message, dir);
CL_ParticleEffect(pos, dir, 0xe8, 250);
break;
case 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:
MSG_ReadPos(&net_message, pos);
MSG_ReadDir(&net_message, dir);
CL_ParticleEffect(pos, dir, 0x75, 40);
S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
break;
case TE_TRACKER_EXPLOSION:
MSG_ReadPos(&net_message, 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(&net_message, pos);
CL_TeleportParticles(pos);
break;
case TE_WIDOWBEAMOUT:
CL_ParseWidow();
break;
case TE_NUKEBLAST:
CL_ParseNuke();
break;
case TE_WIDOWSPLASH:
MSG_ReadPos(&net_message, pos);
CL_WidowSplash(pos);
break;
default:
Com_Error(ERR_DROP, "CL_ParseTEnt: bad type");
}
}
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;
for(i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) {
if(!b->model || b->endtime < cl.time)
continue;
if(b->entity == cl.playernum + 1) {
VectorCopy(cl.refdef.vieworg, b->start);
b->start[2] -= 22; }
VectorAdd(b->start, b->offset, org);
VectorSubtract(b->end, org, dist);
if(dist[1] == 0 && dist[0] == 0) {
yaw = 0;
if(dist[2] > 0)
pitch = 90;
else
pitch = 270;
} else {
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;
}
d = VectorNormalize(dist);
memset(&ent, 0, sizeof(ent));
if(b->model == cl_mod_lightning) {
model_length = 35.0;
d -= 20.0; } else {
model_length = 30.0;
}
steps = ceil(d / model_length);
len = (d - model_length) / (steps - 1);
if((b->model == cl_mod_lightning) && (d <= model_length)) {
VectorCopy(b->end, ent.origin);
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;
}
V_AddEntity(&ent);
for(j = 0; j < 3; j++)
org[j] += dist[j] * len;
d -= model_length;
}
}
}
extern cvar_t *hand;
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;
float model_length;
float hand_multiplier;
frame_t *oldframe;
player_state_t *ps, *ops;
if(hand) {
if(hand->value == 2)
hand_multiplier = 0;
else if(hand->value == 1)
hand_multiplier = -1;
else
hand_multiplier = 1;
} else {
hand_multiplier = 1;
}
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(b->entity == cl.playernum + 1) {
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; 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->value == 2)) {
VectorMA(org, -1, cl.v_up, org);
}
VectorCopy(cl.v_right, r);
VectorCopy(cl.v_forward, f);
VectorCopy(cl.v_up, u);
} else
VectorCopy(b->start, org);
} else {
if(b->entity == cl.playernum + 1) {
VectorCopy(cl.refdef.vieworg, b->start);
b->start[2] -= 22; }
VectorAdd(b->start, b->offset, org);
}
VectorSubtract(b->end, org, dist);
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->value == 2)) {
VectorMA(org, -1, cl.v_up, org);
}
}
if(dist[1] == 0 && dist[0] == 0) {
yaw = 0;
if(dist[2] > 0)
pitch = 90;
else
pitch = 270;
} else {
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.playernum + 1) {
framenum = 2;
ent.angles[0] = -pitch;
ent.angles[1] = yaw + 180.0;
ent.angles[2] = 0;
AngleVectors(ent.angles, f, r, u);
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 {
CL_MonsterPlasma_Shell(b->start);
}
} else {
framenum = 1;
}
}
if((cl_mod_heatbeam && (b->model == cl_mod_heatbeam) && (b->entity == cl.playernum + 1))) {
CL_Heatbeam(org, dist);
}
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; } else {
model_length = 30.0;
}
steps = ceil(d / model_length);
len = (d - model_length) / (steps - 1);
if((b->model == cl_mod_lightning) && (d <= model_length)) {
VectorCopy(b->end, ent.origin);
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;
ent.angles[0] = -pitch;
ent.angles[1] = yaw + 180.0;
ent.angles[2] = (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.0;
ent.angles[2] = rand() % 360;
} else {
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;
}
}
}
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;
case ex_explosion:
case ex_free:
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->oldframe = ex->baseframe + f;
ent->backlerp = 1.0 - cl.lerpfrac;
V_AddEntity(ent);
}
}
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);
}
}
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;
}
}
}
}
void CL_AddTEnts(void) {
CL_AddBeams();
CL_AddPlayerBeams();
CL_AddExplosions();
CL_AddLasers();
CL_ProcessSustain();
}