mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2025-01-30 12:10:43 +00:00
95295401a4
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.
2095 lines
53 KiB
C
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();
|
|
}
|