6e3f69f504
fte particle scripts are disabled (classic works). I'll fix these in the new year. Redid framestate stuff again. Slightly better now, but this is the bulk of the changes here. Reworked the renderqueue to provide batches of items instead of individual items. This cleans up the particle rendering code significantly, and is a step towards multiple concurrent particle systems. fte's scripted particles are broken as I'm trying to find a way to rework them to batch types together, rather than having to restart each batch after each particle when you have two particles in a trail. I'll fix it some time. Reworked some alias model code regarding skeletal models. Added some conceptual skeletal bone control builtins available to csqc. Currently it can query the bone names and save off animation states, but can't animate - its just not complete. Added more info to glsl custom shaders. Updated surface sorting on halflife maps to properly cope with alphaed entities, rather than just texture-based blends (q2-style). git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3095 fc73d0e0-1445-4013-8a0c-d673dee63da5
2881 lines
70 KiB
C
2881 lines
70 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
// cl_tent.c -- client side temporary entities
|
|
|
|
#include "quakedef.h"
|
|
#include "particles.h"
|
|
entity_state_t *CL_FindPacketEntity(int num);
|
|
|
|
#define R_AddDecals(a) //disabled for now
|
|
|
|
int
|
|
pt_gunshot=P_INVALID,
|
|
ptdp_gunshotquad=P_INVALID,
|
|
pt_spike=P_INVALID,
|
|
ptdp_spikequad=P_INVALID,
|
|
pt_superspike=P_INVALID,
|
|
ptdp_superspikequad=P_INVALID,
|
|
pt_wizspike=P_INVALID,
|
|
pt_knightspike=P_INVALID,
|
|
pt_explosion=P_INVALID,
|
|
ptdp_explosionquad=P_INVALID,
|
|
pt_tarexplosion=P_INVALID,
|
|
pt_teleportsplash=P_INVALID,
|
|
pt_lavasplash=P_INVALID,
|
|
ptdp_smallflash=P_INVALID,
|
|
ptdp_flamejet=P_INVALID,
|
|
ptdp_flame=P_INVALID,
|
|
ptdp_blood=P_INVALID,
|
|
ptdp_spark=P_INVALID,
|
|
ptdp_plasmaburn=P_INVALID,
|
|
ptdp_tei_g3=P_INVALID,
|
|
ptdp_tei_smoke=P_INVALID,
|
|
ptdp_tei_bigexplosion=P_INVALID,
|
|
ptdp_tei_plasmahit=P_INVALID,
|
|
ptdp_stardust=P_INVALID,
|
|
rt_rocket=P_INVALID,
|
|
rt_grenade=P_INVALID,
|
|
rt_blood=P_INVALID,
|
|
rt_wizspike=P_INVALID,
|
|
rt_slightblood=P_INVALID,
|
|
rt_knightspike=P_INVALID,
|
|
rt_vorespike=P_INVALID,
|
|
rtdp_neharasmoke=P_INVALID,
|
|
rtdp_nexuizplasma=P_INVALID,
|
|
rtdp_glowtrail=P_INVALID,
|
|
|
|
ptqw_blood=P_INVALID,
|
|
ptqw_lightningblood=P_INVALID,
|
|
|
|
ptq2_blood=P_INVALID,
|
|
rtq2_railtrail=P_INVALID,
|
|
rtq2_blastertrail=P_INVALID,
|
|
ptq2_blasterparticles=P_INVALID,
|
|
rtq2_bubbletrail=P_INVALID,
|
|
rtq2_gib=P_INVALID,
|
|
rtq2_rocket=P_INVALID,
|
|
rtq2_grenade=P_INVALID,
|
|
|
|
rtqw_railtrail=P_INVALID,
|
|
rtfte_lightning1=P_INVALID,
|
|
ptfte_lightning1_end=P_INVALID,
|
|
rtfte_lightning2=P_INVALID,
|
|
ptfte_lightning2_end=P_INVALID,
|
|
rtfte_lightning3=P_INVALID,
|
|
ptfte_lightning3_end=P_INVALID,
|
|
ptfte_bullet=P_INVALID,
|
|
ptfte_superbullet=P_INVALID;
|
|
|
|
#ifdef Q2CLIENT
|
|
typedef enum
|
|
{
|
|
Q2TE_GUNSHOT, //0
|
|
Q2TE_BLOOD,
|
|
Q2TE_BLASTER,
|
|
Q2TE_RAILTRAIL,
|
|
Q2TE_SHOTGUN,
|
|
Q2TE_EXPLOSION1,
|
|
Q2TE_EXPLOSION2,
|
|
Q2TE_ROCKET_EXPLOSION,
|
|
Q2TE_GRENADE_EXPLOSION,
|
|
Q2TE_SPARKS,
|
|
Q2TE_SPLASH, //10
|
|
Q2TE_BUBBLETRAIL,
|
|
Q2TE_SCREEN_SPARKS,
|
|
Q2TE_SHIELD_SPARKS,
|
|
Q2TE_BULLET_SPARKS,
|
|
Q2TE_LASER_SPARKS,
|
|
Q2TE_PARASITE_ATTACK,
|
|
Q2TE_ROCKET_EXPLOSION_WATER,
|
|
Q2TE_GRENADE_EXPLOSION_WATER,
|
|
Q2TE_MEDIC_CABLE_ATTACK,
|
|
Q2TE_BFG_EXPLOSION, //20
|
|
Q2TE_BFG_BIGEXPLOSION,
|
|
Q2TE_BOSSTPORT, // used as '22' in a map, so DON'T RENUMBER!!!
|
|
Q2TE_BFG_LASER,
|
|
Q2TE_GRAPPLE_CABLE,
|
|
Q2TE_WELDING_SPARKS,
|
|
Q2TE_GREENBLOOD,
|
|
Q2TE_BLUEHYPERBLASTER,
|
|
Q2TE_PLASMA_EXPLOSION,
|
|
Q2TE_TUNNEL_SPARKS,
|
|
//ROGUE
|
|
Q2TE_BLASTER2, //30
|
|
Q2TE_RAILTRAIL2,
|
|
Q2TE_FLAME,
|
|
Q2TE_LIGHTNING,
|
|
Q2TE_DEBUGTRAIL,
|
|
Q2TE_PLAIN_EXPLOSION,
|
|
Q2TE_FLASHLIGHT,
|
|
Q2TE_FORCEWALL,
|
|
Q2TE_HEATBEAM,
|
|
Q2TE_MONSTER_HEATBEAM,
|
|
Q2TE_STEAM, //40
|
|
Q2TE_BUBBLETRAIL2,
|
|
Q2TE_MOREBLOOD,
|
|
Q2TE_HEATBEAM_SPARKS,
|
|
Q2TE_HEATBEAM_STEAM,
|
|
Q2TE_CHAINFIST_SMOKE,
|
|
Q2TE_ELECTRIC_SPARKS,
|
|
Q2TE_TRACKER_EXPLOSION,
|
|
Q2TE_TELEPORT_EFFECT,
|
|
Q2TE_DBALL_GOAL,
|
|
Q2TE_WIDOWBEAMOUT, //50
|
|
Q2TE_NUKEBLAST,
|
|
Q2TE_WIDOWSPLASH,
|
|
Q2TE_EXPLOSION1_BIG,
|
|
Q2TE_EXPLOSION1_NP,
|
|
Q2TE_FLECHETTE,
|
|
//ROGUE
|
|
|
|
|
|
//CODERED
|
|
CRTE_LEADERBLASTER, //56
|
|
CRTE_BLASTER_MUZZLEFLASH,
|
|
CRTE_BLUE_MUZZLEFLASH,
|
|
CRTE_SMART_MUZZLEFLASH,
|
|
CRTE_LEADERFIELD, //60
|
|
CRTE_DEATHFIELD,
|
|
CRTE_BLASTERBEAM,
|
|
CRTE_STAIN,
|
|
CRTE_FIRE,
|
|
CRTE_CABLEGUT,
|
|
CRTE_SMOKE
|
|
//CODERED
|
|
} temp_event_t;
|
|
|
|
#define Q2SPLASH_UNKNOWN 0
|
|
#define Q2SPLASH_SPARKS 1
|
|
#define Q2SPLASH_BLUE_WATER 2
|
|
#define Q2SPLASH_BROWN_WATER 3
|
|
#define Q2SPLASH_SLIME 4
|
|
#define Q2SPLASH_LAVA 5
|
|
#define Q2SPLASH_BLOOD 6
|
|
#endif
|
|
|
|
|
|
|
|
// hexen 2
|
|
#define TE_STREAM_CHAIN 25
|
|
#define TE_STREAM_SUNSTAFF1 26
|
|
#define TE_STREAM_SUNSTAFF2 27
|
|
#define TE_STREAM_LIGHTNING 28
|
|
#define TE_STREAM_COLORBEAM 29
|
|
#define TE_STREAM_ICECHUNKS 30
|
|
#define TE_STREAM_GAZE 31
|
|
#define TE_STREAM_FAMINE 32
|
|
|
|
|
|
#define MAX_BEAMS 64
|
|
typedef struct
|
|
{
|
|
int entity;
|
|
short tag;
|
|
qbyte flags;
|
|
qbyte type;
|
|
qbyte skin;
|
|
struct model_s *model;
|
|
float endtime;
|
|
float alpha;
|
|
vec3_t start, end;
|
|
int particleeffect;
|
|
trailstate_t *trailstate;
|
|
trailstate_t *emitstate;
|
|
} beam_t;
|
|
|
|
beam_t cl_beams[MAX_BEAMS];
|
|
|
|
#define MAX_EXPLOSIONS 32
|
|
typedef struct
|
|
{
|
|
vec3_t origin;
|
|
vec3_t oldorigin;
|
|
|
|
int firstframe;
|
|
int numframes;
|
|
|
|
int type;
|
|
vec3_t angles;
|
|
int flags;
|
|
float alpha;
|
|
float start;
|
|
float framerate;
|
|
model_t *model;
|
|
int skinnum;
|
|
} explosion_t;
|
|
|
|
explosion_t cl_explosions[MAX_EXPLOSIONS];
|
|
|
|
static int explosions_running;
|
|
static int beams_running;
|
|
|
|
sfx_t *cl_sfx_wizhit;
|
|
sfx_t *cl_sfx_knighthit;
|
|
sfx_t *cl_sfx_tink1;
|
|
sfx_t *cl_sfx_ric1;
|
|
sfx_t *cl_sfx_ric2;
|
|
sfx_t *cl_sfx_ric3;
|
|
sfx_t *cl_sfx_r_exp3;
|
|
|
|
cvar_t cl_expsprite = SCVAR("cl_expsprite", "0");
|
|
cvar_t r_explosionlight = SCVARC("r_explosionlight", "1", Cvar_Limiter_ZeroToOne_Callback);
|
|
cvar_t cl_truelightning = SCVARF("cl_truelightning", "0", CVAR_SEMICHEAT);
|
|
cvar_t cl_beam_trace = SCVAR("cl_beam_trace", "0");
|
|
|
|
typedef struct {
|
|
sfx_t **sfx;
|
|
char *efname;
|
|
} tentsfx_t;
|
|
tentsfx_t tentsfx[] =
|
|
{
|
|
{&cl_sfx_wizhit, "wizard/hit.wav"},
|
|
{&cl_sfx_knighthit, "hknight/hit.wav"},
|
|
{&cl_sfx_tink1, "weapons/tink1.wav"},
|
|
{&cl_sfx_ric1, "weapons/ric1.wav"},
|
|
{&cl_sfx_ric2, "weapons/ric2.wav"},
|
|
{&cl_sfx_ric3, "weapons/ric3.wav"},
|
|
{&cl_sfx_r_exp3, "weapons/r_exp3.wav"}
|
|
};
|
|
|
|
vec3_t playerbeam_end[MAX_SPLITS];
|
|
/*
|
|
=================
|
|
CL_ParseTEnts
|
|
=================
|
|
*/
|
|
void CL_InitTEnts (void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < sizeof(tentsfx)/sizeof(tentsfx[0]); i++)
|
|
{
|
|
if (COM_FCheckExists(va("sound/%s", tentsfx[i].efname)))
|
|
*tentsfx[i].sfx = S_PrecacheSound (tentsfx[i].efname);
|
|
else
|
|
*tentsfx[i].sfx = NULL;
|
|
}
|
|
|
|
Cvar_Register (&cl_expsprite, "Temporary entity control");
|
|
Cvar_Register (&cl_truelightning, "Temporary entity control");
|
|
Cvar_Register (&cl_beam_trace, "Temporary entity control");
|
|
Cvar_Register (&r_explosionlight, "Temporary entity control");
|
|
}
|
|
|
|
void CL_RegisterParticles(void)
|
|
{
|
|
pt_gunshot = P_ParticleTypeForName("TE_GUNSHOT"); /*shotgun*/
|
|
ptdp_gunshotquad = P_ParticleTypeForName("TE_GUNSHOTQUAD"); /*DP: quadded shotgun*/
|
|
pt_spike = P_ParticleTypeForName("TE_SPIKE"); /*nailgun*/
|
|
ptdp_spikequad = P_ParticleTypeForName("TE_SPIKEQUAD"); /*DP: quadded nailgun*/
|
|
pt_superspike = P_ParticleTypeForName("TE_SUPERSPIKE"); /*nailgun*/
|
|
ptdp_superspikequad = P_ParticleTypeForName("TE_SUPERSPIKEQUAD"); /*DP: quadded nailgun*/
|
|
pt_wizspike = P_ParticleTypeForName("TE_WIZSPIKE"); //scrag missile impact
|
|
pt_knightspike = P_ParticleTypeForName("TE_KNIGHTSPIKE"); //hellknight missile impact
|
|
pt_explosion = P_ParticleTypeForName("TE_EXPLOSION");/*rocket/grenade launcher impacts/far too many things*/
|
|
ptdp_explosionquad = P_ParticleTypeForName("TE_EXPLOSIONQUAD"); /*nailgun*/
|
|
pt_tarexplosion = P_ParticleTypeForName("TE_TAREXPLOSION");//tarbaby/spawn dying.
|
|
pt_teleportsplash = P_ParticleTypeForName("TE_TELEPORT");/*teleporters*/
|
|
pt_lavasplash = P_ParticleTypeForName("TE_LAVASPLASH"); //e1m7 boss dying.
|
|
ptdp_smallflash = P_ParticleTypeForName("TE_SMALLFLASH"); //DP:
|
|
ptdp_flamejet = P_ParticleTypeForName("TE_FLAMEJET"); //DP:
|
|
ptdp_flame = P_ParticleTypeForName("EF_FLAME"); //DP:
|
|
ptdp_blood = P_ParticleTypeForName("TE_BLOOD"); /*when you hit something with the shotgun/axe/nailgun - nq uses the general particle builtin*/
|
|
ptdp_spark = P_ParticleTypeForName("TE_SPARK");//DPTE_SPARK
|
|
ptdp_plasmaburn = P_ParticleTypeForName("TE_PLASMABURN");
|
|
ptdp_tei_g3 = P_ParticleTypeForName("TE_TEI_G3");
|
|
ptdp_tei_smoke = P_ParticleTypeForName("TE_TEI_SMOKE");
|
|
ptdp_tei_bigexplosion = P_ParticleTypeForName("TE_TEI_BIGEXPLOSION");
|
|
ptdp_tei_plasmahit = P_ParticleTypeForName("TE_TEI_PLASMAHIT");
|
|
ptdp_stardust = P_ParticleTypeForName("EF_STARDUST");
|
|
rt_rocket = P_ParticleTypeForName("TR_ROCKET"); /*rocket trail*/
|
|
rt_grenade = P_ParticleTypeForName("TR_GRENADE"); /*grenade trail*/
|
|
rt_blood = P_ParticleTypeForName("TR_BLOOD"); /*blood trail*/
|
|
rt_wizspike = P_ParticleTypeForName("TR_WIZSPIKE");
|
|
rt_slightblood = P_ParticleTypeForName("TR_SLIGHTBLOOD");
|
|
rt_knightspike = P_ParticleTypeForName("TR_KNIGHTSPIKE");
|
|
rt_vorespike = P_ParticleTypeForName("TR_VORESPIKE");
|
|
rtdp_neharasmoke = P_ParticleTypeForName("TR_NEHAHRASMOKE");
|
|
rtdp_nexuizplasma = P_ParticleTypeForName("TR_NEXUIZPLASMA");
|
|
rtdp_glowtrail = P_ParticleTypeForName("TR_GLOWTRAIL");
|
|
/*internal to psystem*/ P_ParticleTypeForName("SVC_PARTICLE");
|
|
|
|
ptqw_blood = P_ParticleTypeForName("TE_BLOOD");
|
|
ptqw_lightningblood = P_ParticleTypeForName("TE_LIGHTNINGBLOOD");
|
|
|
|
ptq2_blood = P_ParticleTypeForName("TE_BLOOD");
|
|
rtq2_railtrail = P_ParticleTypeForName("TR_RAILTRAIL");
|
|
rtq2_blastertrail = P_ParticleTypeForName("TR_BLASTERTRAIL");
|
|
ptq2_blasterparticles = P_ParticleTypeForName("TE_BLASTERPARTICLES");
|
|
rtq2_bubbletrail = P_ParticleTypeForName("TE_BUBBLETRAIL");
|
|
rtq2_gib = P_ParticleTypeForName("TR_GIB");
|
|
rtq2_rocket = P_ParticleTypeForName("TR_ROCKET");
|
|
rtq2_grenade = P_ParticleTypeForName("TR_GRENADE");
|
|
|
|
rtqw_railtrail = P_ParticleTypeForName("TE_RAILTRAIL");
|
|
rtfte_lightning1 = P_ParticleTypeForName("TE_LIGHTNING1");
|
|
ptfte_lightning1_end = P_ParticleTypeForName("TE_LIGHTNING1_END");
|
|
rtfte_lightning2 = P_ParticleTypeForName("TE_LIGHTNING2");
|
|
ptfte_lightning2_end = P_ParticleTypeForName("TE_LIGHTNING2_END");
|
|
rtfte_lightning3 = P_ParticleTypeForName("TE_LIGHTNING3");
|
|
ptfte_lightning3_end = P_ParticleTypeForName("TE_LIGHTNING3_END");
|
|
ptfte_bullet = P_ParticleTypeForName("TE_BULLET");
|
|
ptfte_superbullet = P_ParticleTypeForName("TE_SUPERBULLET");
|
|
}
|
|
|
|
#ifdef Q2CLIENT
|
|
enum {
|
|
q2cl_mod_explode,
|
|
q2cl_mod_smoke,
|
|
q2cl_mod_flash,
|
|
q2cl_mod_parasite_segment,
|
|
q2cl_mod_grapple_cable,
|
|
q2cl_mod_parasite_tip,
|
|
q2cl_mod_explo4,
|
|
q2cl_mod_bfg_explo,
|
|
q2cl_mod_powerscreen,
|
|
q2cl_mod_max
|
|
};
|
|
typedef struct {
|
|
char *modelname;
|
|
|
|
} tentmodels_t;
|
|
tentmodels_t q2tentmodels[q2cl_mod_max] = {
|
|
{"models/objects/explode/tris.md2"},
|
|
{"models/objects/smoke/tris.md2"},
|
|
{"models/objects/flash/tris.md2"},
|
|
{"models/monsters/parasite/segment/tris.md2"},
|
|
{"models/ctf/segment/tris.md2"},
|
|
{"models/monsters/parasite/tip/tris.md2"},
|
|
{"models/objects/r_explode/tris.md2"},
|
|
{"sprites/s_bfg2.sp2"},
|
|
{"models/items/armor/effect/tris.md2"}
|
|
};
|
|
|
|
int CLQ2_RegisterTEntModels (void)
|
|
{
|
|
// int i;
|
|
// for (i = 0; i < q2cl_mod_max; i++)
|
|
// if (!CL_CheckOrDownloadFile(q2tentmodels[i].modelname, false))
|
|
// return false;
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
/*
|
|
=================
|
|
CL_ClearTEnts
|
|
=================
|
|
*/
|
|
void CL_ClearTEnts (void)
|
|
{
|
|
memset (&cl_beams, 0, sizeof(cl_beams));
|
|
memset (&cl_explosions, 0, sizeof(cl_explosions));
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_AllocExplosion
|
|
=================
|
|
*/
|
|
explosion_t *CL_AllocExplosion (void)
|
|
{
|
|
int i;
|
|
float time;
|
|
int index;
|
|
|
|
for (i=0; i < explosions_running; i++)
|
|
{
|
|
if (!cl_explosions[i].model)
|
|
{
|
|
cl_explosions[i].firstframe = -1;
|
|
cl_explosions[i].framerate = 10;
|
|
return &cl_explosions[i];
|
|
}
|
|
}
|
|
|
|
if (i == explosions_running && i != MAX_EXPLOSIONS)
|
|
{
|
|
explosions_running++;
|
|
cl_explosions[i].firstframe = -1;
|
|
cl_explosions[i].framerate = 10;
|
|
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;
|
|
}
|
|
cl_explosions[index].firstframe = -1;
|
|
cl_explosions[index].framerate = 10;
|
|
return &cl_explosions[index];
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_ParseBeam
|
|
=================
|
|
*/
|
|
beam_t *CL_NewBeam (int entity, int tag)
|
|
{
|
|
beam_t *b;
|
|
int i;
|
|
|
|
// override any beam with the same entity (unless they used world)
|
|
if (entity)
|
|
{
|
|
for (i=0, b=cl_beams; i < beams_running; i++, b++)
|
|
if (b->entity == entity && b->tag == tag)
|
|
{
|
|
return b;
|
|
}
|
|
}
|
|
|
|
// find a free beam
|
|
for (i=0, b=cl_beams; i < beams_running; i++, b++)
|
|
{
|
|
if (!b->model)
|
|
{
|
|
return b;
|
|
}
|
|
}
|
|
|
|
if (i == beams_running && i != MAX_BEAMS)
|
|
{
|
|
beams_running++;
|
|
return &cl_beams[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#define STREAM_ATTACHED 16
|
|
#define STREAM_TRANSLUCENT 32
|
|
void CL_AddBeam (int tent, int ent, vec3_t start, vec3_t end) //fixme: use TE_ numbers instead of 0 - 5
|
|
{
|
|
beam_t *b;
|
|
|
|
model_t *m;
|
|
int btype, etype;
|
|
int i;
|
|
vec3_t impact, normal;
|
|
vec3_t extra;
|
|
|
|
switch(tent)
|
|
{
|
|
case 0:
|
|
if (ent < 0 && ent >= -512) //a zquake concept. ent between -1 and -maxplayers is to be taken to be a railtrail from a particular player instead of a beam.
|
|
{
|
|
// TODO: add support for those finnicky colored railtrails...
|
|
if (P_ParticleTrail(start, end, rtqw_railtrail, NULL))
|
|
P_ParticleTrailIndex(start, end, 208, 8, NULL);
|
|
return;
|
|
}
|
|
default:
|
|
m = Mod_ForName("progs/bolt.mdl", false);
|
|
btype = rtfte_lightning1;
|
|
etype = ptfte_lightning1_end;
|
|
break;
|
|
case 1:
|
|
if (ent < 0 && ent >= -MAX_CLIENTS) //based on the railgun concept - this adds a rogue style TE_BEAM effect.
|
|
{
|
|
case 5:
|
|
m = Mod_ForName("progs/beam.mdl", false); //remember to precache!
|
|
btype = P_FindParticleType("te_beam");
|
|
etype = P_FindParticleType("te_beam_end");
|
|
}
|
|
else
|
|
{
|
|
m = Mod_ForName("progs/bolt2.mdl", false);
|
|
btype = rtfte_lightning2;
|
|
etype = ptfte_lightning2_end;
|
|
}
|
|
break;
|
|
case 2:
|
|
m = Mod_ForName("progs/bolt3.mdl", false);
|
|
btype = rtfte_lightning3;
|
|
etype = ptfte_lightning3_end;
|
|
break;
|
|
#ifdef Q2CLIENT
|
|
case 3:
|
|
m = Mod_ForName(q2tentmodels[q2cl_mod_parasite_segment].modelname, false);
|
|
btype = P_FindParticleType("te_parasite_attack");
|
|
etype = P_FindParticleType("te_parasite_attack_end");
|
|
break;
|
|
case 4:
|
|
m = Mod_ForName(q2tentmodels[q2cl_mod_grapple_cable].modelname, false);
|
|
btype = P_FindParticleType("te_grapple_cable");
|
|
etype = P_FindParticleType("te_grapple_cable_end");
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
// save end position for truelightning
|
|
if (ent)
|
|
{
|
|
for (i = 0; i < cl.splitclients; i++)
|
|
{
|
|
if (ent == (autocam[i]?(spec_track[i]+1):(cl.playernum[i]+1)))
|
|
{
|
|
VectorCopy(end, playerbeam_end[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (etype >= 0 && cls.state == ca_active && etype != P_INVALID)
|
|
{
|
|
if (cl_beam_trace.value)
|
|
{
|
|
VectorSubtract(end, start, normal);
|
|
VectorNormalize(normal);
|
|
VectorMA(end, 4, normal, extra); //extend the end-point by four
|
|
if (!TraceLineN(start, extra, impact, normal))
|
|
etype = -1;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(end, impact);
|
|
normal[0] = normal[1] = normal[2] = 0;
|
|
}
|
|
}
|
|
|
|
b = CL_NewBeam(ent, -1);
|
|
if (!b)
|
|
{
|
|
Con_Printf ("beam list overflow!\n");
|
|
return;
|
|
}
|
|
|
|
b->entity = ent;
|
|
b->model = m;
|
|
b->tag = -1;
|
|
b->flags |= /*STREAM_ATTACHED|*/1;
|
|
b->endtime = cl.time + 0.2;
|
|
b->alpha = 1;
|
|
b->particleeffect = btype;
|
|
VectorCopy (start, b->start);
|
|
VectorCopy (end, b->end);
|
|
|
|
if (etype >= 0)
|
|
{
|
|
P_RunParticleEffectState (impact, normal, 1, etype, &(b->emitstate));
|
|
R_AddDecals(end);
|
|
R_AddStain(end, -10, -10, -10, 20);
|
|
}
|
|
}
|
|
void CL_ParseBeam (int tent)
|
|
{
|
|
int ent;
|
|
vec3_t start, end;
|
|
|
|
ent = MSG_ReadShort ();
|
|
|
|
start[0] = MSG_ReadCoord ();
|
|
start[1] = MSG_ReadCoord ();
|
|
start[2] = MSG_ReadCoord ();
|
|
|
|
end[0] = MSG_ReadCoord ();
|
|
end[1] = MSG_ReadCoord ();
|
|
end[2] = MSG_ReadCoord ();
|
|
|
|
CL_AddBeam(tent, ent, start, end);
|
|
}
|
|
void CL_ParseStream (int type)
|
|
{
|
|
int ent;
|
|
vec3_t start, end;
|
|
beam_t *b, *b2;
|
|
int flags;
|
|
int tag;
|
|
float duration;
|
|
int skin;
|
|
|
|
ent = MSG_ReadShort();
|
|
flags = MSG_ReadByte();
|
|
tag = flags&15;
|
|
flags-=tag;
|
|
duration = (float)MSG_ReadByte()*0.05;
|
|
skin = 0;
|
|
if(type == TE_STREAM_COLORBEAM)
|
|
{
|
|
skin = MSG_ReadByte();
|
|
}
|
|
start[0] = MSG_ReadCoord();
|
|
start[1] = MSG_ReadCoord();
|
|
start[2] = MSG_ReadCoord();
|
|
end[0] = MSG_ReadCoord();
|
|
end[1] = MSG_ReadCoord();
|
|
end[2] = MSG_ReadCoord();
|
|
|
|
b = CL_NewBeam(ent, tag);
|
|
if (!b)
|
|
{
|
|
Con_Printf ("beam list overflow!\n");
|
|
return;
|
|
}
|
|
|
|
b->entity = ent;
|
|
b->tag = tag;
|
|
b->flags = flags;
|
|
b->model = NULL;
|
|
b->endtime = cl.time + duration;
|
|
b->alpha = 1;
|
|
VectorCopy (start, b->start);
|
|
VectorCopy (end, b->end);
|
|
|
|
switch(type)
|
|
{
|
|
case TE_STREAM_ICECHUNKS:
|
|
b->model = Mod_ForName("models/stice.mdl", true);
|
|
b->flags |= 2;
|
|
b->particleeffect = P_FindParticleType("te_stream_icechunks");
|
|
R_AddStain(end, -10, -10, 0, 20);
|
|
break;
|
|
case TE_STREAM_SUNSTAFF1:
|
|
b->model = Mod_ForName("models/stsunsf1.mdl", true);
|
|
b->particleeffect = P_FindParticleType("te_stream_sunstaff1");
|
|
if (b->particleeffect < 0)
|
|
{
|
|
b2 = CL_NewBeam(ent, tag+128);
|
|
if (b2)
|
|
{
|
|
memcpy(b2, b, sizeof(*b2));
|
|
b2->model = Mod_ForName("models/stsunsf2.mdl", true);
|
|
b2->alpha = 0.5;
|
|
}
|
|
}
|
|
break;
|
|
case TE_STREAM_SUNSTAFF2:
|
|
b->model = Mod_ForName("models/stsunsf1.mdl", true);
|
|
b->particleeffect = P_FindParticleType("te_stream_sunstaff2");
|
|
R_AddStain(end, -10, -10, -10, 20);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_ParseTEnt
|
|
=================
|
|
*/
|
|
|
|
#ifdef NQPROT
|
|
void CL_ParseTEnt (qboolean nqprot)
|
|
#else
|
|
void CL_ParseTEnt (void)
|
|
#endif
|
|
{
|
|
#ifndef NQPROT
|
|
#define nqprot false //it's easier
|
|
#endif
|
|
int type;
|
|
vec3_t pos, pos2;
|
|
dlight_t *dl;
|
|
int rnd;
|
|
// explosion_t *ex;
|
|
int cnt, colour;
|
|
|
|
type = MSG_ReadByte ();
|
|
|
|
#ifdef CSQC_DAT
|
|
if (type != TE_GUNSHOT)
|
|
{
|
|
//I know I'm going to regret this.
|
|
if (CSQC_ParseTempEntity((unsigned char)type))
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
switch (type)
|
|
{
|
|
case TE_WIZSPIKE: // spike hitting wall
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, -10, 0, -10, 20);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_wizspike))
|
|
P_RunParticleEffect (pos, vec3_origin, 20, 30);
|
|
|
|
S_StartSound (-2, 0, cl_sfx_wizhit, pos, 1, 1);
|
|
break;
|
|
|
|
case TE_KNIGHTSPIKE: // spike hitting wall
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, -10, -10, -10, 20);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_knightspike))
|
|
P_RunParticleEffect (pos, vec3_origin, 226, 20);
|
|
|
|
S_StartSound (-2, 0, cl_sfx_knighthit, pos, 1, 1);
|
|
break;
|
|
|
|
case DPTE_SPIKEQUAD:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, -10, -10, -10, 20);
|
|
R_AddDecals(pos);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, ptdp_spikequad))
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_spike))
|
|
if (P_RunParticleEffectType(pos, NULL, 10, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 10);
|
|
|
|
if ( rand() % 5 )
|
|
S_StartSound (-2, 0, cl_sfx_tink1, pos, 1, 1);
|
|
else
|
|
{
|
|
rnd = rand() & 3;
|
|
if (rnd == 1)
|
|
S_StartSound (-2, 0, cl_sfx_ric1, pos, 1, 1);
|
|
else if (rnd == 2)
|
|
S_StartSound (-2, 0, cl_sfx_ric2, pos, 1, 1);
|
|
else
|
|
S_StartSound (-2, 0, cl_sfx_ric3, pos, 1, 1);
|
|
}
|
|
break;
|
|
case TE_SPIKE: // spike hitting wall
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, -10, -10, -10, 20);
|
|
R_AddDecals(pos);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_spike))
|
|
if (P_RunParticleEffectType(pos, NULL, 10, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 10);
|
|
|
|
if ( rand() % 5 )
|
|
S_StartSound (-2, 0, cl_sfx_tink1, pos, 1, 1);
|
|
else
|
|
{
|
|
rnd = rand() & 3;
|
|
if (rnd == 1)
|
|
S_StartSound (-2, 0, cl_sfx_ric1, pos, 1, 1);
|
|
else if (rnd == 2)
|
|
S_StartSound (-2, 0, cl_sfx_ric2, pos, 1, 1);
|
|
else
|
|
S_StartSound (-2, 0, cl_sfx_ric3, pos, 1, 1);
|
|
}
|
|
break;
|
|
case DPTE_SUPERSPIKEQUAD: // super spike hitting wall
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, -10, -10, -10, 20);
|
|
R_AddDecals(pos);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, ptdp_superspikequad))
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_superspike))
|
|
if (P_RunParticleEffectType(pos, NULL, 2, pt_spike))
|
|
if (P_RunParticleEffectType(pos, NULL, 20, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 20);
|
|
|
|
if ( rand() % 5 )
|
|
S_StartSound (-2, 0, cl_sfx_tink1, pos, 1, 1);
|
|
else
|
|
{
|
|
rnd = rand() & 3;
|
|
if (rnd == 1)
|
|
S_StartSound (-2, 0, cl_sfx_ric1, pos, 1, 1);
|
|
else if (rnd == 2)
|
|
S_StartSound (-2, 0, cl_sfx_ric2, pos, 1, 1);
|
|
else
|
|
S_StartSound (-2, 0, cl_sfx_ric3, pos, 1, 1);
|
|
}
|
|
break;
|
|
case TE_SUPERSPIKE: // super spike hitting wall
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, -10, -10, -10, 20);
|
|
R_AddDecals(pos);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_superspike))
|
|
if (P_RunParticleEffectType(pos, NULL, 2, pt_spike))
|
|
if (P_RunParticleEffectType(pos, NULL, 20, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 20);
|
|
|
|
if ( rand() % 5 )
|
|
S_StartSound (-2, 0, cl_sfx_tink1, pos, 1, 1);
|
|
else
|
|
{
|
|
rnd = rand() & 3;
|
|
if (rnd == 1)
|
|
S_StartSound (-2, 0, cl_sfx_ric1, pos, 1, 1);
|
|
else if (rnd == 2)
|
|
S_StartSound (-2, 0, cl_sfx_ric2, pos, 1, 1);
|
|
else
|
|
S_StartSound (-2, 0, cl_sfx_ric3, pos, 1, 1);
|
|
}
|
|
break;
|
|
|
|
#ifdef PEXT_TE_BULLET
|
|
case TE_BULLET:
|
|
if (!(cls.fteprotocolextensions & PEXT_TE_BULLET))
|
|
Host_EndGame("Thought PEXT_TE_BULLET was disabled");
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, -10, -10, -10, 20);
|
|
R_AddDecals(pos);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, ptfte_bullet))
|
|
if (P_RunParticleEffectType(pos, NULL, 10, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 10);
|
|
|
|
if ( rand() % 5 )
|
|
S_StartSound (-2, 0, cl_sfx_tink1, pos, 1, 1);
|
|
else
|
|
{
|
|
rnd = rand() & 3;
|
|
if (rnd == 1)
|
|
S_StartSound (-2, 0, cl_sfx_ric1, pos, 1, 1);
|
|
else if (rnd == 2)
|
|
S_StartSound (-2, 0, cl_sfx_ric2, pos, 1, 1);
|
|
else
|
|
S_StartSound (-2, 0, cl_sfx_ric3, pos, 1, 1);
|
|
}
|
|
break;
|
|
case TE_SUPERBULLET:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, -10, -10, -10, 20);
|
|
R_AddDecals(pos);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, ptfte_superbullet))
|
|
if (P_RunParticleEffectType(pos, NULL, 2, ptfte_bullet))
|
|
if (P_RunParticleEffectType(pos, NULL, 20, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 20);
|
|
|
|
if ( rand() % 5 )
|
|
S_StartSound (-2, 0, cl_sfx_tink1, pos, 1, 1);
|
|
else
|
|
{
|
|
rnd = rand() & 3;
|
|
if (rnd == 1)
|
|
S_StartSound (-2, 0, cl_sfx_ric1, pos, 1, 1);
|
|
else if (rnd == 2)
|
|
S_StartSound (-2, 0, cl_sfx_ric2, pos, 1, 1);
|
|
else
|
|
S_StartSound (-2, 0, cl_sfx_ric3, pos, 1, 1);
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case DPTE_EXPLOSIONQUAD: // rocket explosion
|
|
// particles
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
if (P_RunParticleEffectType(pos, NULL, 1, ptdp_explosionquad))
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))
|
|
P_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111
|
|
|
|
R_AddStain(pos, -1, -1, -1, 100);
|
|
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 + r_explosionlight.value*200;
|
|
dl->die = cl.time + 1;
|
|
dl->decay = 300;
|
|
|
|
dl->color[0] = 0.2;
|
|
dl->color[1] = 0.155;
|
|
dl->color[2] = 0.05;
|
|
dl->channelfade[0] = 0.196;
|
|
dl->channelfade[1] = 0.23;
|
|
dl->channelfade[2] = 0.12;
|
|
}
|
|
|
|
|
|
// sound
|
|
S_StartSound (-2, 0, cl_sfx_r_exp3, pos, 1, 1);
|
|
|
|
// sprite
|
|
if (cl_expsprite.value) // temp hopefully
|
|
{
|
|
explosion_t *ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
ex->start = cl.time;
|
|
ex->model = Mod_ForName ("progs/s_explod.spr", true);
|
|
}
|
|
break;
|
|
case TE_EXPLOSION: // rocket explosion
|
|
// particles
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))
|
|
P_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111
|
|
|
|
R_AddStain(pos, -1, -1, -1, 100);
|
|
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 + r_explosionlight.value*200;
|
|
dl->die = cl.time + 1;
|
|
dl->decay = 300;
|
|
|
|
dl->color[0] = 0.2;
|
|
dl->color[1] = 0.155;
|
|
dl->color[2] = 0.05;
|
|
dl->channelfade[0] = 0.196;
|
|
dl->channelfade[1] = 0.23;
|
|
dl->channelfade[2] = 0.12;
|
|
}
|
|
|
|
|
|
// sound
|
|
S_StartSound (-2, 0, cl_sfx_r_exp3, pos, 1, 1);
|
|
|
|
// sprite
|
|
if (cl_expsprite.value && !nqprot) // temp hopefully
|
|
{
|
|
explosion_t *ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
ex->start = cl.time;
|
|
ex->model = Mod_ForName ("progs/s_explod.spr", true);
|
|
}
|
|
break;
|
|
|
|
case DPTE_EXPLOSIONRGB:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))
|
|
P_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111
|
|
|
|
R_AddStain(pos, -1, -1, -1, 100);
|
|
|
|
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 + r_explosionlight.value*200;
|
|
dl->die = cl.time + 0.5;
|
|
dl->decay = 300;
|
|
|
|
dl->color[0] = 0.4f*MSG_ReadByte()/255.0f;
|
|
dl->color[1] = 0.4f*MSG_ReadByte()/255.0f;
|
|
dl->color[2] = 0.4f*MSG_ReadByte()/255.0f;
|
|
dl->channelfade[0] = 0;
|
|
dl->channelfade[1] = 0;
|
|
dl->channelfade[2] = 0;
|
|
}
|
|
|
|
S_StartSound (-2, 0, cl_sfx_r_exp3, pos, 1, 1);
|
|
break;
|
|
|
|
case DPTE_TEI_BIGEXPLOSION:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
if (P_RunParticleEffectType(pos, NULL, 1, ptdp_tei_bigexplosion))
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))
|
|
P_RunParticleEffect(pos, NULL, 107, 1024); // should be 97-111
|
|
|
|
R_AddStain(pos, -1, -1, -1, 100);
|
|
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
// no point in doing this the fuh/ez way
|
|
dl->radius = 500*r_explosionlight.value;
|
|
dl->die = cl.time + 1;
|
|
dl->decay = 500;
|
|
|
|
dl->color[0] = 0.4f;
|
|
dl->color[1] = 0.3f;
|
|
dl->color[2] = 0.15f;
|
|
dl->channelfade[0] = 0;
|
|
dl->channelfade[1] = 0;
|
|
dl->channelfade[2] = 0;
|
|
}
|
|
|
|
S_StartSound (-2, 0, cl_sfx_r_exp3, pos, 1, 1);
|
|
break;
|
|
|
|
case TE_TAREXPLOSION: // tarbaby explosion
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
P_RunParticleEffectType(pos, NULL, 1, pt_tarexplosion);
|
|
|
|
S_StartSound (-2, 0, cl_sfx_r_exp3, pos, 1, 1);
|
|
break;
|
|
|
|
case TE_LIGHTNING1: // lightning bolts
|
|
case TE_LIGHTNING2: // lightning bolts
|
|
CL_ParseBeam (type - TE_LIGHTNING1);
|
|
break;
|
|
case TE_LIGHTNING3: // lightning bolts
|
|
CL_ParseBeam (2);
|
|
break;
|
|
|
|
case TE_LAVASPLASH:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
P_RunParticleEffectType(pos, NULL, 1, pt_lavasplash);
|
|
break;
|
|
|
|
case TE_TELEPORT:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
P_RunParticleEffectType(pos, NULL, 1, pt_teleportsplash);
|
|
break;
|
|
|
|
case DPTE_GUNSHOTQUAD: // bullet hitting wall
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, -10, -10, -10, 20);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, ptdp_gunshotquad))
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 20);
|
|
|
|
break;
|
|
case TE_GUNSHOT: // bullet hitting wall
|
|
if (nqprot)
|
|
cnt = 1;
|
|
else
|
|
cnt = MSG_ReadByte ();
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, -10, -10, -10, 20);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, cnt, pt_gunshot))
|
|
P_RunParticleEffect (pos, vec3_origin, 0, 20*cnt);
|
|
|
|
break;
|
|
|
|
case TE_BLOOD: // bullets hitting body
|
|
cnt = MSG_ReadByte ();
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, 0, -10, -10, 40);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, cnt, ptqw_blood))
|
|
if (P_RunParticleEffectType(pos, NULL, cnt, ptdp_blood))
|
|
P_RunParticleEffect (pos, vec3_origin, 73, 20*cnt);
|
|
|
|
break;
|
|
|
|
case TE_LIGHTNINGBLOOD: // lightning hitting body
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
R_AddStain(pos, 1, -10, -10, 20);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, ptqw_lightningblood))
|
|
P_RunParticleEffect (pos, vec3_origin, 225, 50);
|
|
|
|
break;
|
|
|
|
case TE_RAILTRAIL:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
pos2[0] = MSG_ReadCoord ();
|
|
pos2[1] = MSG_ReadCoord ();
|
|
pos2[2] = MSG_ReadCoord ();
|
|
|
|
if (P_ParticleTrail(pos, pos2, rtqw_railtrail, NULL))
|
|
if (P_ParticleTrail(pos, pos2, rtq2_railtrail, NULL))
|
|
P_ParticleTrailIndex(pos, pos2, 208, 8, NULL);
|
|
break;
|
|
|
|
case TE_STREAM_CHAIN:
|
|
case TE_STREAM_SUNSTAFF1:
|
|
case TE_STREAM_SUNSTAFF2:
|
|
case TE_STREAM_LIGHTNING:
|
|
case TE_STREAM_COLORBEAM:
|
|
case TE_STREAM_ICECHUNKS:
|
|
case TE_STREAM_GAZE:
|
|
case TE_STREAM_FAMINE:
|
|
CL_ParseStream (type);
|
|
break;
|
|
|
|
case DPTE_BLOOD:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
pos2[0] = MSG_ReadChar ();
|
|
pos2[1] = MSG_ReadChar ();
|
|
pos2[2] = MSG_ReadChar ();
|
|
|
|
cnt = MSG_ReadByte ();
|
|
|
|
P_RunParticleEffectType(pos, pos2, cnt, ptdp_blood);
|
|
break;
|
|
|
|
case DPTE_SPARK:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
pos2[0] = MSG_ReadChar ();
|
|
pos2[1] = MSG_ReadChar ();
|
|
pos2[2] = MSG_ReadChar ();
|
|
|
|
cnt = MSG_ReadByte ();
|
|
{
|
|
P_RunParticleEffectType(pos, pos2, cnt, ptdp_spark);
|
|
}
|
|
break;
|
|
|
|
case DPTE_BLOODSHOWER:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
pos2[0] = MSG_ReadCoord ();
|
|
pos2[1] = MSG_ReadCoord ();
|
|
pos2[2] = MSG_ReadCoord ();
|
|
|
|
cnt = MSG_ReadCoord (); //speed
|
|
|
|
cnt = MSG_ReadShort ();
|
|
|
|
{
|
|
VectorAdd(pos, pos2, pos);
|
|
VectorScale(pos, 0.5, pos);
|
|
P_RunParticleEffectTypeString(pos, NULL, cnt, "te_bloodshower");
|
|
}
|
|
break;
|
|
|
|
case DPTE_SMALLFLASH:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
// light
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 200;
|
|
dl->decay = 1000;
|
|
dl->die = cl.time + 0.2;
|
|
dl->color[0] = 0.4;
|
|
dl->color[1] = 0.4;
|
|
dl->color[2] = 0.4;
|
|
break;
|
|
|
|
case DPTE_CUSTOMFLASH:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
// light
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = MSG_ReadByte()*8;
|
|
pos2[0] = (MSG_ReadByte() + 1) * (1.0 / 256.0);
|
|
dl->die = cl.time + pos2[0];
|
|
dl->decay = dl->radius / pos2[0];
|
|
|
|
// DP's range is 0-2 for lights, FTE is 0-0.4.. 255/637.5 = 0.4
|
|
dl->color[0] = MSG_ReadByte()*(1.0f/637.5f);
|
|
dl->color[1] = MSG_ReadByte()*(1.0f/637.5f);
|
|
dl->color[2] = MSG_ReadByte()*(1.0f/637.5f);
|
|
|
|
break;
|
|
|
|
case DPTE_FLAMEJET:
|
|
// origin
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
// velocity
|
|
pos2[0] = MSG_ReadCoord ();
|
|
pos2[1] = MSG_ReadCoord ();
|
|
pos2[2] = MSG_ReadCoord ();
|
|
|
|
// count
|
|
cnt = MSG_ReadByte ();
|
|
|
|
if (P_RunParticleEffectType(pos, pos2, cnt, ptdp_flamejet))
|
|
P_RunParticleEffect (pos, pos2, 232, cnt);
|
|
break;
|
|
|
|
case DPTE_PLASMABURN:
|
|
// origin
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
// light
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 200;
|
|
dl->decay = 1000;
|
|
dl->die = cl.time + 0.2;
|
|
dl->color[0] = 0.2;
|
|
dl->color[1] = 0.2;
|
|
dl->color[2] = 0.2;
|
|
|
|
// stain (Hopefully this is close to how DP does it)
|
|
R_AddStain(pos, -10, -10, -10, 30);
|
|
|
|
if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_plasmaburn"), NULL))
|
|
P_ParticleTrailIndex(pos, pos2, 15, 0, NULL);
|
|
break;
|
|
|
|
case DPTE_TEI_G3: //nexuiz's nex beam
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
pos2[0] = MSG_ReadCoord ();
|
|
pos2[1] = MSG_ReadCoord ();
|
|
pos2[2] = MSG_ReadCoord ();
|
|
|
|
//sigh...
|
|
MSG_ReadCoord ();
|
|
MSG_ReadCoord ();
|
|
MSG_ReadCoord ();
|
|
|
|
if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_nexbeam"), NULL))
|
|
P_ParticleTrailIndex(pos, pos2, 15, 0, NULL);
|
|
break;
|
|
|
|
case DPTE_SMOKE:
|
|
//org
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
//dir
|
|
pos2[0] = MSG_ReadCoord ();
|
|
pos2[1] = MSG_ReadCoord ();
|
|
pos2[2] = MSG_ReadCoord ();
|
|
|
|
//count
|
|
cnt = MSG_ReadByte ();
|
|
{
|
|
P_RunParticleEffectType(pos, pos2, cnt, ptdp_tei_smoke);
|
|
}
|
|
break;
|
|
|
|
case DPTE_TEI_PLASMAHIT:
|
|
pos[0] = MSG_ReadCoord ();
|
|
pos[1] = MSG_ReadCoord ();
|
|
pos[2] = MSG_ReadCoord ();
|
|
|
|
//dir
|
|
pos2[0] = MSG_ReadCoord ();
|
|
pos2[1] = MSG_ReadCoord ();
|
|
pos2[2] = MSG_ReadCoord ();
|
|
cnt = MSG_ReadByte ();
|
|
|
|
{
|
|
P_RunParticleEffectType(pos, pos2, cnt, ptdp_tei_plasmahit);
|
|
}
|
|
break;
|
|
|
|
case DPTE_PARTICLECUBE:
|
|
{
|
|
vec3_t dir;
|
|
int jitter;
|
|
int gravity;
|
|
|
|
//min
|
|
pos[0] = MSG_ReadCoord();
|
|
pos[1] = MSG_ReadCoord();
|
|
pos[2] = MSG_ReadCoord();
|
|
|
|
//max
|
|
pos2[0] = MSG_ReadCoord();
|
|
pos2[1] = MSG_ReadCoord();
|
|
pos2[2] = MSG_ReadCoord();
|
|
|
|
//dir
|
|
dir[0] = MSG_ReadCoord();
|
|
dir[1] = MSG_ReadCoord();
|
|
dir[2] = MSG_ReadCoord();
|
|
|
|
cnt = MSG_ReadShort(); //count
|
|
colour = MSG_ReadByte (); //colour
|
|
gravity = MSG_ReadByte (); //gravity flag
|
|
jitter = MSG_ReadCoord(); //jitter
|
|
|
|
P_RunParticleCube(pos, pos2, dir, cnt, colour, gravity, jitter);
|
|
}
|
|
break;
|
|
case DPTE_PARTICLERAIN:
|
|
{
|
|
vec3_t dir;
|
|
|
|
//min
|
|
pos[0] = MSG_ReadCoord();
|
|
pos[1] = MSG_ReadCoord();
|
|
pos[2] = MSG_ReadCoord();
|
|
|
|
//max
|
|
pos2[0] = MSG_ReadCoord();
|
|
pos2[1] = MSG_ReadCoord();
|
|
pos2[2] = MSG_ReadCoord();
|
|
|
|
//dir
|
|
dir[0] = MSG_ReadCoord();
|
|
dir[1] = MSG_ReadCoord();
|
|
dir[2] = MSG_ReadCoord();
|
|
|
|
cnt = MSG_ReadShort(); //count
|
|
colour = MSG_ReadByte (); //colour
|
|
|
|
P_RunParticleWeather(pos, pos2, dir, cnt, colour, "rain");
|
|
}
|
|
break;
|
|
case DPTE_PARTICLESNOW:
|
|
{
|
|
vec3_t dir;
|
|
|
|
//min
|
|
pos[0] = MSG_ReadCoord();
|
|
pos[1] = MSG_ReadCoord();
|
|
pos[2] = MSG_ReadCoord();
|
|
|
|
//max
|
|
pos2[0] = MSG_ReadCoord();
|
|
pos2[1] = MSG_ReadCoord();
|
|
pos2[2] = MSG_ReadCoord();
|
|
|
|
//dir
|
|
dir[0] = MSG_ReadCoord();
|
|
dir[1] = MSG_ReadCoord();
|
|
dir[2] = MSG_ReadCoord();
|
|
|
|
cnt = MSG_ReadShort(); //count
|
|
colour = MSG_ReadByte (); //colour
|
|
|
|
P_RunParticleWeather(pos, pos2, dir, cnt, colour, "snow");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Host_EndGame ("CL_ParseTEnt: bad type - %i", type);
|
|
}
|
|
}
|
|
|
|
void MSG_ReadPos (vec3_t pos);
|
|
void MSG_ReadDir (vec3_t dir);
|
|
typedef struct {
|
|
int netstyle;
|
|
int particleeffecttype;
|
|
char stain[3];
|
|
qbyte radius;
|
|
vec3_t dlightrgb;
|
|
float dlightradius;
|
|
float dlighttime;
|
|
vec3_t dlightcfade;
|
|
} clcustomtents_t;
|
|
|
|
#define CTE_CUSTOMCOUNT 1
|
|
#define CTE_CUSTOMDIRECTION 2
|
|
#define CTE_STAINS 4
|
|
#define CTE_GLOWS 8
|
|
#define CTE_CHANNELFADE 16
|
|
#define CTE_ISBEAM 128
|
|
|
|
clcustomtents_t customtenttype[255]; //network based.
|
|
void CL_ParseCustomTEnt(void)
|
|
{
|
|
int count;
|
|
vec3_t pos;
|
|
vec3_t pos2;
|
|
vec3_t dir;
|
|
char *str;
|
|
clcustomtents_t *t;
|
|
int type = MSG_ReadByte();
|
|
|
|
if (type == 255) //255 is register
|
|
{
|
|
type = MSG_ReadByte();
|
|
if (type == 255)
|
|
Host_EndGame("Custom temp type 255 isn't valid\n");
|
|
t = &customtenttype[type];
|
|
|
|
t->netstyle = MSG_ReadByte();
|
|
str = MSG_ReadString();
|
|
t->particleeffecttype = P_ParticleTypeForName(str);
|
|
|
|
if (t->netstyle & CTE_STAINS)
|
|
{
|
|
t->stain[0] = MSG_ReadChar();
|
|
t->stain[1] = MSG_ReadChar();
|
|
t->stain[2] = MSG_ReadChar();
|
|
t->radius = MSG_ReadByte();
|
|
}
|
|
else
|
|
t->radius = 0;
|
|
if (t->netstyle & CTE_GLOWS)
|
|
{
|
|
t->dlightrgb[0] = MSG_ReadByte()/255.0f;
|
|
t->dlightrgb[1] = MSG_ReadByte()/255.0f;
|
|
t->dlightrgb[2] = MSG_ReadByte()/255.0f;
|
|
t->dlightradius = MSG_ReadByte();
|
|
t->dlighttime = MSG_ReadByte()/16.0f;
|
|
if (t->netstyle & CTE_CHANNELFADE)
|
|
{
|
|
t->dlightcfade[0] = MSG_ReadByte()/64.0f;
|
|
t->dlightcfade[1] = MSG_ReadByte()/64.0f;
|
|
t->dlightcfade[2] = MSG_ReadByte()/64.0f;
|
|
}
|
|
}
|
|
else
|
|
t->dlighttime = 0;
|
|
return;
|
|
}
|
|
|
|
t = &customtenttype[type];
|
|
if (t->particleeffecttype < 0)
|
|
Host_EndGame("Custom Temporary entity %i was not registered\n", type);
|
|
|
|
if (t->netstyle & CTE_ISBEAM)
|
|
{
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadPos (pos2);
|
|
P_ParticleTrail(pos, pos2, t->particleeffecttype, NULL);
|
|
}
|
|
else
|
|
{
|
|
if (t->netstyle & CTE_CUSTOMCOUNT)
|
|
count = MSG_ReadByte();
|
|
else
|
|
count = 1;
|
|
|
|
MSG_ReadPos (pos);
|
|
VectorCopy(pos, pos2);
|
|
|
|
if (t->netstyle & CTE_CUSTOMDIRECTION)
|
|
{
|
|
MSG_ReadDir (dir);
|
|
P_RunParticleEffectType(pos, dir, 1, t->particleeffecttype);
|
|
}
|
|
else P_RunParticleEffectType(pos, NULL, 1, t->particleeffecttype);
|
|
}
|
|
|
|
if (t->netstyle & CTE_STAINS)
|
|
{ //added at pos2 - end of trail
|
|
R_AddStain(pos2, t->stain[0], t->stain[1], t->stain[2], 40);
|
|
}
|
|
if (t->netstyle & CTE_GLOWS)
|
|
{ //added at pos1 firer's end.
|
|
dlight_t *dl;
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = t->dlightradius*4;
|
|
dl->die = cl.time + t->dlighttime;
|
|
dl->decay = t->radius/t->dlighttime;
|
|
|
|
dl->color[0] = t->dlightrgb[0];
|
|
dl->color[1] = t->dlightrgb[1];
|
|
dl->color[2] = t->dlightrgb[2];
|
|
|
|
if (t->netstyle & CTE_CHANNELFADE)
|
|
{
|
|
dl->channelfade[0] = t->dlightcfade[0];
|
|
dl->channelfade[1] = t->dlightcfade[1];
|
|
dl->channelfade[2] = t->dlightcfade[2];
|
|
}
|
|
|
|
/*
|
|
if (dl->color[0] < 0)
|
|
dl->channelfade[0] = 0;
|
|
else
|
|
dl->channelfade[0] = dl->color[0]/t->dlighttime;
|
|
|
|
if (dl->color[1] < 0)
|
|
dl->channelfade[1] = 0;
|
|
else
|
|
dl->channelfade[1] = dl->color[0]/t->dlighttime;
|
|
|
|
if (dl->color[2] < 0)
|
|
dl->channelfade[2] = 0;
|
|
else
|
|
dl->channelfade[2] = dl->color[0]/t->dlighttime;
|
|
*/
|
|
}
|
|
}
|
|
void CL_ClearCustomTEnts(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < sizeof(customtenttype)/sizeof(customtenttype[0]); i++)
|
|
customtenttype[i].particleeffecttype = -1;
|
|
}
|
|
|
|
void CLDP_ParseTrailParticles(void)
|
|
{
|
|
int entityindex;
|
|
int effectindex;
|
|
vec3_t start, end;
|
|
trailstate_t **ts;
|
|
|
|
entityindex = (unsigned short)MSG_ReadShort();
|
|
effectindex = (unsigned short)MSG_ReadShort();
|
|
|
|
start[0] = MSG_ReadCoord();
|
|
start[1] = MSG_ReadCoord();
|
|
start[2] = MSG_ReadCoord();
|
|
end[0] = MSG_ReadCoord();
|
|
end[1] = MSG_ReadCoord();
|
|
end[2] = MSG_ReadCoord();
|
|
|
|
if (entityindex && (unsigned int)entityindex < MAX_EDICTS)
|
|
ts = &cl.lerpents[entityindex].trailstate;
|
|
else
|
|
ts = NULL;
|
|
|
|
effectindex = P_ParticleTypeForName(COM_Effectinfo_ForNumber(effectindex));
|
|
if (P_ParticleTrail(start, end, effectindex, ts))
|
|
P_ParticleTrail(start, end, rt_blood, ts);
|
|
}
|
|
|
|
void CLDP_ParsePointParticles(qboolean compact)
|
|
{
|
|
vec3_t org, dir;
|
|
unsigned int count, effectindex;
|
|
|
|
effectindex = (unsigned short)MSG_ReadShort();
|
|
org[0] = MSG_ReadCoord();
|
|
org[1] = MSG_ReadCoord();
|
|
org[2] = MSG_ReadCoord();
|
|
if (compact)
|
|
{
|
|
dir[0] = dir[1] = dir[2] = 0;
|
|
count = 1;
|
|
}
|
|
else
|
|
{
|
|
dir[0] = MSG_ReadCoord();
|
|
dir[1] = MSG_ReadCoord();
|
|
dir[2] = MSG_ReadCoord();
|
|
count = (unsigned short)MSG_ReadShort();
|
|
}
|
|
|
|
effectindex = P_ParticleTypeForName(COM_Effectinfo_ForNumber(effectindex));
|
|
if (P_RunParticleEffectType(org, dir, count, effectindex))
|
|
P_RunParticleEffect (org, dir, 15, 15);
|
|
}
|
|
|
|
void CLNQ_ParseParticleEffect (void)
|
|
{
|
|
vec3_t org, dir;
|
|
int i, count, msgcount, color;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
org[i] = MSG_ReadCoord ();
|
|
for (i=0 ; i<3 ; i++)
|
|
dir[i] = MSG_ReadChar () * (1.0/16);
|
|
msgcount = MSG_ReadByte ();
|
|
color = MSG_ReadByte ();
|
|
|
|
if (msgcount == 255)
|
|
count = 1024;
|
|
else
|
|
count = msgcount;
|
|
|
|
P_RunParticleEffect (org, dir, color, count);
|
|
}
|
|
void CL_ParseParticleEffect2 (void)
|
|
{
|
|
vec3_t org, dmin, dmax;
|
|
int i, msgcount, color, effect;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
org[i] = MSG_ReadCoord ();
|
|
for (i=0 ; i<3 ; i++)
|
|
dmin[i] = MSG_ReadFloat ();
|
|
for (i=0 ; i<3 ; i++)
|
|
dmax[i] = MSG_ReadFloat ();
|
|
color = MSG_ReadShort ();
|
|
msgcount = MSG_ReadByte ();
|
|
effect = MSG_ReadByte ();
|
|
|
|
P_RunParticleEffect2 (org, dmin, dmax, color, effect, msgcount);
|
|
}
|
|
void CL_ParseParticleEffect3 (void)
|
|
{
|
|
vec3_t org, box;
|
|
int i, msgcount, color, effect;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
org[i] = MSG_ReadCoord ();
|
|
for (i=0 ; i<3 ; i++)
|
|
box[i] = MSG_ReadByte ();
|
|
color = MSG_ReadShort ();
|
|
msgcount = MSG_ReadByte ();
|
|
effect = MSG_ReadByte ();
|
|
|
|
P_RunParticleEffect3 (org, box, color, effect, msgcount);
|
|
}
|
|
void CL_ParseParticleEffect4 (void)
|
|
{
|
|
vec3_t org;
|
|
int i, msgcount, color, effect;
|
|
float radius;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
org[i] = MSG_ReadCoord ();
|
|
radius = MSG_ReadByte();
|
|
color = MSG_ReadShort ();
|
|
msgcount = MSG_ReadByte ();
|
|
effect = MSG_ReadByte ();
|
|
|
|
P_RunParticleEffect4 (org, radius, color, effect, msgcount);
|
|
}
|
|
|
|
void CL_SpawnSpriteEffect(vec3_t org, model_t *model, int startframe, int framecount, int framerate)
|
|
{
|
|
explosion_t *ex;
|
|
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (org, ex->origin);
|
|
ex->start = cl.time;
|
|
ex->model = model;
|
|
ex->firstframe = startframe;
|
|
ex->numframes = framecount;
|
|
ex->framerate = framerate;
|
|
|
|
ex->angles[0] = 0;
|
|
ex->angles[1] = 0;
|
|
ex->angles[2] = 0;
|
|
}
|
|
|
|
// [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate
|
|
// [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate
|
|
void CL_ParseEffect (qboolean effect2)
|
|
{
|
|
vec3_t org;
|
|
int modelindex;
|
|
int startframe;
|
|
int framecount;
|
|
int framerate;
|
|
|
|
org[0] = MSG_ReadCoord();
|
|
org[1] = MSG_ReadCoord();
|
|
org[2] = MSG_ReadCoord();
|
|
|
|
if (effect2)
|
|
modelindex = MSG_ReadShort();
|
|
else
|
|
modelindex = MSG_ReadByte();
|
|
|
|
if (effect2)
|
|
startframe = MSG_ReadShort();
|
|
else
|
|
startframe = MSG_ReadByte();
|
|
|
|
framecount = MSG_ReadByte();
|
|
framerate = MSG_ReadByte();
|
|
|
|
|
|
CL_SpawnSpriteEffect(org, cl.model_precache[modelindex], startframe, framecount, framerate);
|
|
}
|
|
|
|
#ifdef Q2CLIENT
|
|
void CL_SmokeAndFlash(vec3_t origin)
|
|
{
|
|
explosion_t *ex;
|
|
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (origin, ex->origin);
|
|
VectorClear(ex->angles);
|
|
// ex->type = ex_misc;
|
|
ex->numframes = 4;
|
|
ex->flags = Q2RF_TRANSLUCENT;
|
|
ex->start = cl.time;
|
|
ex->model = Mod_ForName (q2tentmodels[q2cl_mod_smoke].modelname, false);
|
|
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (origin, ex->origin);
|
|
VectorClear(ex->angles);
|
|
// ex->type = ex_flash;
|
|
ex->flags = Q2RF_FULLBRIGHT;
|
|
ex->numframes = 2;
|
|
ex->start = cl.time;
|
|
ex->model = Mod_ForName (q2tentmodels[q2cl_mod_flash].modelname, false);
|
|
}
|
|
|
|
void CL_Laser (vec3_t start, vec3_t end, int colors)
|
|
{
|
|
explosion_t *ex = CL_AllocExplosion();
|
|
ex->firstframe = 0;
|
|
ex->numframes = 10;
|
|
ex->alpha = 0.33f;
|
|
ex->model = (void*)0xDEAFF00D; //something not null
|
|
ex->skinnum = (colors >> ((rand() % 4)*8)) & 0xff;
|
|
VectorCopy (start, ex->origin);
|
|
VectorCopy (end, ex->oldorigin);
|
|
ex->flags = Q2RF_TRANSLUCENT | Q2RF_BEAM;
|
|
ex->start = cl.time;
|
|
ex->framerate = 100; // smoother fading
|
|
}
|
|
|
|
static qbyte splash_color[] = {0x00, 0xe0, 0xb0, 0x50, 0xd0, 0xe0, 0xe8};
|
|
|
|
#define ATTN_NONE 0
|
|
#define ATTN_NORM 1
|
|
#define ATTN_STATIC 1
|
|
void Q2S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation, float timeofs)
|
|
{
|
|
S_StartSoundDelayed(entnum, entchannel, sfx, origin, fvol, attenuation, timeofs);
|
|
}
|
|
void CLQ2_ParseTEnt (void)
|
|
{
|
|
int type;
|
|
vec3_t pos, pos2, dir;
|
|
explosion_t *ex;
|
|
int cnt;
|
|
int color;
|
|
int r;
|
|
// int ent;
|
|
// int magnitude;
|
|
|
|
type = MSG_ReadByte ();
|
|
|
|
switch (type)
|
|
{
|
|
case Q2TE_BLOOD: // bullet hitting flesh
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
if (P_RunParticleEffectType(pos, dir, 1, ptq2_blood))
|
|
if (P_RunParticleEffectType(pos, dir, 1, ptqw_blood))
|
|
P_RunParticleEffect(pos, dir, 0xe8, 60);
|
|
R_AddStain(pos, 0, -10, -10, 40);
|
|
break;
|
|
|
|
case Q2TE_GUNSHOT: // bullet hitting wall
|
|
case Q2TE_SPARKS:
|
|
case Q2TE_BULLET_SPARKS:
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
if (type == Q2TE_GUNSHOT)
|
|
P_RunParticleEffect (pos, dir, 0, 40);
|
|
else
|
|
P_RunParticleEffect (pos, dir, 0xe0, 6);
|
|
|
|
R_AddStain(pos, -10, -10, -10, 20);
|
|
|
|
if (type != Q2TE_SPARKS)
|
|
{
|
|
CL_SmokeAndFlash(pos);
|
|
|
|
// impact sound (nope, not the same as Q1...)
|
|
cnt = rand()&15;
|
|
if (cnt == 1)
|
|
Q2S_StartSound (pos, 0, 0, S_PrecacheSound ("world/ric1.wav"), 1, ATTN_NORM, 0);
|
|
else if (cnt == 2)
|
|
Q2S_StartSound (pos, 0, 0, S_PrecacheSound ("world/ric2.wav"), 1, ATTN_NORM, 0);
|
|
else if (cnt == 3)
|
|
Q2S_StartSound (pos, 0, 0, S_PrecacheSound ("world/ric3.wav"), 1, ATTN_NORM, 0);
|
|
}
|
|
|
|
break;
|
|
|
|
case Q2TE_SCREEN_SPARKS:
|
|
case Q2TE_SHIELD_SPARKS:
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
if (type == Q2TE_SCREEN_SPARKS)
|
|
P_RunParticleEffect (pos, dir, 0xd0, 40);
|
|
else
|
|
P_RunParticleEffect (pos, dir, 0xb0, 40);
|
|
//FIXME : replace or remove this sound
|
|
S_StartSound (-2, 0, S_PrecacheSound ("weapons/lashit.wav"), pos, 1, 1);
|
|
break;
|
|
|
|
case Q2TE_SHOTGUN: // bullet hitting wall
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
P_RunParticleEffect (pos, dir, 0, 20);
|
|
CL_SmokeAndFlash(pos);
|
|
R_AddStain(pos, -10, -10, -10, 20);
|
|
break;
|
|
|
|
case Q2TE_SPLASH: // bullet hitting water
|
|
cnt = MSG_ReadByte ();
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
r = MSG_ReadByte ();
|
|
if (r > 6)
|
|
color = 0x00;
|
|
else
|
|
color = splash_color[r];
|
|
P_RunParticleEffect (pos, dir, color, cnt);
|
|
|
|
if (r == Q2SPLASH_SPARKS)
|
|
{
|
|
r = rand() & 3;
|
|
if (r == 1)
|
|
Q2S_StartSound (pos, 0, 0, S_PrecacheSound ("world/spark5.wav"), 1, ATTN_NORM, 0);
|
|
else if (r == 2)
|
|
Q2S_StartSound (pos, 0, 0, S_PrecacheSound ("world/spark6.wav"), 1, ATTN_NORM, 0);
|
|
else
|
|
Q2S_StartSound (pos, 0, 0, S_PrecacheSound ("world/spark7.wav"), 1, ATTN_NORM, 0);
|
|
|
|
// if (r == 0)
|
|
// Q2S_StartSound (pos, 0, 0, cl_sfx_spark5, 1, ATTN_STATIC, 0);
|
|
// else if (r == 1)
|
|
// Q2S_StartSound (pos, 0, 0, cl_sfx_spark6, 1, ATTN_STATIC, 0);
|
|
// else
|
|
// Q2S_StartSound (pos, 0, 0, cl_sfx_spark7, 1, ATTN_STATIC, 0);
|
|
}
|
|
break;
|
|
|
|
case Q2TE_LASER_SPARKS:
|
|
cnt = MSG_ReadByte ();
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
color = MSG_ReadByte ();
|
|
P_RunParticleEffect (pos, dir, color, cnt);
|
|
break;
|
|
|
|
// RAFAEL
|
|
case Q2TE_BLUEHYPERBLASTER:
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadPos (dir);
|
|
if (P_RunParticleEffectType(pos, dir, 1, ptq2_blasterparticles))
|
|
P_RunParticleEffect (pos, dir, 0xe0, 40);
|
|
break;
|
|
|
|
case Q2TE_BLASTER: // blaster hitting wall
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
|
|
if (P_RunParticleEffectType(pos, dir, 1, ptq2_blasterparticles))
|
|
P_RunParticleEffect (pos, dir, 0xe0, 40);
|
|
|
|
R_AddStain(pos, 0, -5, -10, 20);
|
|
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
ex->start = cl.time;
|
|
ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explode].modelname, false);
|
|
ex->firstframe = 0;
|
|
ex->numframes = 4;
|
|
ex->flags = Q2RF_FULLBRIGHT;
|
|
|
|
ex->angles[0] = acos(dir[2])/M_PI*180;
|
|
// PMM - fixed to correct for pitch of 0
|
|
if (dir[0])
|
|
ex->angles[1] = atan2(dir[1], dir[0])/M_PI*180;
|
|
else if (dir[1] > 0)
|
|
ex->angles[1] = 90;
|
|
else if (dir[1] < 0)
|
|
ex->angles[1] = 270;
|
|
else
|
|
ex->angles[1] = 0;
|
|
ex->angles[0]*=-1;
|
|
|
|
S_StartSound (-2, 0, S_PrecacheSound ("weapons/lashit.wav"), pos, 1, 1);
|
|
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dlight_t *dl;
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 * r_explosionlight.value;
|
|
dl->die = cl.time + 0.4;
|
|
dl->decay = 400;
|
|
dl->color[0] = 0.2;
|
|
dl->color[1] = 0.2;
|
|
dl->color[2] = 0.0;
|
|
dl->channelfade[0] = 0.5;
|
|
dl->channelfade[1] = 0.51;
|
|
dl->channelfade[2] = 0.0;
|
|
}
|
|
|
|
break;
|
|
|
|
case Q2TE_RAILTRAIL: // railgun effect
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadPos (pos2);
|
|
if (P_ParticleTrail(pos, pos2, rtq2_railtrail, NULL))
|
|
P_ParticleTrailIndex(pos, pos2, 0x74, 8, NULL);
|
|
Q2S_StartSound (pos, 0, 0, S_PrecacheSound ("weapons/railgf1a.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2TE_EXPLOSION2:
|
|
case Q2TE_GRENADE_EXPLOSION:
|
|
case Q2TE_GRENADE_EXPLOSION_WATER:
|
|
MSG_ReadPos (pos);
|
|
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))
|
|
P_RunParticleEffect(pos, NULL, 0xe0, 256);
|
|
|
|
R_AddStain(pos, -1, -1, -1, 100);
|
|
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dlight_t *dl;
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 + r_explosionlight.value*200;
|
|
dl->die = cl.time + 0.5;
|
|
dl->decay = 300;
|
|
dl->color[0] = 0.2;
|
|
dl->color[1] = 0.1;
|
|
dl->color[2] = 0.1;
|
|
dl->channelfade[0] = 0.36;
|
|
dl->channelfade[1] = 0.19;
|
|
dl->channelfade[2] = 0.19;
|
|
}
|
|
|
|
// sound
|
|
if (type == Q2TE_GRENADE_EXPLOSION_WATER)
|
|
S_StartSound (-2, 0, S_PrecacheSound ("weapons/xpld_wat.wav"), pos, 1, 1);
|
|
else
|
|
S_StartSound (-2, 0, S_PrecacheSound ("weapons/grenlx1a.wav"), pos, 1, 1);
|
|
|
|
// sprite
|
|
/*
|
|
if (!R_ParticleExplosionHeart(pos))
|
|
{
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
VectorClear(ex->angles);
|
|
ex->start = cl.time;
|
|
ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explo4].modelname, true);
|
|
ex->firstframe = 30;
|
|
ex->numframes = 19;
|
|
}
|
|
*/
|
|
break;
|
|
/*
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->ent.origin);
|
|
ex->type = ex_poly;
|
|
ex->ent.flags = RF_FULLBRIGHT;
|
|
ex->start = cl.frame.servertime - 100;
|
|
ex->light = 350;
|
|
ex->lightcolor[0] = 1.0;
|
|
ex->lightcolor[1] = 0.5;
|
|
ex->lightcolor[2] = 0.5;
|
|
ex->ent.model = cl_mod_explo4;
|
|
ex->frames = 19;
|
|
ex->baseframe = 30;
|
|
ex->ent.angles[1] = rand() % 360;
|
|
CL_ExplosionParticles (pos);
|
|
if (type == TE_GRENADE_EXPLOSION_WATER)
|
|
Q2S_StartSound (pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0);
|
|
else
|
|
Q2S_StartSound (pos, 0, 0, cl_sfx_grenexp, 1, ATTN_NORM, 0);
|
|
break;
|
|
*/
|
|
// RAFAEL
|
|
case Q2TE_PLASMA_EXPLOSION:
|
|
MSG_ReadPos (pos);
|
|
/* ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->ent.origin);
|
|
ex->type = ex_poly;
|
|
ex->ent.flags = RF_FULLBRIGHT;
|
|
ex->start = cl.frame.servertime - 100;
|
|
ex->light = 350;
|
|
ex->lightcolor[0] = 1.0;
|
|
ex->lightcolor[1] = 0.5;
|
|
ex->lightcolor[2] = 0.5;
|
|
ex->ent.angles[1] = rand() % 360;
|
|
ex->ent.model = cl_mod_explo4;
|
|
if (frand() < 0.5)
|
|
ex->baseframe = 15;
|
|
ex->frames = 15;
|
|
CL_ExplosionParticles (pos);
|
|
Q2S_StartSound (pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0);
|
|
*/ break;
|
|
|
|
case Q2TE_EXPLOSION1:
|
|
case Q2TE_EXPLOSION1_BIG: // PMM
|
|
case Q2TE_ROCKET_EXPLOSION:
|
|
case Q2TE_ROCKET_EXPLOSION_WATER:
|
|
case Q2TE_EXPLOSION1_NP: // PMM
|
|
MSG_ReadPos (pos);
|
|
|
|
// particle effect
|
|
if (type != Q2TE_EXPLOSION1_BIG && type != Q2TE_EXPLOSION1_NP)
|
|
{
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_explosion))
|
|
P_RunParticleEffect(pos, NULL, 0xe0, 256);
|
|
|
|
R_AddStain(pos, -1, -1, -1, 100);
|
|
}
|
|
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dlight_t *dl;
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 + r_explosionlight.value*200;
|
|
dl->die = cl.time + 0.5;
|
|
dl->decay = 300;
|
|
dl->color[0] = 0.2;
|
|
dl->color[1] = 0.1;
|
|
dl->color[2] = 0.08;
|
|
dl->channelfade[0] = 0.36;
|
|
dl->channelfade[1] = 0.19;
|
|
dl->channelfade[2] = 0.19;
|
|
}
|
|
|
|
// sound
|
|
if (type == Q2TE_ROCKET_EXPLOSION_WATER)
|
|
S_StartSound (-2, 0, S_PrecacheSound ("weapons/xpld_wat.wav"), pos, 1, 1);
|
|
else
|
|
S_StartSound (-2, 0, S_PrecacheSound ("weapons/rocklx1a.wav"), pos, 1, 1);
|
|
|
|
// sprite
|
|
/* if (!R_ParticleExplosionHeart(pos))
|
|
{
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
VectorClear(ex->angles);
|
|
ex->start = cl.time;
|
|
ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explo4].modelname, false);
|
|
if (rand()&1)
|
|
ex->firstframe = 15;
|
|
else
|
|
ex->firstframe = 0;
|
|
ex->numframes = 15;
|
|
}*/
|
|
break;
|
|
/*
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->ent.origin);
|
|
ex->type = ex_poly;
|
|
ex->ent.flags = RF_FULLBRIGHT;
|
|
ex->start = cl.frame.servertime - 100;
|
|
ex->light = 350;
|
|
ex->lightcolor[0] = 1.0;
|
|
ex->lightcolor[1] = 0.5;
|
|
ex->lightcolor[2] = 0.5;
|
|
ex->ent.angles[1] = rand() % 360;
|
|
if (type != TE_EXPLOSION1_BIG) // PMM
|
|
ex->ent.model = cl_mod_explo4; // PMM
|
|
else
|
|
ex->ent.model = cl_mod_explo4_big;
|
|
if (frand() < 0.5)
|
|
ex->baseframe = 15;
|
|
ex->frames = 15;
|
|
if ((type != TE_EXPLOSION1_BIG) && (type != TE_EXPLOSION1_NP)) // PMM
|
|
CL_ExplosionParticles (pos); // PMM
|
|
if (type == TE_ROCKET_EXPLOSION_WATER)
|
|
Q2S_StartSound (pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0);
|
|
else
|
|
Q2S_StartSound (pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
*/ case Q2TE_BFG_EXPLOSION:
|
|
MSG_ReadPos (pos);
|
|
/* ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->ent.origin);
|
|
ex->type = ex_poly;
|
|
ex->flags = RF_FULLBRIGHT;
|
|
ex->start = cl.q2frame.servertime - 100;
|
|
ex->light = 350;
|
|
ex->lightcolor[0] = 0.0;
|
|
ex->lightcolor[1] = 1.0;
|
|
ex->lightcolor[2] = 0.0;
|
|
ex->model = cl_mod_bfg_explo;
|
|
ex->flags |= RF_TRANSLUCENT;
|
|
ex->alpha = 0.30;
|
|
ex->frames = 4;
|
|
*/ break;
|
|
|
|
case Q2TE_BFG_BIGEXPLOSION:
|
|
MSG_ReadPos (pos);
|
|
// CL_BFGExplosionParticles (pos);
|
|
if (P_RunParticleEffectTypeString(pos, dir, 1, "te_bfg_bigexplosion"))
|
|
P_RunParticleEffect(pos, dir, 0xd0, 256); // TODO: x+(r%8) unstead of x&7+(r&7)
|
|
break;
|
|
|
|
case Q2TE_BFG_LASER:
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadPos (pos2);
|
|
CL_Laser(pos, pos2, 0xd0d1d2d3);
|
|
break;
|
|
|
|
case Q2TE_BUBBLETRAIL:
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadPos (pos2);
|
|
if (P_ParticleTrail(pos, pos2, rtq2_bubbletrail, NULL))
|
|
P_ParticleTrailIndex(pos, pos2, 4, 8, NULL);
|
|
break;
|
|
|
|
case Q2TE_PARASITE_ATTACK:
|
|
case Q2TE_MEDIC_CABLE_ATTACK:
|
|
CL_ParseBeam (3);
|
|
break;
|
|
|
|
case Q2TE_BOSSTPORT: // boss teleporting to station
|
|
MSG_ReadPos (pos);
|
|
/* CL_BigTeleportParticles (pos);
|
|
*/ Q2S_StartSound (pos, 0, 0, S_PrecacheSound ("misc/bigtele.wav"), 1, ATTN_NONE, 0);
|
|
break;
|
|
|
|
case Q2TE_GRAPPLE_CABLE:
|
|
CL_ParseBeam (4);
|
|
MSG_ReadPos (pos);
|
|
break;
|
|
|
|
// RAFAEL
|
|
case Q2TE_WELDING_SPARKS:
|
|
cnt = MSG_ReadByte ();
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
color = MSG_ReadByte ();
|
|
|
|
// TODO: fix to Q2's standards
|
|
P_RunParticleEffect(pos, dir, color, cnt);
|
|
/* CL_ParticleEffect2 (pos, dir, color, cnt);
|
|
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->ent.origin);
|
|
ex->type = ex_flash;
|
|
// note to self
|
|
// we need a better no draw flag
|
|
ex->ent.flags = RF_BEAM;
|
|
ex->start = cl.frame.servertime - 0.1;
|
|
ex->light = 100 + (rand()%75);
|
|
ex->lightcolor[0] = 1.0;
|
|
ex->lightcolor[1] = 1.0;
|
|
ex->lightcolor[2] = 0.3;
|
|
ex->ent.model = cl_mod_flash;
|
|
ex->frames = 2;
|
|
*/ break;
|
|
|
|
case Q2TE_GREENBLOOD:
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
if (P_RunParticleEffectTypeString(pos, dir, 1, "te_greenblood"))
|
|
P_RunParticleEffect(pos, dir, 0xdf, 30); // TODO: x+(r%8) unstead of x&7+(r&7)
|
|
// CL_ParticleEffect2 (pos, dir, 0xdf, 30);
|
|
break;
|
|
|
|
// RAFAEL
|
|
case Q2TE_TUNNEL_SPARKS:
|
|
cnt = MSG_ReadByte ();
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
color = MSG_ReadByte ();
|
|
// CL_ParticleEffect3 (pos, dir, color, cnt);
|
|
break;
|
|
|
|
//=============
|
|
//PGM
|
|
// PMM -following code integrated for flechette (different color)
|
|
case Q2TE_BLASTER2: // green blaster hitting wall
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
|
|
if (P_RunParticleEffectTypeString(pos, dir, 1, "te_blaster2"))
|
|
if (P_RunParticleEffectType(pos, dir, 1, ptq2_blasterparticles))
|
|
P_RunParticleEffect (pos, dir, 0xd0, 40);
|
|
|
|
R_AddStain(pos, -10, 0, -10, 20);
|
|
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
ex->start = cl.time;
|
|
ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explode].modelname, false);
|
|
ex->firstframe = 0;
|
|
ex->numframes = 4;
|
|
ex->flags = Q2RF_FULLBRIGHT;
|
|
|
|
ex->angles[0] = acos(dir[2])/M_PI*180;
|
|
// PMM - fixed to correct for pitch of 0
|
|
if (dir[0])
|
|
ex->angles[1] = atan2(dir[1], dir[0])/M_PI*180;
|
|
else if (dir[1] > 0)
|
|
ex->angles[1] = 90;
|
|
else if (dir[1] < 0)
|
|
ex->angles[1] = 270;
|
|
else
|
|
ex->angles[1] = 0;
|
|
ex->angles[0]*=-1;
|
|
|
|
S_StartSound (-2, 0, S_PrecacheSound ("weapons/lashit.wav"), pos, 1, 1);
|
|
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dlight_t *dl;
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 * r_explosionlight.value;
|
|
dl->die = cl.time + 0.4;
|
|
dl->decay = 400;
|
|
dl->color[0] = 0.01;
|
|
dl->color[1] = 0.2;
|
|
dl->color[2] = 0.01;
|
|
dl->channelfade[0] = 0.1;
|
|
dl->channelfade[1] = 0.5;
|
|
dl->channelfade[2] = 0.1;
|
|
}
|
|
break;
|
|
|
|
case Q2TE_FLECHETTE: // blue blaster effect
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
|
|
if (P_RunParticleEffectTypeString(pos, dir, 1, "te_blaster2"))
|
|
if (P_RunParticleEffectType(pos, dir, 1, ptq2_blasterparticles))
|
|
P_RunParticleEffect (pos, dir, 0x6f, 40);
|
|
|
|
R_AddStain(pos, -10, -2, 0, 20);
|
|
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
ex->start = cl.time;
|
|
ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explode].modelname, false);
|
|
ex->firstframe = 0;
|
|
ex->numframes = 4;
|
|
ex->flags = Q2RF_FULLBRIGHT;
|
|
|
|
ex->angles[0] = acos(dir[2])/M_PI*180;
|
|
// PMM - fixed to correct for pitch of 0
|
|
if (dir[0])
|
|
ex->angles[1] = atan2(dir[1], dir[0])/M_PI*180;
|
|
else if (dir[1] > 0)
|
|
ex->angles[1] = 90;
|
|
else if (dir[1] < 0)
|
|
ex->angles[1] = 270;
|
|
else
|
|
ex->angles[1] = 0;
|
|
ex->angles[0]*=-1;
|
|
|
|
S_StartSound (-2, 0, S_PrecacheSound ("weapons/lashit.wav"), pos, 1, 1);
|
|
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dlight_t *dl;
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 * r_explosionlight.value;
|
|
dl->die = cl.time + 0.4;
|
|
dl->decay = 400;
|
|
dl->color[0] = 0.038;
|
|
dl->color[1] = 0.082;
|
|
dl->color[2] = 0.150;
|
|
dl->channelfade[0] = 0.085;
|
|
dl->channelfade[1] = 0.180;
|
|
dl->channelfade[2] = 0.300;
|
|
}
|
|
break;
|
|
|
|
|
|
case Q2TE_LIGHTNING:
|
|
CL_ParseBeam(TE_LIGHTNING1);
|
|
Q2S_StartSound (pos, 0, 0, S_PrecacheSound("weapons/tesla.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2TE_DEBUGTRAIL:
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadPos (pos2);
|
|
if (P_ParticleTrail(pos, pos2, P_FindParticleType("te_debugtrail"), NULL))
|
|
P_ParticleTrailIndex(pos, pos2, 116, 8, NULL);
|
|
break;
|
|
|
|
case Q2TE_PLAIN_EXPLOSION:
|
|
MSG_ReadPos (pos);
|
|
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
// ex->type = ex_poly;
|
|
ex->flags = Q2RF_FULLBRIGHT;
|
|
ex->angles[1] = rand() % 360;
|
|
ex->model = Mod_ForName (q2tentmodels[q2cl_mod_explo4].modelname, false);
|
|
if (rand() < RAND_MAX/2)
|
|
ex->firstframe = 15;
|
|
ex->numframes = 15;
|
|
Q2S_StartSound (pos, 0, 0, S_PrecacheSound("weapons/rocklx1a.wav"), 1, ATTN_NORM, 0);
|
|
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dlight_t *dl;
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 + r_explosionlight.value*200;
|
|
dl->die = cl.time + 0.5;
|
|
dl->decay = 300;
|
|
dl->color[0] = 0.2;
|
|
dl->color[1] = 0.1;
|
|
dl->color[2] = 0.08;
|
|
dl->channelfade[0] = 0.36;
|
|
dl->channelfade[1] = 0.19;
|
|
dl->channelfade[2] = 0.19;
|
|
}
|
|
|
|
break;
|
|
/*
|
|
case Q2TE_FLASHLIGHT:
|
|
MSG_ReadPos(&net_message, pos);
|
|
ent = MSG_ReadShort(&net_message);
|
|
CL_Flashlight(ent, pos);
|
|
break;
|
|
|
|
case Q2TE_FORCEWALL:
|
|
MSG_ReadPos(&net_message, pos);
|
|
MSG_ReadPos(&net_message, pos2);
|
|
color = MSG_ReadByte (&net_message);
|
|
CL_ForceWall(pos, pos2, color);
|
|
break;
|
|
*/
|
|
case Q2TE_HEATBEAM:
|
|
MSG_ReadPos(pos);
|
|
MSG_ReadPos(pos2);
|
|
// ent = CL_ParsePlayerBeam (cl_mod_heatbeam);
|
|
break;
|
|
/*
|
|
case Q2TE_MONSTER_HEATBEAM:
|
|
ent = CL_ParsePlayerBeam (cl_mod_monster_heatbeam);
|
|
break;
|
|
|
|
case Q2TE_HEATBEAM_SPARKS:
|
|
// cnt = MSG_ReadByte (&net_message);
|
|
cnt = 50;
|
|
MSG_ReadPos (&net_message, pos);
|
|
MSG_ReadDir (&net_message, dir);
|
|
// r = MSG_ReadByte (&net_message);
|
|
// magnitude = MSG_ReadShort (&net_message);
|
|
r = 8;
|
|
magnitude = 60;
|
|
color = r & 0xff;
|
|
CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude);
|
|
S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2TE_HEATBEAM_STEAM:
|
|
// cnt = MSG_ReadByte (&net_message);
|
|
cnt = 20;
|
|
MSG_ReadPos (&net_message, pos);
|
|
MSG_ReadDir (&net_message, dir);
|
|
// r = MSG_ReadByte (&net_message);
|
|
// magnitude = MSG_ReadShort (&net_message);
|
|
// color = r & 0xff;
|
|
color = 0xe0;
|
|
magnitude = 60;
|
|
CL_ParticleSteamEffect (pos, dir, color, cnt, magnitude);
|
|
S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2TE_STEAM:
|
|
CL_ParseSteam();
|
|
break;
|
|
|
|
case Q2TE_BUBBLETRAIL2:
|
|
// cnt = MSG_ReadByte (&net_message);
|
|
cnt = 8;
|
|
MSG_ReadPos (&net_message, pos);
|
|
MSG_ReadPos (&net_message, pos2);
|
|
CL_BubbleTrail2 (pos, pos2, cnt);
|
|
S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
|
|
break;
|
|
*/
|
|
case Q2TE_MOREBLOOD:
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadDir (dir);
|
|
if (P_RunParticleEffectTypeString(pos, dir, 1, "te_moreblood"))
|
|
if (P_RunParticleEffectType(pos, dir, 4, ptqw_blood))
|
|
P_RunParticleEffect(pos, dir, 0xe8, 250);
|
|
break;
|
|
/*
|
|
case Q2TE_CHAINFIST_SMOKE:
|
|
dir[0]=0; dir[1]=0; dir[2]=1;
|
|
MSG_ReadPos(&net_message, pos);
|
|
CL_ParticleSmokeEffect (pos, dir, 0, 20, 20);
|
|
break;
|
|
|
|
case Q2TE_ELECTRIC_SPARKS:
|
|
MSG_ReadPos (&net_message, pos);
|
|
MSG_ReadDir (&net_message, dir);
|
|
// CL_ParticleEffect (pos, dir, 109, 40);
|
|
CL_ParticleEffect (pos, dir, 0x75, 40);
|
|
//FIXME : replace or remove this sound
|
|
S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
*/
|
|
case Q2TE_TRACKER_EXPLOSION:
|
|
MSG_ReadPos (pos);
|
|
|
|
// effect
|
|
if (P_RunParticleEffectTypeString(pos, NULL, 1, "te_tracker_explosion"))
|
|
P_RunParticleEffect(pos, NULL, 0, 128); // TODO: needs to be nonrandom instead of 0+r%8
|
|
|
|
// light
|
|
// light
|
|
if (r_explosionlight.value)
|
|
{
|
|
dlight_t *dl;
|
|
dl = CL_AllocDlight (0);
|
|
VectorCopy (pos, dl->origin);
|
|
dl->radius = 150 * r_explosionlight.value;
|
|
dl->die = cl.time + 0.1;
|
|
dl->minlight = 250;
|
|
dl->color[0] = -0.2;
|
|
dl->color[1] = -0.2;
|
|
dl->color[2] = -0.2;
|
|
}
|
|
|
|
// sound
|
|
Q2S_StartSound (pos, 0, 0, S_PrecacheSound("weapons/disrupthit.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
case Q2TE_TELEPORT_EFFECT:
|
|
case Q2TE_DBALL_GOAL:
|
|
MSG_ReadPos (pos);
|
|
if (P_RunParticleEffectType(pos, NULL, 1, pt_teleportsplash))
|
|
P_RunParticleEffect(pos, NULL, 8, 768);
|
|
// This effect won't match ---
|
|
// Color should be 7+(rand()%8)
|
|
// not 8&~7+(rand()%8)
|
|
break;
|
|
|
|
case Q2TE_WIDOWBEAMOUT:
|
|
// this one is really annoying, it's supposed to be a random choice
|
|
// between 2*8, 13*8, 21*8, 18*8, and it respreads every frame
|
|
// into a circle but it could be faked well enough, well except for
|
|
// the fact that these effects have ids associated with them
|
|
// sort of how beams have ents associated
|
|
MSG_ReadShort(); // id
|
|
if (P_RunParticleEffectTypeString(pos, NULL, 1, "te_widowbeamout"))
|
|
P_RunParticleEffect(pos, NULL, 13*8, 300);
|
|
break;
|
|
|
|
case Q2TE_NUKEBLAST:
|
|
// same problem as te_widowbeamout, but colors are a bit easier to manage
|
|
// and there's no id to read in
|
|
MSG_ReadPos (pos);
|
|
if (P_RunParticleEffectTypeString(pos, NULL, 1, "te_nukeblast"))
|
|
P_RunParticleEffect(pos, NULL, 110, 700);
|
|
break;
|
|
|
|
case Q2TE_WIDOWSPLASH:
|
|
// there's the color issue like with te_widowbeamout, but the particles
|
|
// are spawned in an immediate circle and not substained, so it's much
|
|
// easier to manage
|
|
MSG_ReadPos (pos);
|
|
if (P_RunParticleEffectTypeString(pos, NULL, 1, "te_widowsplash"))
|
|
P_RunParticleEffect(pos, NULL, 13*8, 256);
|
|
break;
|
|
//PGM
|
|
//==============
|
|
|
|
|
|
case CRTE_LEADERBLASTER:
|
|
Host_EndGame ("CLQ2_ParseTEnt: bad/non-implemented type %i", type);
|
|
case CRTE_BLASTER_MUZZLEFLASH:
|
|
MSG_ReadPos (pos);
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
ex->flags = Q2RF_FULLBRIGHT;
|
|
ex->start = cl.q2frame.servertime - 100;
|
|
CL_NewDlightRGB(0, pos[0], pos[1], pos[2], 350, 0.5, 0.2, 0.1, 0);
|
|
P_RunParticleEffectTypeString(pos, NULL, 1, "te_muzzleflash");
|
|
break;
|
|
case CRTE_BLUE_MUZZLEFLASH:
|
|
MSG_ReadPos (pos);
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
ex->flags = Q2RF_FULLBRIGHT;
|
|
ex->start = cl.q2frame.servertime - 100;
|
|
CL_NewDlightRGB(0, pos[0], pos[1], pos[2], 350, 0.5, 0.2, 0.1, 0);
|
|
P_RunParticleEffectTypeString(pos, NULL, 1, "te_blue_muzzleflash");
|
|
break;
|
|
case CRTE_SMART_MUZZLEFLASH:
|
|
MSG_ReadPos (pos);
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
ex->flags = Q2RF_FULLBRIGHT;
|
|
ex->start = cl.q2frame.servertime - 100;
|
|
CL_NewDlightRGB(0, pos[0], pos[1], pos[2], 350, 0.5, 0.2, 0, 0.2);
|
|
P_RunParticleEffectTypeString(pos, NULL, 1, "te_smart_muzzleflash");
|
|
break;
|
|
case CRTE_LEADERFIELD:
|
|
Host_EndGame ("CLQ2_ParseTEnt: bad/non-implemented type %i", type);
|
|
case CRTE_DEATHFIELD:
|
|
MSG_ReadPos (pos);
|
|
ex = CL_AllocExplosion ();
|
|
VectorCopy (pos, ex->origin);
|
|
ex->flags = Q2RF_FULLBRIGHT;
|
|
ex->start = cl.q2frame.servertime - 100;
|
|
CL_NewDlightRGB(0, pos[0], pos[1], pos[2], 350, 0.5, 0.2, 0, 0.2);
|
|
P_RunParticleEffectTypeString(pos, NULL, 1, "te_deathfield");
|
|
break;
|
|
case CRTE_BLASTERBEAM:
|
|
MSG_ReadPos (pos);
|
|
MSG_ReadPos (pos2);
|
|
CLQ2_BlasterTrail2 (pos, pos2);
|
|
break;
|
|
case CRTE_STAIN:
|
|
Host_EndGame ("CLQ2_ParseTEnt: bad/non-implemented type %i", type);
|
|
case CRTE_FIRE:
|
|
Host_EndGame ("CLQ2_ParseTEnt: bad/non-implemented type %i", type);
|
|
case CRTE_CABLEGUT:
|
|
Host_EndGame ("CLQ2_ParseTEnt: bad/non-implemented type %i", type);
|
|
case CRTE_SMOKE:
|
|
Host_EndGame ("CLQ2_ParseTEnt: bad/non-implemented type %i", type);
|
|
|
|
default:
|
|
Host_EndGame ("CLQ2_ParseTEnt: bad/non-implemented type %i", type);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
=================
|
|
CL_NewTempEntity
|
|
=================
|
|
*/
|
|
entity_t *CL_NewTempEntity (void)
|
|
{
|
|
entity_t *ent;
|
|
|
|
if (cl_numvisedicts == MAX_VISEDICTS)
|
|
return NULL;
|
|
ent = &cl_visedicts[cl_numvisedicts];
|
|
cl_numvisedicts++;
|
|
ent->keynum = 0;
|
|
|
|
memset (ent, 0, sizeof(*ent));
|
|
|
|
#ifdef SWQUAKE
|
|
ent->palremap = D_IdentityRemap();
|
|
#endif
|
|
#ifdef PEXT_SCALE
|
|
ent->scale = 1;
|
|
#endif
|
|
ent->shaderRGBAf[0] = 1;
|
|
ent->shaderRGBAf[1] = 1;
|
|
ent->shaderRGBAf[2] = 1;
|
|
ent->shaderRGBAf[3] = 1;
|
|
return ent;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CL_UpdateBeams
|
|
=================
|
|
*/
|
|
void CL_UpdateBeams (void)
|
|
{
|
|
int bnum;
|
|
int i, j;
|
|
beam_t *b;
|
|
vec3_t dist, org;
|
|
float *vieworg;
|
|
float d;
|
|
entity_t *ent;
|
|
entity_state_t *st;
|
|
float yaw, pitch;
|
|
float forward, offset;
|
|
int lastrunningbeam = -1;
|
|
|
|
extern cvar_t cl_truelightning, v_viewheight;
|
|
|
|
// update lightning
|
|
for (bnum=0, b=cl_beams; bnum < beams_running; bnum++, b++)
|
|
{
|
|
if (!b->model)
|
|
continue;
|
|
|
|
if (b->endtime < cl.time)
|
|
{
|
|
P_DelinkTrailstate(&b->trailstate);
|
|
P_DelinkTrailstate(&b->emitstate);
|
|
b->model = NULL;
|
|
continue;
|
|
}
|
|
|
|
lastrunningbeam = bnum;
|
|
|
|
// if coming from the player, update the start position
|
|
if ((b->flags & 1) && b->entity > 0 && b->entity <= MAX_CLIENTS)
|
|
{
|
|
for (j = 0; j < cl.splitclients; j++)
|
|
{
|
|
if (b->entity == ((cl.spectator&&autocam[j])?spec_track[j]+1:(cl.playernum[j]+1)))
|
|
{
|
|
player_state_t *pl;
|
|
// VectorSubtract(cl.simorg, b->start, org);
|
|
// VectorAdd(b->end, org, b->end); //move the end point by simorg-start
|
|
|
|
pl = &cl.frames[cl.parsecount&UPDATE_MASK].playerstate[b->entity-1];
|
|
if (pl->messagenum == cl.parsecount)
|
|
{
|
|
vec3_t fwd, org, ang;
|
|
float delta, f, len;
|
|
|
|
if (cl.spectator && autocam[j])
|
|
{
|
|
vieworg = pl->origin;
|
|
}
|
|
else
|
|
vieworg = cl.simorg[j];
|
|
|
|
VectorCopy (vieworg, b->start);
|
|
b->start[2] += cl.crouch[j] + bound(-7, v_viewheight.value, 4);
|
|
|
|
f = bound(0, cl_truelightning.value, 1);
|
|
|
|
if (!f)
|
|
break;
|
|
|
|
VectorSubtract (playerbeam_end[j], vieworg, org);
|
|
len = VectorLength(org);
|
|
org[2] -= 22; // adjust for view height
|
|
vectoangles (org, ang);
|
|
|
|
// lerp pitch
|
|
ang[0] = -ang[0];
|
|
if (ang[0] < -180)
|
|
ang[0] += 360;
|
|
ang[0] += (cl.simangles[j][0] - ang[0]) * f;
|
|
|
|
// lerp yaw
|
|
delta = cl.simangles[j][1] - ang[1];
|
|
if (delta > 180)
|
|
delta -= 360;
|
|
if (delta < -180)
|
|
delta += 360;
|
|
ang[1] += delta * f;
|
|
ang[2] = 0;
|
|
|
|
AngleVectors (ang, fwd, ang, ang);
|
|
VectorCopy(fwd, ang);
|
|
VectorScale (fwd, len, fwd);
|
|
VectorCopy (cl.simorg[j], org);
|
|
org[2] += 16;
|
|
VectorAdd (org, fwd, b->end);
|
|
|
|
if (cl_beam_trace.value)
|
|
{
|
|
vec3_t normal;
|
|
VectorMA(org, len+4, ang, fwd);
|
|
if (TraceLineN(org, fwd, ang, normal))
|
|
VectorCopy (ang, b->end);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (b->flags & STREAM_ATTACHED)
|
|
{
|
|
player_state_t *pl;
|
|
st = CL_FindPacketEntity(b->entity);
|
|
if (st)
|
|
{
|
|
VectorCopy(st->origin, b->start);
|
|
}
|
|
else if (b->entity <= MAX_CLIENTS && b->entity > 0)
|
|
{
|
|
pl = &cl.frames[cl.parsecount&UPDATE_MASK].playerstate[b->entity-1];
|
|
VectorCopy(pl->origin, b->start);
|
|
b->start[2]+=16;
|
|
}
|
|
}
|
|
|
|
// calculate pitch and yaw
|
|
VectorSubtract (b->end, b->start, dist);
|
|
|
|
if (dist[1] == 0 && dist[0] == 0)
|
|
{
|
|
yaw = 0;
|
|
if (dist[2] > 0)
|
|
pitch = 90;
|
|
else
|
|
pitch = 270;
|
|
}
|
|
else
|
|
{
|
|
yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI);
|
|
if (yaw < 0)
|
|
yaw += 360;
|
|
|
|
forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]);
|
|
pitch = (int) (atan2(dist[2], forward) * 180 / M_PI);
|
|
if (pitch < 0)
|
|
pitch += 360;
|
|
}
|
|
|
|
if (ruleset_allow_particle_lightning.value)
|
|
if (b->particleeffect >= 0 && !P_ParticleTrail(b->start, b->end, b->particleeffect, &b->trailstate))
|
|
continue;
|
|
|
|
// add new entities for the lightning
|
|
VectorCopy (b->start, org);
|
|
d = VectorNormalize(dist);
|
|
|
|
if(b->flags & 2)
|
|
{
|
|
offset = (int)(cl.time*40)%30;
|
|
for(i = 0; i < 3; i++)
|
|
{
|
|
org[i] += dist[i]*offset;
|
|
}
|
|
}
|
|
|
|
while (d > 0)
|
|
{
|
|
ent = CL_NewTempEntity ();
|
|
if (!ent)
|
|
return;
|
|
VectorCopy (org, ent->origin);
|
|
ent->model = b->model;
|
|
ent->drawflags |= MLS_ABSLIGHT;
|
|
ent->abslight = 192;
|
|
ent->shaderRGBAf[3] = b->alpha;
|
|
|
|
ent->angles[0] = -pitch;
|
|
ent->angles[1] = yaw;
|
|
ent->angles[2] = rand()%360;
|
|
AngleVectors(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);
|
|
ent->angles[0] = pitch;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
org[i] += dist[i]*30;
|
|
d -= 30;
|
|
}
|
|
}
|
|
|
|
beams_running = lastrunningbeam+1;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CL_UpdateExplosions
|
|
=================
|
|
*/
|
|
void CL_UpdateExplosions (void)
|
|
{
|
|
int i;
|
|
float f;
|
|
int of;
|
|
int numframes;
|
|
int firstframe;
|
|
explosion_t *ex;
|
|
entity_t *ent;
|
|
int lastrunningexplosion = -1;
|
|
|
|
for (i=0, ex=cl_explosions; i < explosions_running; i++, ex++)
|
|
{
|
|
if (!ex->model)
|
|
continue;
|
|
|
|
lastrunningexplosion = i;
|
|
f = ex->framerate*(cl.time - ex->start);
|
|
if (ex->firstframe >= 0)
|
|
{
|
|
firstframe = ex->firstframe;
|
|
numframes = ex->numframes;
|
|
}
|
|
else
|
|
{
|
|
firstframe = 0;
|
|
numframes = ex->model->numframes;
|
|
}
|
|
|
|
of = (int)f-1;
|
|
if ((int)f >= numframes || (int)f < 0)
|
|
{
|
|
ex->model = NULL;
|
|
continue;
|
|
}
|
|
if (of < 0)
|
|
of = 0;
|
|
|
|
ent = CL_NewTempEntity ();
|
|
if (!ent)
|
|
return;
|
|
VectorCopy (ex->origin, ent->origin);
|
|
VectorCopy (ex->oldorigin, ent->oldorigin);
|
|
VectorCopy (ex->angles, ent->angles);
|
|
ent->skinnum = ex->skinnum;
|
|
ent->angles[0]*=-1;
|
|
AngleVectors(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);
|
|
VectorInverse(ent->axis[1]);
|
|
ent->model = ex->model;
|
|
ent->framestate.g[FS_REG].frame[0] = (int)f+firstframe;
|
|
ent->framestate.g[FS_REG].frame[1] = of+firstframe;
|
|
ent->framestate.g[FS_REG].lerpfrac = 1-(f - (int)f);
|
|
ent->shaderRGBAf[3] = 1.0 - f/(numframes);
|
|
ent->flags = ex->flags;
|
|
}
|
|
|
|
explosions_running = lastrunningexplosion + 1;
|
|
}
|
|
|
|
entity_state_t *CL_FindPacketEntity(int num);
|
|
|
|
/*
|
|
=================
|
|
CL_UpdateTEnts
|
|
=================
|
|
*/
|
|
void CL_UpdateTEnts (void)
|
|
{
|
|
CL_UpdateBeams ();
|
|
CL_UpdateExplosions ();
|
|
}
|