thirtyflightsofloving/client/cl_tempent.c
Knightmare66 95295401a4 Added support for custom client railgun colors in 3ZB2, Awakening2, and Zaero DLLs.
Added weapon balancing and tech control cvars to 3ZB2 DLL.
Added Vampire and Ammogen techs to 3ZB2 game DLL.
Fixed func_door_secret movement bug (when built with origin brush) in Zaero DLL.
Fixed armor stacking bug in default Lazarus, missionpack, and Zaero DLLs.
Removed unused tech cvars in default Lazarus DLL.
Changed parsing of TE_BLASTER_COLORED tempent.
2021-10-31 03:53:09 -04:00

2095 lines
53 KiB
C

/*
===========================================================================
Copyright (C) 1997-2001 Id Software, Inc.
This file is part of Quake 2 source code.
Quake 2 source code 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.
Quake 2 source code 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 Quake 2 source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// cl_tent.c -- client side temporary entities
#include "client.h"
#include "particles.h"
void CL_LightningBeam (vec3_t start, vec3_t end, int srcEnt, int dstEnt, float size);
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 512 // was 32
explosion_t cl_explosions[MAX_EXPLOSIONS];
#define MAX_BEAMS 512 // was 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];
//PMM - added this for player-linked beams. Currently only used by the plasma beam
beam_t cl_playerbeams[MAX_BEAMS];
#define MAX_LASERS 512 // was 32
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
// Psychospaz's enhanced particle code
void CL_Explosion_Particle (vec3_t org, float scale, qboolean rocket);
void CL_Explosion_FlashParticle (vec3_t org, float size, qboolean large);
void CL_BloodHit (vec3_t org, vec3_t dir);
void CL_GreenBloodHit (vec3_t org, vec3_t dir);
void CL_ParticleEffectSparks (vec3_t org, vec3_t dir, vec3_t color, int count);
void CL_ParticleBulletDecal(vec3_t org, vec3_t dir, float size);
void CL_ParticlePlasmaBeamDecal(vec3_t org, vec3_t dir, float size);
void CL_ParticleBlasterDecal (vec3_t org, vec3_t dir, float size, int red, int green, int blue);
void CL_Explosion_Decal (vec3_t org, float size, int decalnum);
void CL_Explosion_Sparks (vec3_t org, int size, int count);
void CL_BFGExplosionParticles (vec3_t org);
clientMedia_t clMedia;
void ReadTextureSurfaceAssignments();
/*
=================
CL_RegisterTEntSounds
=================
*/
void CL_RegisterTEntSounds (void)
{
int i;
char name[MAX_QPATH];
clMedia.sfx_ric[0] = S_RegisterSound ("world/ric1.wav");
clMedia.sfx_ric[1] = S_RegisterSound ("world/ric2.wav");
clMedia.sfx_ric[2] = S_RegisterSound ("world/ric3.wav");
clMedia.sfx_lashit = S_RegisterSound("weapons/lashit.wav");
clMedia.sfx_spark[0] = S_RegisterSound ("world/spark5.wav");
clMedia.sfx_spark[1] = S_RegisterSound ("world/spark6.wav");
clMedia.sfx_spark[2] = S_RegisterSound ("world/spark7.wav");
clMedia.sfx_railg = S_RegisterSound ("weapons/railgf1a.wav");
clMedia.sfx_rockexp = S_RegisterSound ("weapons/rocklx1a.wav");
clMedia.sfx_grenexp = S_RegisterSound ("weapons/grenlx1a.wav");
clMedia.sfx_watrexp = S_RegisterSound ("weapons/xpld_wat.wav");
// Xatrix
clMedia.sfx_plasexp = S_RegisterSound ("weapons/plasexpl.wav");
// Rogue
clMedia.sfx_lightning = S_RegisterSound ("weapons/tesla.wav");
clMedia.sfx_disrexp = S_RegisterSound ("weapons/disrupthit.wav");
// shockwave impact
clMedia.sfx_shockhit = S_RegisterSound ("weapons/shockhit.wav");
for (i=0 ; i<4 ; i++) {
Com_sprintf (name, sizeof(name), "player/step%i.wav", i+1);
clMedia.sfx_footsteps[i] = S_RegisterSound (name);
}
// Lazarus footstep sounds
for (i=0 ; i<4 ; i++) {
Com_sprintf (name, sizeof(name), "player/pl_metal%i.wav", i+1);
clMedia.sfx_metal_footsteps[i] = S_RegisterSound (name);
}
for (i=0 ; i<4 ; i++) {
Com_sprintf (name, sizeof(name), "player/pl_dirt%i.wav", i+1);
clMedia.sfx_dirt_footsteps[i] = S_RegisterSound (name);
}
for (i=0 ; i<4 ; i++) {
Com_sprintf (name, sizeof(name), "player/pl_duct%i.wav", i+1);
clMedia.sfx_vent_footsteps[i] = S_RegisterSound (name);
}
for (i=0 ; i<4 ; i++) {
Com_sprintf (name, sizeof(name), "player/pl_grate%i.wav", i+1);
clMedia.sfx_grate_footsteps[i] = S_RegisterSound (name);
}
for (i=0 ; i<4 ; i++) {
Com_sprintf (name, sizeof(name), "player/pl_tile%i.wav", i+1);
clMedia.sfx_tile_footsteps[i] = S_RegisterSound (name);
}
for (i=0 ; i<4 ; i++) {
Com_sprintf (name, sizeof(name), "player/pl_grass%i.wav", i+1);
clMedia.sfx_grass_footsteps[i] = S_RegisterSound (name);
}
for (i=0 ; i<4 ; i++) {
Com_sprintf (name, sizeof(name), "player/pl_snow%i.wav", i+1);
clMedia.sfx_snow_footsteps[i] = S_RegisterSound (name);
}
for (i=0 ; i<4 ; i++) {
Com_sprintf (name, sizeof(name), "player/pl_carpet%i.wav", i+1);
clMedia.sfx_carpet_footsteps[i] = S_RegisterSound (name);
}
for (i=0 ; i<4 ; i++) {
Com_sprintf (name, sizeof(name), "player/pl_force%i.wav", i+1);
clMedia.sfx_force_footsteps[i] = S_RegisterSound (name);
}
for (i=0 ; i<4 ; i++) {
Com_sprintf (name, sizeof(name), "player/pl_gravel%i.wav", i+1);
clMedia.sfx_gravel_footsteps[i] = S_RegisterSound (name);
}
for (i=0; i<4; i++) {
Com_sprintf (name, sizeof(name), "player/pl_ice%i.wav", i+1);
clMedia.sfx_ice_footsteps[i] = S_RegisterSound (name);
}
for (i=0; i<4; i++) {
Com_sprintf (name, sizeof(name), "player/pl_sand%i.wav", i+1);
clMedia.sfx_sand_footsteps[i] = S_RegisterSound (name);
}
for (i=0; i<4; i++) {
Com_sprintf (name, sizeof(name), "player/pl_wood%i.wav", i+1);
clMedia.sfx_wood_footsteps[i] = S_RegisterSound (name);
}
for (i=0; i<4; i++) {
Com_sprintf (name, sizeof(name), "player/pl_slosh%i.wav", i+1);
clMedia.sfx_slosh[i] = S_RegisterSound (name);
}
for (i=0; i<4; i++) {
Com_sprintf (name, sizeof(name), "player/pl_wade%i.wav", i+1);
clMedia.sfx_wade[i] = S_RegisterSound (name);
}
for (i=0; i<2; i++) {
Com_sprintf (name, sizeof(name), "mud/wade_mud%i.wav", i+1);
clMedia.sfx_mud_wade[i] = S_RegisterSound (name);
}
for (i=0; i<4; i++) {
Com_sprintf (name, sizeof(name), "player/pl_ladder%i.wav", i+1);
clMedia.sfx_ladder[i] = S_RegisterSound (name);
}
// player loud step
clMedia.sfx_player_land = S_RegisterSound ("player/land1.wav");
// player teleport
clMedia.sfx_player_teleport = S_RegisterSound ("misc/tele1.wav");
// Q1-style player teleport (in rogue pak0.pak)
for (i=0; i<5; i++) {
Com_sprintf (name, sizeof(name), "misc/r_tele%i.wav", i+1);
clMedia.sfx_player_teleport2[i] = S_RegisterSound (name);
}
// item respawn
clMedia.sfx_item_respawn = S_RegisterSound ("items/respawn1.wav");
// read footstep defintion file
// if (cl_footstep_override->value)
if (cl_footstep_override->integer)
ReadTextureSurfaceAssignments();
// end Lazarus footstep sounds
// S_RegisterSound ("player/land1.wav");
S_RegisterSound ("player/fall2.wav");
S_RegisterSound ("player/fall1.wav");
}
/*
=================
CL_RegisterTEntModels
=================
*/
void CL_RegisterTEntModels (void)
{
clMedia.mod_explode = R_RegisterModel ("models/objects/explode/tris.md2");
clMedia.mod_smoke = R_RegisterModel ("models/objects/smoke/tris.md2");
clMedia.mod_flash = R_RegisterModel ("models/objects/flash/tris.md2");
clMedia.mod_parasite_segment = R_RegisterModel ("models/monsters/parasite/segment/tris.md2");
clMedia.mod_grapple_cable = R_RegisterModel ("models/ctf/segment/tris.md2");
clMedia.mod_parasite_tip = R_RegisterModel ("models/monsters/parasite/tip/tris.md2");
clMedia.mod_explo = R_RegisterModel ("models/objects/r_explode/tris.md2");
clMedia.mod_bfg_explo = R_RegisterModel ("sprites/s_bfg2.sp2");
clMedia.mod_powerscreen = R_RegisterModel ("models/items/armor/effect/tris.md2");
// Rogue
clMedia.mod_explo_big = R_RegisterModel ("models/objects/r_explode2/tris.md2");
clMedia.mod_lightning = R_RegisterModel ("models/proj/lightning/tris.md2");
clMedia.mod_heatbeam = R_RegisterModel ("models/proj/beam/tris.md2");
clMedia.mod_monster_heatbeam = R_RegisterModel ("models/proj/widowbeam/tris.md2");
// new effect models
clMedia.mod_shocksplash = R_RegisterModel ("models/objects/shocksplash/tris.md2");
R_RegisterModel ("models/objects/laser/tris.md2");
R_RegisterModel ("models/objects/grenade2/tris.md2");
R_RegisterModel ("models/weapons/v_machn/tris.md2");
R_RegisterModel ("models/weapons/v_handgr/tris.md2");
R_RegisterModel ("models/weapons/v_shotg2/tris.md2");
R_RegisterModel ("models/objects/gibs/bone/tris.md2");
R_RegisterModel ("models/objects/gibs/sm_meat/tris.md2");
R_RegisterModel ("models/objects/gibs/bone2/tris.md2");
// Xatrix
// R_RegisterModel ("models/objects/blaser/tris.md2");
R_DrawFindPic ("w_machinegun");
R_DrawFindPic ("a_bullets");
R_DrawFindPic ("i_health");
R_DrawFindPic ("a_grenades");
}
/*
=================
CL_ClearTEnts
=================
*/
void CL_ClearTEnts (void)
{
memset (cl_beams, 0, sizeof(cl_beams));
memset (cl_explosions, 0, sizeof(cl_explosions));
memset (cl_lasers, 0, sizeof(cl_lasers));
//ROGUE
memset (cl_playerbeams, 0, sizeof(cl_playerbeams));
memset (cl_sustains, 0, sizeof(cl_sustains));
//ROGUE
}
/*
=================
CL_AllocExplosion
=================
*/
explosion_t *CL_AllocExplosion (void)
{
int i;
int time;
int index;
for (i=0; i<MAX_EXPLOSIONS; i++)
{
if (cl_explosions[i].type == ex_free)
{
memset (&cl_explosions[i], 0, sizeof (cl_explosions[i]));
return &cl_explosions[i];
}
}
// find the oldest explosion
time = cl.time;
index = 0;
for (i=0; i<MAX_EXPLOSIONS; i++)
if (cl_explosions[i].start < time)
{
time = cl_explosions[i].start;
index = i;
}
memset (&cl_explosions[index], 0, sizeof (cl_explosions[index]));
return &cl_explosions[index];
}
void CL_Explosion_Flash (vec3_t origin, int dist, int size, qboolean plasma)
{
int i,j,k;
int limit = (plasma)?1:2;
vec3_t org;
if (cl_particle_scale->value > 1 || plasma)
{
for (i=0; i<limit; i++)
CL_Explosion_FlashParticle (origin, size+2*dist, true);
return;
}
for (i=-1; i<2; i+=2)
for (j=-1; j<2; j+=2)
for (k=-1; k<2; k+=2)
{
org[0]=origin[0]+i*dist;
org[1]=origin[1]+j*dist;
org[2]=origin[2]+k*dist;
CL_Explosion_FlashParticle (org, size, false);
}
}
/*
=================
CL_SmokeAndFlash
=================
*/
void CL_SmokeAndFlash(vec3_t origin)
{
explosion_t *ex;
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 = clMedia.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
=================
*/
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);
// 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;
}
}
Com_Printf ("beam list overflow!\n");
return ent;
}
/*
=================
CL_ParseBeam2
=================
*/
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);
// Com_Printf ("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;
}
}
Com_Printf ("beam list overflow!\n");
return ent;
}
// ROGUE
/*
=================
CL_ParsePlayerBeam
- adds to the cl_playerbeam array instead of the cl_beams array
=================
*/
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);
// PMM - network optimization
if (model == clMedia.mod_heatbeam)
VectorSet(offset, 2, 7, -3);
else if (model == clMedia.mod_monster_heatbeam)
{
model = clMedia.mod_heatbeam;
VectorSet(offset, 0, 0, 0);
}
else
MSG_ReadPos (&net_message, offset);
// Com_Printf ("end- %f %f %f\n", end[0], end[1], end[2]);
// override any beam with the same entity
// PMM - For player beams, we only want one per player (entity) so..
for (i=0, b=cl_playerbeams; i<MAX_BEAMS; i++, b++)
{
if (b->entity == ent)
{
b->entity = ent;
b->model = model;
b->endtime = cl.time + 200;
VectorCopy (start, b->start);
VectorCopy (end, b->end);
VectorCopy (offset, b->offset);
return ent;
}
}
// find a free beam
for (i=0, b=cl_playerbeams; i<MAX_BEAMS; i++, b++)
{
if (!b->model || b->endtime < cl.time)
{
b->entity = ent;
b->model = model;
b->endtime = cl.time + 100; // PMM - this needs to be 100 to prevent multiple heatbeams
VectorCopy (start, b->start);
VectorCopy (end, b->end);
VectorCopy (offset, b->offset);
return ent;
}
}
Com_Printf ("beam list overflow!\n");
return ent;
}
//rogue
/*
=================
CL_ParseLightning
=================
*/
// Psychspaz's enhanced particles
int CL_ParseLightning (void)
{
vec3_t start, end; // move,vec
int srcEnt, dstEnt; // len, dec
srcEnt = MSG_ReadShort (&net_message);
dstEnt = MSG_ReadShort (&net_message);
MSG_ReadPos (&net_message, start);
MSG_ReadPos (&net_message, end);
CL_LightningBeam (start, end, srcEnt, dstEnt, 5);
return srcEnt;
}
/*
=================
CL_ParseLaser
=================
*/
// Psychspaz's enhanced particles
void CL_ParseLaser (int colors)
{
vec3_t start,end;
vec3_t move;
vec3_t vec;
vec3_t point;
float len;
float dec;
MSG_ReadPos (&net_message, start);
MSG_ReadPos (&net_message, end);
VectorSubtract (end, start, vec);
len = VectorNormalize (vec);
VectorCopy (vec, point);
dec = 20;
VectorScale (vec, dec, vec);
VectorCopy (start, move);
while (len > 0)
{
len -= dec;
CL_SetupParticle (
point[0], point[1], point[2],
move[0], move[1], move[2],
0, 0, 0,
0, 0, 0,
50, 255, 50,
0, 0, 0,
1, -2.5,
GL_SRC_ALPHA, GL_ONE,
dec*TWOTHIRDS, 0,
particle_beam,
PART_DIRECTION,
NULL,0);
VectorAdd (move, vec, move);
}
}
//=============
//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
{
//Com_Printf ("Sustain effect id %d\n", id);
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
{
//Com_Printf ("No free sustains!\n");
// 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,
color8red(color), color8green(color), color8blue(color),
-20, -20, -20, cnt, magnitude);
//CL_ParticleSteamEffect (pos, dir, 240, 240, 240, -20, -20, -20, cnt, magnitude);
//S_StartSound (pos, 0, 0, clMedia.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; 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 // 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; 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 // no free sustains
{
// FIXME - read the stuff anyway
MSG_ReadPos (&net_message, pos);
}
}
//ROGUE
//=============
// Psychospaz's enhanced particle code
void CL_GunSmokeEffect (vec3_t org, vec3_t dir)
{
vec3_t velocity, origin;
int j;
for (j=0 ; j<3 ; j++)
{
origin[j] = org[j] + dir[j]*10;
velocity[j] = 10*dir[j];
}
velocity[2] = 10;
CL_ParticleSmokeEffect (origin, velocity, 10);
}
/*
=================
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 red, green, blue;
int r, i;
int ent;
int magnitude;
CL_FixParticleCvars (); // clamp critical effects vars to acceptable bounds
type = MSG_ReadByte (&net_message);
// TODO: have index 255 as a flag to read a short for the real extended index
/* if ( !LegacyProtocol() && (type == 255) )
type = (unsigned short)MSG_ReadShort (&net_message); */
switch (type)
{
case TE_BLOOD: // bullet hitting flesh
MSG_ReadPos (&net_message, pos);
MSG_ReadDir (&net_message, dir);
// Psychospaz's enhanced particle code
CL_BloodHit (pos, dir);
break;
case TE_GUNSHOT: // bullet hitting wall
case TE_SPARKS:
case TE_BULLET_SPARKS:
MSG_ReadPos (&net_message, pos);
MSG_ReadDir (&net_message, dir);
// Psychospaz's enhanced particle code
if (type == TE_BULLET_SPARKS)
VectorScale(dir, 2, dir);
{
vec3_t color = { 255, 125, 10 };
CL_ParticleEffectSparks (pos, dir, color, (type == TE_GUNSHOT)? 5 : 10);
}
if (type != TE_SPARKS)
{
CL_GunSmokeEffect (pos, dir);
if (type == TE_GUNSHOT || type == TE_BULLET_SPARKS)
CL_ParticleBulletDecal(pos, dir, 2.5);
// impact sound
cnt = rand()&15;
if (cnt == 1)
S_StartSound (pos, 0, 0, clMedia.sfx_ric[0], 1, ATTN_NORM, 0);
else if (cnt == 2)
S_StartSound (pos, 0, 0, clMedia.sfx_ric[1], 1, ATTN_NORM, 0);
else if (cnt == 3)
S_StartSound (pos, 0, 0, clMedia.sfx_ric[2], 1, ATTN_NORM, 0);
}
break;
case TE_SHOTGUN: // bullet hitting wall
MSG_ReadPos (&net_message, pos);
MSG_ReadDir (&net_message, dir);
// Psychospaz's enhanced particle code
CL_GunSmokeEffect (pos, dir);
CL_ParticleBulletDecal(pos, dir, 2.8);
{
vec3_t color = { 200, 100, 10 };
CL_ParticleEffectSparks (pos, dir, color, 8);
}
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);
//FIXME : replace or remove this sound
S_StartSound (pos, 0, 0, clMedia.sfx_lashit, 1, ATTN_NORM, 0);
break;
case TE_SPLASH: // bullet hitting water
cnt = MSG_ReadByte (&net_message);
cnt = min(cnt, 40); // cap at 40
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];
// Psychospaz's enhanced particle code
CL_ParticleEffectSplash (pos, dir, color, cnt);
if (r == SPLASH_SPARKS)
{
r = rand() & 3;
if (r == 0)
S_StartSound (pos, 0, 0, clMedia.sfx_spark[0], 1, ATTN_STATIC, 0);
else if (r == 1)
S_StartSound (pos, 0, 0, clMedia.sfx_spark[1], 1, ATTN_STATIC, 0);
else
S_StartSound (pos, 0, 0, clMedia.sfx_spark[2], 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_RAILTRAIL: // railgun effect
case TE_RAILTRAIL2: // 12/23/2001 - red railgun trail
case TE_RAILTRAIL_COLORED: // changeable color railgun trail
MSG_ReadPos (&net_message, pos);
MSG_ReadPos (&net_message, pos2);
if (type == TE_RAILTRAIL_COLORED)
{
red = MSG_ReadByte (&net_message);
green = MSG_ReadByte (&net_message);
blue = MSG_ReadByte (&net_message);
CL_RailTrail (pos, pos2, red, green, blue);
}
else if (type == TE_RAILTRAIL2) {
CL_RailTrail (pos, pos2, 255, 20, 20);
}
else { // 20, 48, 176 default
CL_RailTrail (pos, pos2, cl_railred->integer, cl_railgreen->integer, cl_railblue->integer);
}
S_StartSound (pos2, 0, 0, clMedia.sfx_railg, 1, ATTN_NORM, 0);
break;
case TE_EXPLOSION2:
case TE_GRENADE_EXPLOSION:
case TE_GRENADE_EXPLOSION_WATER:
MSG_ReadPos (&net_message, pos);
if (cl_old_explosions->integer)
{
ex = CL_AllocExplosion ();
VectorCopy (pos, ex->ent.origin);
ex->type = ex_poly;
ex->ent.flags = RF_FULLBRIGHT|RF_NOSHADOW; // noshadow flag
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 = clMedia.mod_explo;
ex->frames = 19;
ex->baseframe = 30;
ex->ent.angles[1] = rand() % 360;
}
else {
CL_Explosion_Particle (pos, 0, false);
if (type != TE_GRENADE_EXPLOSION_WATER)
CL_Explosion_Flash (pos, 10, 50, false);
}
CL_Explosion_Sparks (pos, 16, 128);
if (type != TE_EXPLOSION2)
CL_Explosion_Decal (pos, 50, particle_burnmark);
if (type == TE_GRENADE_EXPLOSION_WATER)
S_StartSound (pos, 0, 0, clMedia.sfx_watrexp, 1, ATTN_NORM, 0);
else
S_StartSound (pos, 0, 0, clMedia.sfx_grenexp, 1, ATTN_NORM, 0);
break;
// RAFAEL
case TE_PLASMA_EXPLOSION:
MSG_ReadPos (&net_message, pos);
// if (cl_old_explosions->value)
if (cl_old_explosions->integer)
{
ex = CL_AllocExplosion ();
VectorCopy (pos, ex->ent.origin);
ex->type = ex_poly;
ex->ent.flags = RF_FULLBRIGHT|RF_NOSHADOW; // noshadow flag
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 = clMedia.mod_explo;
if (frand() < 0.5)
ex->baseframe = 15;
ex->frames = 15;
}
else {
CL_Explosion_Particle (pos, 0, true);
CL_Explosion_Flash (pos, 10, 50, true);
}
CL_Explosion_Sparks (pos, 16, 128);
CL_Explosion_Decal (pos, 50, particle_burnmark);
// if (cl_plasma_explo_sound->value)
if (cl_plasma_explo_sound->integer)
S_StartSound (pos, 0, 0, clMedia.sfx_plasexp, 1, ATTN_NORM, 0);
else
S_StartSound (pos, 0, 0, clMedia.sfx_rockexp, 1, ATTN_NORM, 0);
break;
case TE_EXPLOSION1_BIG: // PMM
MSG_ReadPos (&net_message, pos);
// if (cl_old_explosions->value)
if (cl_old_explosions->integer)
{
ex = CL_AllocExplosion ();
VectorCopy (pos, ex->ent.origin);
ex->type = ex_poly;
ex->ent.flags = RF_FULLBRIGHT|RF_NOSHADOW; // noshadow flag
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 = clMedia.mod_explo_big;
if (frand() < 0.5)
ex->baseframe = 15;
ex->frames = 15;
}
else {
CL_Explosion_Particle (pos, 150, true); // increased size
CL_Explosion_Flash (pos, 30, 150, false);
}
CL_Explosion_Sparks (pos, 48, 128);
S_StartSound (pos, 0, 0, clMedia.sfx_rockexp, 1, ATTN_NORM, 0);
break;
case TE_EXPLOSION1_NP: // PMM
MSG_ReadPos (&net_message, pos);
// if (cl_old_explosions->value)
if (cl_old_explosions->integer)
{
ex = CL_AllocExplosion ();
VectorCopy (pos, ex->ent.origin);
ex->type = ex_poly;
ex->ent.flags = RF_FULLBRIGHT|RF_NOSHADOW; // noshadow flag
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 = clMedia.mod_explo;
if (frand() < 0.5)
ex->baseframe = 15;
ex->frames = 15;
}
else {
CL_Explosion_Particle (pos, 50, true);
CL_Explosion_Flash (pos, 7, 32, false);
}
CL_Explosion_Sparks (pos, 10, 128);
S_StartSound (pos, 0, 0, clMedia.sfx_grenexp, 1, ATTN_NORM, 0);
break;
case TE_EXPLOSION1:
case TE_PLAIN_EXPLOSION:
case TE_ROCKET_EXPLOSION:
case TE_ROCKET_EXPLOSION_WATER:
MSG_ReadPos (&net_message, pos);
// if (cl_old_explosions->value)
if (cl_old_explosions->integer)
{
ex = CL_AllocExplosion ();
VectorCopy (pos, ex->ent.origin);
ex->type = ex_poly;
ex->ent.flags = RF_FULLBRIGHT|RF_NOSHADOW; // noshadow flag
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 = clMedia.mod_explo;
if (frand() < 0.5)
ex->baseframe = 15;
ex->frames = 15;
}
else {
CL_Explosion_Particle (pos, 0, true);
CL_Explosion_Flash (pos, 10, 50, false);
}
CL_Explosion_Sparks (pos, 16, 128);
if (type == TE_ROCKET_EXPLOSION || type == TE_ROCKET_EXPLOSION_WATER)
CL_Explosion_Decal (pos, 50, particle_burnmark);
if (type == TE_ROCKET_EXPLOSION_WATER)
S_StartSound (pos, 0, 0, clMedia.sfx_watrexp, 1, ATTN_NORM, 0);
else
S_StartSound (pos, 0, 0, clMedia.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 = clMedia.mod_bfg_explo;
ex->ent.flags |= RF_TRANSLUCENT | RF_TRANS_ADDITIVE;
ex->ent.alpha = 0.30;
ex->frames = 4;
break;
case TE_BFG_BIGEXPLOSION:
MSG_ReadPos (&net_message, pos);
CL_BFGExplosionParticles (pos);
CL_Explosion_Decal (pos, 75, particle_bfgmark);
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 (clMedia.mod_parasite_segment);
break;
case TE_BOSSTPORT: // boss teleporting to station
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 (clMedia.mod_grapple_cable);
break;
// RAFAEL
case TE_WELDING_SPARKS:
cnt = MSG_ReadByte (&net_message);
MSG_ReadPos (&net_message, pos);
MSG_ReadDir (&net_message, dir);
color = MSG_ReadByte (&net_message);
// Psychospaz's enhanced particle code
{
vec3_t sparkcolor = {color8red(color), color8green(color), color8blue(color)};
CL_ParticleEffectSparks (pos, dir, sparkcolor, 40);
}
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 = clMedia.mod_flash;
ex->frames = 2;
break;
case TE_GREENBLOOD:
MSG_ReadPos (&net_message, pos);
MSG_ReadDir (&net_message, dir);
// Psychospaz's enhanced particle code
CL_GreenBloodHit (pos, dir);
break;
// RAFAEL
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;
//=============
//PGM
// PMM -following code integrated for flechette (different color)
case TE_BLASTER: // blaster hitting wall
case TE_BLASTER2: // green blaster hitting wall
case TE_BLUEHYPERBLASTER: // blue blaster hitting wall
case TE_REDBLASTER: // red blaster hitting wall
case TE_BLASTER_COLORED: // variable colored blaster hitting wall
case TE_FLECHETTE: // flechette hitting wall
MSG_ReadPos (&net_message, pos);
MSG_ReadDir (&net_message, dir);
if (type == TE_BLASTER_COLORED) {
red = MSG_ReadByte (&net_message);
green = MSG_ReadByte (&net_message);
blue = MSG_ReadByte (&net_message);
}
if ( cl_old_explosions->integer || !(cl_add_particles->integer) )
{
ex = CL_AllocExplosion ();
VectorCopy (pos, ex->ent.origin);
ex->ent.angles[0] = acos(dir[2])/M_PI*180;
if (dir[0]) // PMM - fixed to correct for pitch of 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|RF_NOSHADOW; // no shadow on these
if (type == TE_BLASTER2)
ex->ent.skinnum = 1;
else if (type == TE_REDBLASTER)
ex->ent.skinnum = 3;
else if (type == TE_FLECHETTE || type == TE_BLUEHYPERBLASTER)
ex->ent.skinnum = 2;
else // TE_BLASTER
ex->ent.skinnum = 0;
ex->start = cl.frame.servertime - 100;
ex->light = 150;
if (type == TE_BLASTER2) {
ex->lightcolor[0] = 0.15f;
ex->lightcolor[1] = 1.0f;
ex->lightcolor[2] = 0.15f;
}
else if (type == TE_BLUEHYPERBLASTER) {
ex->lightcolor[0] = 0.19f;
ex->lightcolor[1] = 0.41f;
ex->lightcolor[2] = 0.75f;
}
else if (type == TE_REDBLASTER) {
ex->lightcolor[0] = 0.75f;
ex->lightcolor[1] = 0.19f; // was 0.41f
ex->lightcolor[2] = 0.19f;
}
else if (type == TE_FLECHETTE) {
ex->lightcolor[0] = 0.39f;
ex->lightcolor[1] = 0.61f;
ex->lightcolor[2] = 0.75f;
}
else { // TE_BLASTER
ex->lightcolor[0] = 1.0f;
ex->lightcolor[1] = 1.0f;
ex->lightcolor[2] = 0.0f;
}
ex->ent.model = clMedia.mod_explode;
ex->frames = 4;
}
// Psychospaz's enhanced particle code
{
int pRed, pGreen, pBlue, redDelta, greenDelta, blueDelta, numparts;
float partsize = (cl_old_explosions->integer) ? 2 : 4;
numparts = (cl_old_explosions->integer) ? 12 : (32 / max(cl_particle_scale->value/2, 1));
if (type == TE_BLASTER2) {
pRed = 50; pGreen = 235; pBlue = 50;
redDelta = -10; greenDelta = 0; blueDelta = -10;
}
else if (type == TE_BLUEHYPERBLASTER) {
pRed = 50; pGreen = 50; pBlue = 235;
redDelta = -10; greenDelta = 0; blueDelta = -10;
}
else if (type == TE_REDBLASTER) {
pRed = 235; pGreen = 50; pBlue = 50;
redDelta = 255; greenDelta = -30; blueDelta = -30;
}
else if (type == TE_FLECHETTE) {
pRed = 100; pGreen = 100; pBlue = 195;
redDelta = -10; greenDelta = 0; blueDelta = -10;
}
else if (type == TE_BLASTER_COLORED) {
pRed = red; pGreen = green; pBlue = blue;
redDelta = -10; greenDelta = -10; blueDelta = -10;
}
else { // TE_BLASTER
pRed = 255; pGreen = 150; pBlue = 50; // was 40
redDelta = 255; greenDelta = -90; blueDelta = -30;
}
CL_BlasterParticles (pos, dir, numparts, partsize, pRed, pGreen, pBlue, redDelta, greenDelta, blueDelta);
CL_ParticleBlasterDecal(pos, dir, 10, pRed, pGreen, pBlue);
}
S_StartSound (pos, 0, 0, clMedia.sfx_lashit, 1, ATTN_NORM, 0);
break;
// shockwave impact effect
case TE_SHOCKSPLASH:
MSG_ReadPos (&net_message, pos);
MSG_ReadDir (&net_message, dir);
// Spawn 5 rings
for (i = 0; i < 5; i++)
{
ex = CL_AllocExplosion ();
VectorMA (pos, 16.0*(i+1), dir, ex->ent.origin);
vectoangles2(dir, ex->ent.angles);
//VectorCopy (dir, ex->ent.angles);
ex->type = ex_poly2;
ex->ent.flags |= RF_FULLBRIGHT|RF_TRANSLUCENT|RF_NOSHADOW;
ex->start = cl.frame.servertime - 100;
ex->light = 75;
ex->lightcolor[0] = 0.39;
ex->lightcolor[1] = 0.61;
ex->lightcolor[2] = 0.75;
ex->ent.model = clMedia.mod_shocksplash;
ex->ent.alpha = 0.70;
ex->baseframe = 4-i;
ex->frames = 4+i;
}
S_StartSound (pos, 0, 0, clMedia.sfx_shockhit, 1, ATTN_NONE, 0);
break;
case TE_LIGHTNING:
// Psychospaz's enhanced particle code
ent = CL_ParseLightning ();
S_StartSound (NULL, ent, CHAN_WEAPON, clMedia.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_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 (clMedia.mod_heatbeam);
break;
case TE_MONSTER_HEATBEAM:
ent = CL_ParsePlayerBeam (clMedia.mod_monster_heatbeam);
break;
case TE_HEATBEAM_SPARKS:
cnt = 50;
MSG_ReadPos (&net_message, pos);
MSG_ReadDir (&net_message, dir);
r = 8;
magnitude = 60;
CL_ParticleSteamEffect (pos, dir, 240, 240, 240, -20, -20, -20, cnt, magnitude);
S_StartSound (pos, 0, 0, clMedia.sfx_lashit, 1, ATTN_NORM, 0);
break;
case TE_HEATBEAM_STEAM:
cnt = 20;
MSG_ReadPos (&net_message, pos);
MSG_ReadDir (&net_message, dir);
magnitude = 60;
CL_ParticleSteamEffect (pos, dir, 255, 150, 50, 0, -90, -30, cnt, magnitude);
CL_ParticlePlasmaBeamDecal (pos, dir, 10); // added burnmark
S_StartSound (pos, 0, 0, clMedia.sfx_lashit, 1, ATTN_NORM, 0);
break;
case TE_STEAM:
CL_ParseSteam();
break;
case TE_BUBBLETRAIL2:
cnt = 3; // was 8
MSG_ReadPos (&net_message, pos);
MSG_ReadPos (&net_message, pos2);
CL_BubbleTrail2 (pos, pos2, cnt);
S_StartSound (pos, 0, 0, clMedia.sfx_lashit, 1, ATTN_NORM, 0);
break;
case TE_MOREBLOOD:
MSG_ReadPos (&net_message, pos);
MSG_ReadDir (&net_message, dir);
// Psychospaz's enhanced particle code
CL_BloodHit (pos, dir);
CL_BloodHit (pos, dir);
break;
case TE_CHAINFIST_SMOKE:
dir[0]=0; dir[1]=0; dir[2]=1;
MSG_ReadPos(&net_message, pos);
// Psychospaz's enhanced particle code
CL_ParticleSmokeEffect (pos, dir, 8);
break;
case TE_ELECTRIC_SPARKS:
MSG_ReadPos (&net_message, pos);
MSG_ReadDir (&net_message, dir);
// new blue electric sparks
CL_ElectricParticles (pos, dir, 40);
//FIXME : replace or remove this sound
S_StartSound (pos, 0, 0, clMedia.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);
CL_Tracker_Explode (pos);
CL_Explosion_Decal (pos, 14, particle_trackermark);
S_StartSound (pos, 0, 0, clMedia.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;
//PGM
//==============
default:
Com_Error (ERR_DROP, "CL_ParseTEnt: bad type");
}
}
// backup of client angles
extern vec3_t old_viewangles;
/*
=================
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;
frame_t *oldframe; // added
player_state_t *ps, *ops;
qboolean firstperson, chasecam;
int handmult;
vec3_t thirdp_grapple_offset;
vec3_t grapple_offset_dir;
// chasecam grapple offset stuff
if (hand)
{
// if (hand->value == 2)
if (hand->integer == 2)
handmult = 1;
// else if (hand->value == 1)
else if (hand->integer == 1)
handmult = -1;
else
handmult = 1;
}
else
handmult = 1;
VectorSet(thirdp_grapple_offset, 6, 16, 16);
// end third person grapple
// update beams
for (i=0, b=cl_beams; i< MAX_BEAMS; i++, b++)
{
if (!b->model || b->endtime < cl.time)
continue;
// firstperson = ((b->entity == cl.playernum+1) && !cg_thirdperson->value);
// chasecam = ((b->entity == cl.playernum+1) && cg_thirdperson->value);
firstperson = ((b->entity == cl.playernum+1) && !cg_thirdperson->integer);
chasecam = ((b->entity == cl.playernum+1) && cg_thirdperson->integer);
// if coming from the player, update the start position
if (firstperson) // entity 0 is the world
{
VectorCopy (cl.refdef.vieworg, b->start);
b->start[2] -= 22; // adjust for view height
}
else if (chasecam)
{
vec3_t f,r,u;
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;
AngleVectors(old_viewangles, f, r, u);
VectorClear (grapple_offset_dir);
for (j=0; j<3; j++)
{
grapple_offset_dir[j] += f[j]*thirdp_grapple_offset[1];
grapple_offset_dir[j] += r[j]*thirdp_grapple_offset[0]*handmult;
grapple_offset_dir[j] += u[j]*(-thirdp_grapple_offset[2]);
}
for (j=0; j<3; j++)
b->start[j] = cl.predicted_origin[j] + ops->viewoffset[j]
+ cl.lerpfrac * (ps->viewoffset[j] - ops->viewoffset[j])
+ grapple_offset_dir[j];
// Com_Printf("%f, %f, %f\n", grapple_offset_dir[0], grapple_offset_dir[1], grapple_offset_dir[2]);
}
if (chasecam)
VectorCopy (b->start, org);
else
VectorAdd (b->start, b->offset, org);
// calculate pitch and yaw
VectorSubtract (b->end, org, dist);
if (dist[1] == 0 && dist[0] == 0)
{
yaw = 0;
if (dist[2] > 0)
pitch = 90;
else
pitch = 270;
}
else
{
// PMM - fixed to correct for pitch of 0
if (dist[0])
yaw = (atan2(dist[1], dist[0]) * 180 / M_PI);
else if (dist[1] > 0)
yaw = 90;
else
yaw = 270;
if (yaw < 0)
yaw += 360;
forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
pitch = (atan2(dist[2], forward) * -180.0 / M_PI);
if (pitch < 0)
pitch += 360.0;
}
// add new entities for the beams
d = VectorNormalize(dist);
memset (&ent, 0, sizeof(ent));
if (b->model == clMedia.mod_lightning)
{
model_length = 35.0;
d-= 20.0; // correction so it doesn't end in middle of tesla
}
else
{
model_length = 30.0;
}
steps = ceil(d/model_length);
len = (d-model_length)/(steps-1);
// PMM - special case for lightning model .. if the real length is shorter than the model,
// flip it around & draw it from the end to the start. This prevents the model from going
// through the tesla mine (instead it goes through the target)
if ((b->model == clMedia.mod_lightning) && (d <= model_length))
{
// Com_Printf ("special case\n");
VectorCopy (b->end, ent.origin);
// offset to push beam outside of tesla model (negative because dist is from end to start
// for this beam)
// for (j=0 ; j<3 ; j++)
// ent.origin[j] -= dist[j]*10.0;
ent.model = b->model;
ent.flags |= RF_FULLBRIGHT;
ent.angles[0] = pitch;
ent.angles[1] = yaw;
ent.angles[2] = rand()%360;
//AnglesToAxis(ent.angles, ent.axis);
V_AddEntity (&ent);
return;
}
while (d > 0)
{
VectorCopy (org, ent.origin);
ent.model = b->model;
if (b->model == clMedia.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;
}
//AnglesToAxis(ent.angles, ent.axis);
ent.flags |= RF_NOSHADOW; // beams don't cast shadows
// Com_Printf("B: %d -> %d\n", b->entity, b->dest_entity);
V_AddEntity (&ent);
for (j=0 ; j<3 ; j++)
org[j] += dist[j]*len;
d -= model_length;
}
}
}
/*
// Com_Printf ("Endpoint: %f %f %f\n", b->end[0], b->end[1], b->end[2]);
// Com_Printf ("Pred View Angles: %f %f %f\n", cl.predicted_angles[0], cl.predicted_angles[1], cl.predicted_angles[2]);
// Com_Printf ("Act View Angles: %f %f %f\n", cl.refdef.viewangles[0], cl.refdef.viewangles[1], cl.refdef.viewangles[2]);
// VectorCopy (cl.predicted_origin, b->start);
// b->start[2] += 22; // adjust for view height
// if (fabs(cl.refdef.vieworg[2] - b->start[2]) >= 10) {
// b->start[2] = cl.refdef.vieworg[2];
// }
// Com_Printf ("Time: %d %d %f\n", cl.time, cls.realtime, cls.renderFrameTime);
*/
/*
=================
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;
float model_length;
float hand_multiplier;
frame_t *oldframe;
player_state_t *ps, *ops;
qboolean firstperson, chasecam;
int newhandmult;
vec3_t thirdp_pbeam_offset;
vec3_t pbeam_offset_dir;
//PMM
if (hand)
{
// if (hand->value == 2)
if (hand->integer == 2)
hand_multiplier = 0;
// else if (hand->value == 1)
else if (hand->integer == 1)
hand_multiplier = -1;
else
hand_multiplier = 1;
}
else
{
hand_multiplier = 1;
}
//PMM
// chasecam beam offset stuff
newhandmult = hand_multiplier;
if (newhandmult == 0)
newhandmult = 1;
VectorSet(thirdp_pbeam_offset, 6.5, 0, 12);
// end chasecam beam offset stuff
// 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;
// firstperson = ((b->entity == cl.playernum+1) && !cg_thirdperson->value);
// chasecam = ((b->entity == cl.playernum+1) && cg_thirdperson->value);
firstperson = ((b->entity == cl.playernum+1) && !cg_thirdperson->integer);
chasecam = ((b->entity == cl.playernum+1) && cg_thirdperson->integer);
if(clMedia.mod_heatbeam && (b->model == clMedia.mod_heatbeam))
{
// if coming from the player, update the start position
if (firstperson || chasecam)
{
// 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;
// lerp for chasecam mode
if (chasecam)
{ // use player's original viewangles
AngleVectors(old_viewangles, f, r, u);
VectorClear (pbeam_offset_dir);
for (j=0; j<3; j++)
{
pbeam_offset_dir[j] += f[j]*thirdp_pbeam_offset[1];
pbeam_offset_dir[j] += r[j]*thirdp_pbeam_offset[0]*newhandmult;
pbeam_offset_dir[j] += u[j]*(-thirdp_pbeam_offset[2]);
}
for (j=0; j<3; j++)
b->start[j] = cl.predicted_origin[j] + ops->viewoffset[j]
+ cl.lerpfrac * (ps->viewoffset[j] - ops->viewoffset[j])
+ pbeam_offset_dir[j];
// Com_Printf("%f, %f, %f\n", pbeam_offset_dir[0], pbeam_offset_dir[1], pbeam_offset_dir[2]);
VectorMA (b->start, (newhandmult * b->offset[0]), r, org);
VectorMA ( org, b->offset[1], f, org);
VectorMA ( org, b->offset[2], u, org);
}
else // firstperson
{
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)) {
if ((hand) && (hand->integer == 2)) {
VectorMA (org, -1, cl.v_up, org);
}
// FIXME - take these out when final
VectorCopy (cl.v_right, r);
VectorCopy (cl.v_forward, f);
VectorCopy (cl.v_up, u);
}
}
else // some other player or monster
VectorCopy (b->start, org);
}
else // some other beam model
{
// if coming from the player, update the start position
// skip for chasecam mode
if (firstperson)
{
VectorCopy (cl.refdef.vieworg, b->start);
b->start[2] -= 22; // adjust for view height
}
VectorAdd (b->start, b->offset, org);
}
// calculate pitch and yaw
VectorSubtract (b->end, org, dist);
//PMM
if (clMedia.mod_heatbeam && (b->model == clMedia.mod_heatbeam) && (firstperson||chasecam))
{
vec_t len;
len = VectorLength (dist);
VectorScale (f, len, dist);
if (chasecam)
VectorMA (dist, (newhandmult * b->offset[0]), r, dist);
else
VectorMA (dist, (hand_multiplier * b->offset[0]), r, dist);
VectorMA (dist, b->offset[1], f, dist);
VectorMA (dist, b->offset[2], u, dist);
if (chasecam) {
VectorMA (dist, -(newhandmult * thirdp_pbeam_offset[0]), r, dist);
VectorMA (dist, thirdp_pbeam_offset[1], f, dist);
VectorMA (dist, thirdp_pbeam_offset[2], u, dist);
}
// if ((hand) && (hand->value == 2) && !chasecam)
if ((hand) && (hand->integer == 2) && !chasecam)
VectorMA (org, -1, cl.v_up, org);
}
//PMM
if (dist[1] == 0 && dist[0] == 0)
{
yaw = 0;
if (dist[2] > 0)
pitch = 90;
else
pitch = 270;
}
else
{
// PMM - fixed to correct for pitch of 0
if (dist[0])
yaw = (atan2(dist[1], dist[0]) * 180 / M_PI);
else if (dist[1] > 0)
yaw = 90;
else
yaw = 270;
if (yaw < 0)
yaw += 360;
forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
pitch = (atan2(dist[2], forward) * -180.0 / M_PI);
if (pitch < 0)
pitch += 360.0;
}
if (clMedia.mod_heatbeam && (b->model == clMedia.mod_heatbeam) )
{
if (!firstperson)
{
framenum = 2;
ent.angles[0] = -pitch;
ent.angles[1] = yaw + 180.0;
ent.angles[2] = 0;
// skip this for chasecam mode
if (!chasecam)
{
// Com_Printf ("%f %f - %f %f %f\n", -pitch, yaw+180.0, b->offset[0], b->offset[1], b->offset[2]);
AngleVectors(ent.angles, f, r, u);
// if it's a non-origin offset, it's a player, so use the hardcoded player offset
if (!VectorCompare (b->offset, vec3_origin))
{
VectorMA (org, -(b->offset[0])+1, r, org);
VectorMA (org, -(b->offset[1])+12, f, org); //was 0
VectorMA (org, -(b->offset[2])-5, u, org); //was -10
}
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
// also do this in chasecam mode
if ((clMedia.mod_heatbeam && (b->model == clMedia.mod_heatbeam) && (firstperson||chasecam)))
{
CL_HeatbeamParticles (org, dist);
}
// add new entities for the beams
d = VectorNormalize(dist);
memset (&ent, 0, sizeof(ent));
if (b->model == clMedia.mod_heatbeam)
{
model_length = 32.0;
}
else if (b->model == clMedia.mod_lightning)
{
model_length = 35.0;
d-= 20.0; // correction so it doesn't end in middle of tesla
}
else
{
model_length = 30.0;
}
steps = ceil(d/model_length);
len = (d-model_length)/(steps-1);
// PMM - special case for lightning model .. if the real length is shorter than the model,
// flip it around & draw it from the end to the start. This prevents the model from going
// through the tesla mine (instead it goes through the target)
if ((b->model == clMedia.mod_lightning) && (d <= model_length))
{
// Com_Printf ("special case\n");
VectorCopy (b->end, ent.origin);
// offset to push beam outside of tesla model (negative because dist is from end to start
// for this beam)
// for (j=0 ; j<3 ; j++)
// ent.origin[j] -= dist[j]*10.0;
ent.model = b->model;
ent.flags |= RF_FULLBRIGHT;
ent.angles[0] = pitch;
ent.angles[1] = yaw;
ent.angles[2] = rand()%360;
//AnglesToAxis(ent.angles, ent.axis);
ent.flags |= RF_NOSHADOW; // beams don't cast shadows
V_AddEntity (&ent);
return;
}
while (d > 0)
{
VectorCopy (org, ent.origin);
ent.model = b->model;
if (clMedia.mod_heatbeam && (b->model == clMedia.mod_heatbeam))
{
// ent.flags |= RF_FULLBRIGHT|RF_TRANSLUCENT;
// ent.alpha = 0.6;
ent.flags |= RF_FULLBRIGHT;
ent.angles[0] = -pitch;
ent.angles[1] = yaw + 180.0;
ent.angles[2] = (cl.time) % 360;
// ent.angles[2] = rand()%360;
ent.frame = framenum;
}
else if (b->model == clMedia.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;
}
//AnglesToAxis(ent.angles, ent.axis);
ent.flags |= RF_NOSHADOW; // beams don't cast shadows
// Com_Printf("B: %d -> %d\n", b->entity, b->dest_entity);
V_AddEntity (&ent);
for (j=0 ; j<3 ; j++)
org[j] += dist[j]*len;
d -= model_length;
}
}
}
/*
=================
CL_AddExplosions
=================
*/
void CL_AddExplosions (void)
{
entity_t *ent;
int i;
explosion_t *ex;
float frac;
int f;
memset (&ent, 0, sizeof(ent));
for (i=0, ex=cl_explosions ; i< MAX_EXPLOSIONS ; i++, ex++)
{
if (ex->type == ex_free)
continue;
frac = (cl.time - ex->start)/100.0;
f = floor(frac);
ent = &ex->ent;
switch (ex->type)
{
case ex_mflash:
if (f >= ex->frames-1)
ex->type = ex_free;
break;
case ex_misc:
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;
}
// since nothing else uses this,
// I'd just thought I'd change it...
ent->alpha = max((0.7 - f*0.10), 0.10);
//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->oldframe = ex->baseframe + f;
ent->backlerp = 1.0 - cl.lerpfrac;
//AnglesToAxis(ent->angles, ent->axis);
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);
}
}
/* 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))
{
// Com_Printf ("think %d %d %d\n", cl.time, s->nextthink, s->thinkinterval);
s->think (s);
}
else if (s->endtime < cl.time)
s->id = 0;
}
}
/*
=================
CL_AddTEnts
=================
*/
void CL_AddTEnts (void)
{
CL_AddBeams ();
// PMM - draw plasma beams
CL_AddPlayerBeams ();
CL_AddExplosions ();
CL_AddLasers ();
// PMM - set up sustain
CL_ProcessSustain();
}