mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-26 05:41:52 +00:00
eukara
f317282571
* Quake II: read the skin, effects, renderfx, and solidsize fields as unsigned shorts. * Add missing header definition for int MSG_ReadUShort(); * Rename MSG_ReadUShort() to MSG_ReadUInt16() as per @Shpoike's wishes * solidsize needs to be read in via MSG_ReadSize16 because it does protocol conversions (@Shpoike) * Q2UFRAME16 and Q2USOUND should be read unsigned as well, as it matches the internal data within FTE (@Shpoike)
2559 lines
73 KiB
C
2559 lines
73 KiB
C
#include "quakedef.h"
|
|
#include "particles.h"
|
|
|
|
#ifdef Q2CLIENT
|
|
#include "shader.h"
|
|
|
|
//q2pro's framerate scaling runs the entire server at a higher rate (including gamecode).
|
|
//this allows lower latency on player movements without breaking things too much
|
|
//animations are still assumed to run at 10fps, so those need some fixup too
|
|
//events are keyed to not renew twice within the same 10fps window, unless the entity was actually updated.
|
|
|
|
extern cvar_t r_drawviewmodel;
|
|
|
|
extern cvar_t cl_nopred;
|
|
typedef enum
|
|
{
|
|
Q2EV_NONE,
|
|
Q2EV_ITEM_RESPAWN,
|
|
Q2EV_FOOTSTEP,
|
|
Q2EV_FALLSHORT,
|
|
Q2EV_FALL,
|
|
Q2EV_FALLFAR,
|
|
Q2EV_PLAYER_TELEPORT,
|
|
Q2EV_OTHER_TELEPORT
|
|
} q2entity_event_t;
|
|
|
|
#define Q2PMF_NO_PREDICTION 64 // temporarily disables prediction (used for grappling hook)
|
|
|
|
float LerpAngle (float a2, float a1, float frac)
|
|
{
|
|
if (a1 - a2 > 180)
|
|
a1 -= 360;
|
|
if (a1 - a2 < -180)
|
|
a1 += 360;
|
|
return a2 + frac * (a1 - a2);
|
|
}
|
|
|
|
// entity_state_t->effects
|
|
// Effects are things handled on the client side (lights, particles, frame animations)
|
|
// that happen constantly on the given entity.
|
|
// An entity that has effects will be sent to the client
|
|
// even if it has a zero index model.
|
|
#define Q2EF_ROTATE 0x00000001 // rotate (bonus items)
|
|
#define Q2EF_GIB 0x00000002 // leave a trail
|
|
#define Q2EF_BLASTER 0x00000008 // redlight + trail
|
|
#define Q2EF_ROCKET 0x00000010 // redlight + trail
|
|
#define Q2EF_GRENADE 0x00000020
|
|
#define Q2EF_HYPERBLASTER 0x00000040
|
|
#define Q2EF_BFG 0x00000080
|
|
#define Q2EF_COLOR_SHELL 0x00000100
|
|
#define Q2EF_POWERSCREEN 0x00000200
|
|
#define Q2EF_ANIM01 0x00000400 // automatically cycle between frames 0 and 1 at 2 hz
|
|
#define Q2EF_ANIM23 0x00000800 // automatically cycle between frames 2 and 3 at 2 hz
|
|
#define Q2EF_ANIM_ALL 0x00001000 // automatically cycle through all frames at 2hz
|
|
#define Q2EF_ANIM_ALLFAST 0x00002000 // automatically cycle through all frames at 10hz
|
|
#define Q2EF_FLIES 0x00004000
|
|
#define Q2EF_QUAD 0x00008000
|
|
#define Q2EF_PENT 0x00010000
|
|
#define Q2EF_TELEPORTER 0x00020000 // particle fountain
|
|
#define Q2EF_FLAG1 0x00040000
|
|
#define Q2EF_FLAG2 0x00080000
|
|
// RAFAEL
|
|
#define Q2EF_IONRIPPER 0x00100000
|
|
#define Q2EF_GREENGIB 0x00200000
|
|
#define Q2EF_BLUEHYPERBLASTER 0x00400000
|
|
#define Q2EF_SPINNINGLIGHTS 0x00800000
|
|
#define Q2EF_PLASMA 0x01000000
|
|
#define Q2EF_TRAP 0x02000000
|
|
|
|
//ROGUE
|
|
#define Q2EF_TRACKER 0x04000000
|
|
#define Q2EF_DOUBLE 0x08000000
|
|
#define Q2EF_SPHERETRANS 0x10000000
|
|
#define Q2EF_TAGTRAIL 0x20000000
|
|
#define Q2EF_HALF_DAMAGE 0x40000000
|
|
#define Q2EF_TRACKERTRAIL 0x80000000
|
|
//ROGUE
|
|
|
|
|
|
|
|
|
|
#define Q2MAX_STATS 32
|
|
|
|
typedef struct q2centity_s
|
|
{
|
|
entity_state_t baseline; // delta from this if not from a previous frame
|
|
entity_state_t current;
|
|
entity_state_t prev; // will always be valid, but might just be a copy of current
|
|
|
|
int serverframe; // if not current, this ent isn't in the frame
|
|
|
|
trailkey_t trailstate;
|
|
trailkey_t emitstate;
|
|
// float trailcount; // for diminishing grenade trails
|
|
vec3_t lerp_origin; // for trails (variable hz)
|
|
|
|
float fly_stoptime;
|
|
} q2centity_t;
|
|
|
|
static void Q2S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation, float delay)
|
|
{
|
|
S_StartSound(entnum, entchannel, sfx, origin, NULL, fvol, attenuation, -delay, 0, 0);
|
|
}
|
|
sfx_t *S_PrecacheSexedSound(int entnum, const char *soundname)
|
|
{
|
|
if (soundname[0] == '*')
|
|
{ //a 'sexed' sound
|
|
if (entnum > 0 && entnum <= MAX_CLIENTS)
|
|
{
|
|
char *model = InfoBuf_ValueForKey(&cl.players[entnum-1].userinfo, "skin");
|
|
char *skin;
|
|
skin = strchr(model, '/');
|
|
if (skin)
|
|
*skin = '\0';
|
|
if (*model)
|
|
{
|
|
sfx_t *sfx = S_PrecacheSound(va("players/%s/%s", model, soundname+1));
|
|
if (sfx && sfx->loadstate != SLS_FAILED) //warning: the sound might still be loading (and later fail).
|
|
return sfx;
|
|
}
|
|
}
|
|
return S_PrecacheSound(va("players/male/%s", soundname+1));
|
|
}
|
|
return S_PrecacheSound(soundname);
|
|
}
|
|
|
|
|
|
void CLQ2_EntityEvent(entity_state_t *es)
|
|
{
|
|
switch (es->u.q2.event)
|
|
{
|
|
case Q2EV_NONE:
|
|
case Q2EV_OTHER_TELEPORT: //inihibits interpolation. not an event in itself.
|
|
break;
|
|
case Q2EV_ITEM_RESPAWN:
|
|
pe->RunParticleEffectState(es->origin, NULL, 1, pt_q2[Q2PT_RESPAWN], NULL);
|
|
break;
|
|
case Q2EV_PLAYER_TELEPORT:
|
|
pe->RunParticleEffectState(es->origin, NULL, 1, pt_q2[Q2PT_PLAYER_TELEPORT], NULL);
|
|
break;
|
|
case Q2EV_FOOTSTEP:
|
|
pe->RunParticleEffectState(es->origin, NULL, 1, pt_q2[Q2PT_FOOTSTEP], NULL);
|
|
break;
|
|
case Q2EV_FALLSHORT:
|
|
Q2S_StartSound (NULL, es->number, CHAN_AUTO, S_PrecacheSound ("player/land1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
case Q2EV_FALL:
|
|
Q2S_StartSound (NULL, es->number, CHAN_AUTO, S_PrecacheSexedSound (es->number, "*fall2.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
case Q2EV_FALLFAR:
|
|
Q2S_StartSound (NULL, es->number, CHAN_AUTO, S_PrecacheSexedSound (es->number, "*fall1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
default:
|
|
Con_Printf("event %u not supported\n", es->u.q2.event);
|
|
break;
|
|
}
|
|
};
|
|
void CLQ2_TeleporterParticles(entity_state_t *es){};
|
|
|
|
/*these are emissive effects (ie: emitted each frame), but they're also mutually exclusive, so sharing emitstate is fine*/
|
|
void CLQ2_Tracker_Shell(q2centity_t *ent, vec3_t org)
|
|
{
|
|
P_EmitEffect (org, NULL, 0, pt_q2[Q2PT_TRACKERSHELL], &(ent->emitstate));
|
|
};
|
|
void CLQ2_BfgParticles(q2centity_t *ent, vec3_t org)
|
|
{
|
|
P_EmitEffect (org, NULL, 0, pt_q2[Q2PT_BFGPARTICLES], &(ent->emitstate));
|
|
};
|
|
void CLQ2_FlyEffect(q2centity_t *ent, vec3_t org)
|
|
{
|
|
float starttime, n;
|
|
float cltime = cl.time;
|
|
int count;
|
|
if (cl.paused)
|
|
return;
|
|
|
|
//q2 ramps up to 162 within the first 20 secs, sits at 162 for the next 20, then ramps back down for the 20 after that.
|
|
//I have no idea how pvs issues are handled.
|
|
|
|
if (ent->fly_stoptime < cltime)
|
|
{
|
|
starttime = cltime;
|
|
ent->fly_stoptime = cltime + 60;
|
|
}
|
|
else
|
|
{
|
|
starttime = ent->fly_stoptime - 60;
|
|
}
|
|
|
|
n = cltime - starttime;
|
|
if (n < 20)
|
|
count = n * 162 / 20.0;
|
|
else
|
|
{
|
|
n = ent->fly_stoptime - cltime;
|
|
if (n < 20)
|
|
count = n * 162 / 20.0;
|
|
else
|
|
count = 162;
|
|
}
|
|
if (count < 0)
|
|
return;
|
|
|
|
//these are assumed to be spawned anew
|
|
pe->RunParticleEffectState(org, NULL, count, pt_q2[Q2PT_FLIES], &(ent->emitstate));
|
|
};
|
|
|
|
|
|
#define MAX_Q2EDICTS 1024
|
|
#define MAX_PARSE_ENTITIES 1024
|
|
|
|
|
|
static q2centity_t cl_entities[MAX_Q2EDICTS];
|
|
entity_state_t clq2_parse_entities[MAX_PARSE_ENTITIES];
|
|
|
|
void CL_SmokeAndFlash(vec3_t origin);
|
|
|
|
void CL_GetNumberedEntityInfo (int num, float *org, float *ang)
|
|
{
|
|
q2centity_t *ent;
|
|
|
|
if (num < 0 || num >= MAX_Q2EDICTS)
|
|
Host_EndGame ("CL_GetNumberedEntityInfo: bad ent");
|
|
ent = &cl_entities[num];
|
|
|
|
if (org)
|
|
VectorCopy (ent->current.origin, org);
|
|
if (ang)
|
|
VectorCopy (ent->current.angles, ang);
|
|
|
|
|
|
// FIXME: bmodel issues...
|
|
}
|
|
|
|
#ifdef Q2SERVER
|
|
void CLQ2_WriteDemoBaselines(sizebuf_t *buf)
|
|
{
|
|
int i;
|
|
q2entity_state_t nullstate = {0};
|
|
for (i = 0; i < MAX_Q2EDICTS; i++)
|
|
{
|
|
q2entity_state_t es;
|
|
entity_state_t *base = &cl_entities[i].baseline;
|
|
if (!base->modelindex)
|
|
continue;
|
|
|
|
//I brought these copies on myself...
|
|
es.number = i;
|
|
VectorCopy(base->origin, es.origin);
|
|
VectorCopy(base->angles, es.angles);
|
|
VectorCopy(base->u.q2.old_origin, es.old_origin);
|
|
es.modelindex = base->modelindex;
|
|
es.modelindex2 = base->modelindex2;
|
|
es.modelindex3 = base->u.q2.modelindex3;
|
|
es.modelindex4 = base->u.q2.modelindex4;
|
|
es.frame = base->frame;
|
|
es.skinnum = base->skinnum;
|
|
es.effects = base->effects;
|
|
es.renderfx = base->u.q2.renderfx;
|
|
es.solid = base->solidsize;
|
|
es.sound = base->u.q2.sound;
|
|
es.event = base->u.q2.event;
|
|
|
|
if (buf->cursize > buf->maxsize/2)
|
|
{
|
|
CL_WriteRecordQ2DemoMessage (buf);
|
|
SZ_Clear (buf);
|
|
}
|
|
|
|
MSG_WriteByte (buf, svcq2_spawnbaseline);
|
|
MSGQ2_WriteDeltaEntity(&nullstate, &es, buf, true, true);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CLQ2_ClearState(void)
|
|
{
|
|
memset(cl_entities, 0, sizeof(cl_entities));
|
|
}
|
|
|
|
#include "q2m_flash.c"
|
|
void CLQ2_RunMuzzleFlash2 (int ent, int flash_number)
|
|
{
|
|
vec3_t origin;
|
|
dlight_t *dl;
|
|
vec3_t forward, right, up;
|
|
char soundname[64];
|
|
int ef;
|
|
|
|
if (flash_number < 0 || flash_number >= sizeof(monster_flash_offset)/sizeof(monster_flash_offset[0]))
|
|
return;
|
|
|
|
// locate the origin
|
|
AngleVectors (cl_entities[ent].current.angles, forward, right, up);
|
|
origin[0] = cl_entities[ent].current.origin[0] + forward[0] * monster_flash_offset[flash_number].offset[0] + right[0] * monster_flash_offset[flash_number].offset[1];
|
|
origin[1] = cl_entities[ent].current.origin[1] + forward[1] * monster_flash_offset[flash_number].offset[0] + right[1] * monster_flash_offset[flash_number].offset[1];
|
|
origin[2] = cl_entities[ent].current.origin[2] + forward[2] * monster_flash_offset[flash_number].offset[0] + right[2] * monster_flash_offset[flash_number].offset[1] + monster_flash_offset[flash_number].offset[2];
|
|
|
|
ef = P_FindParticleType(monster_flash_offset[flash_number].name);
|
|
if (ef != P_INVALID)
|
|
{
|
|
P_RunParticleEffectType(origin, NULL, 1, ef);
|
|
return;
|
|
}
|
|
|
|
//the rest of the function is legacy code.
|
|
|
|
dl = CL_AllocDlight (ent);
|
|
VectorCopy (origin, dl->origin);
|
|
dl->radius = 200 + (rand()&31);
|
|
// dl->minlight = 32;
|
|
dl->die = cl.time + 0.1;
|
|
|
|
switch (flash_number)
|
|
{
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_1:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_2:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_3:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_4:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_5:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_6:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_7:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_8:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_9:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_10:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_11:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_12:
|
|
case Q2MZ2_INFANTRY_MACHINEGUN_13:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
P_RunParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_1:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_2:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_3:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_4:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_5:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_6:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_7:
|
|
case Q2MZ2_SOLDIER_MACHINEGUN_8:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
P_RunParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("soldier/solatck3.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_GUNNER_MACHINEGUN_1:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_2:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_3:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_4:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_5:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_6:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_7:
|
|
case Q2MZ2_GUNNER_MACHINEGUN_8:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
P_RunParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("gunner/gunatck2.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_ACTOR_MACHINEGUN_1:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_1:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_2:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_3:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_4:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_5:
|
|
case Q2MZ2_SUPERTANK_MACHINEGUN_6:
|
|
case Q2MZ2_TURRET_MACHINEGUN: // PGM
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
|
|
P_RunParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_BOSS2_MACHINEGUN_L1:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_L2:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_L3:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_L4:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_L5:
|
|
case Q2MZ2_CARRIER_MACHINEGUN_L1: // PMM
|
|
case Q2MZ2_CARRIER_MACHINEGUN_L2: // PMM
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
|
|
P_RunParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("infantry/infatck1.wav"), 1, ATTN_NONE, 0);
|
|
break;
|
|
|
|
case Q2MZ2_SOLDIER_BLASTER_1:
|
|
case Q2MZ2_SOLDIER_BLASTER_2:
|
|
case Q2MZ2_SOLDIER_BLASTER_3:
|
|
case Q2MZ2_SOLDIER_BLASTER_4:
|
|
case Q2MZ2_SOLDIER_BLASTER_5:
|
|
case Q2MZ2_SOLDIER_BLASTER_6:
|
|
case Q2MZ2_SOLDIER_BLASTER_7:
|
|
case Q2MZ2_SOLDIER_BLASTER_8:
|
|
case Q2MZ2_TURRET_BLASTER: // PGM
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("soldier/solatck2.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_FLYER_BLASTER_1:
|
|
case Q2MZ2_FLYER_BLASTER_2:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("flyer/flyatck3.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_MEDIC_BLASTER_1:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("medic/medatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_HOVER_BLASTER_1:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("hover/hovatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_FLOAT_BLASTER_1:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("floater/fltatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_SOLDIER_SHOTGUN_1:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_2:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_3:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_4:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_5:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_6:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_7:
|
|
case Q2MZ2_SOLDIER_SHOTGUN_8:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("soldier/solatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_TANK_BLASTER_1:
|
|
case Q2MZ2_TANK_BLASTER_2:
|
|
case Q2MZ2_TANK_BLASTER_3:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_TANK_MACHINEGUN_1:
|
|
case Q2MZ2_TANK_MACHINEGUN_2:
|
|
case Q2MZ2_TANK_MACHINEGUN_3:
|
|
case Q2MZ2_TANK_MACHINEGUN_4:
|
|
case Q2MZ2_TANK_MACHINEGUN_5:
|
|
case Q2MZ2_TANK_MACHINEGUN_6:
|
|
case Q2MZ2_TANK_MACHINEGUN_7:
|
|
case Q2MZ2_TANK_MACHINEGUN_8:
|
|
case Q2MZ2_TANK_MACHINEGUN_9:
|
|
case Q2MZ2_TANK_MACHINEGUN_10:
|
|
case Q2MZ2_TANK_MACHINEGUN_11:
|
|
case Q2MZ2_TANK_MACHINEGUN_12:
|
|
case Q2MZ2_TANK_MACHINEGUN_13:
|
|
case Q2MZ2_TANK_MACHINEGUN_14:
|
|
case Q2MZ2_TANK_MACHINEGUN_15:
|
|
case Q2MZ2_TANK_MACHINEGUN_16:
|
|
case Q2MZ2_TANK_MACHINEGUN_17:
|
|
case Q2MZ2_TANK_MACHINEGUN_18:
|
|
case Q2MZ2_TANK_MACHINEGUN_19:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
P_RunParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
snprintf(soundname, sizeof(soundname), "tank/tnkatk2%c.wav", 'a' + rand() % 5);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound(soundname), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_CHICK_ROCKET_1:
|
|
case Q2MZ2_TURRET_ROCKET: // PGM
|
|
dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("chick/chkatck2.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_TANK_ROCKET_1:
|
|
case Q2MZ2_TANK_ROCKET_2:
|
|
case Q2MZ2_TANK_ROCKET_3:
|
|
dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("tank/tnkatck1.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_SUPERTANK_ROCKET_1:
|
|
case Q2MZ2_SUPERTANK_ROCKET_2:
|
|
case Q2MZ2_SUPERTANK_ROCKET_3:
|
|
case Q2MZ2_BOSS2_ROCKET_1:
|
|
case Q2MZ2_BOSS2_ROCKET_2:
|
|
case Q2MZ2_BOSS2_ROCKET_3:
|
|
case Q2MZ2_BOSS2_ROCKET_4:
|
|
case Q2MZ2_CARRIER_ROCKET_1:
|
|
// case Q2MZ2_CARRIER_ROCKET_2:
|
|
// case Q2MZ2_CARRIER_ROCKET_3:
|
|
// case Q2MZ2_CARRIER_ROCKET_4:
|
|
dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("tank/rocket.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_GUNNER_GRENADE_1:
|
|
case Q2MZ2_GUNNER_GRENADE_2:
|
|
case Q2MZ2_GUNNER_GRENADE_3:
|
|
case Q2MZ2_GUNNER_GRENADE_4:
|
|
dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("gunner/gunatck3.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_GLADIATOR_RAILGUN_1:
|
|
// PMM
|
|
case Q2MZ2_CARRIER_RAILGUN:
|
|
case Q2MZ2_WIDOW_RAIL:
|
|
// pmm
|
|
dl->color[0] = 0.5;dl->color[1] = 0.5;dl->color[2] = 1.0;
|
|
break;
|
|
|
|
// --- Xian's shit starts ---
|
|
case Q2MZ2_MAKRON_BFG:
|
|
dl->color[0] = 0.5;dl->color[1] = 1 ;dl->color[2] = 0.5;
|
|
//Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_RegisterSound("makron/bfg_fire.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_MAKRON_BLASTER_1:
|
|
case Q2MZ2_MAKRON_BLASTER_2:
|
|
case Q2MZ2_MAKRON_BLASTER_3:
|
|
case Q2MZ2_MAKRON_BLASTER_4:
|
|
case Q2MZ2_MAKRON_BLASTER_5:
|
|
case Q2MZ2_MAKRON_BLASTER_6:
|
|
case Q2MZ2_MAKRON_BLASTER_7:
|
|
case Q2MZ2_MAKRON_BLASTER_8:
|
|
case Q2MZ2_MAKRON_BLASTER_9:
|
|
case Q2MZ2_MAKRON_BLASTER_10:
|
|
case Q2MZ2_MAKRON_BLASTER_11:
|
|
case Q2MZ2_MAKRON_BLASTER_12:
|
|
case Q2MZ2_MAKRON_BLASTER_13:
|
|
case Q2MZ2_MAKRON_BLASTER_14:
|
|
case Q2MZ2_MAKRON_BLASTER_15:
|
|
case Q2MZ2_MAKRON_BLASTER_16:
|
|
case Q2MZ2_MAKRON_BLASTER_17:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("makron/blaster.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_JORG_MACHINEGUN_L1:
|
|
case Q2MZ2_JORG_MACHINEGUN_L2:
|
|
case Q2MZ2_JORG_MACHINEGUN_L3:
|
|
case Q2MZ2_JORG_MACHINEGUN_L4:
|
|
case Q2MZ2_JORG_MACHINEGUN_L5:
|
|
case Q2MZ2_JORG_MACHINEGUN_L6:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
P_RunParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("boss3/xfire.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_JORG_MACHINEGUN_R1:
|
|
case Q2MZ2_JORG_MACHINEGUN_R2:
|
|
case Q2MZ2_JORG_MACHINEGUN_R3:
|
|
case Q2MZ2_JORG_MACHINEGUN_R4:
|
|
case Q2MZ2_JORG_MACHINEGUN_R5:
|
|
case Q2MZ2_JORG_MACHINEGUN_R6:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
P_RunParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
break;
|
|
|
|
case Q2MZ2_JORG_BFG_1:
|
|
dl->color[0] = 0.5;dl->color[1] = 1 ;dl->color[2] = 0.5;
|
|
break;
|
|
|
|
case Q2MZ2_BOSS2_MACHINEGUN_R1:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_R2:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_R3:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_R4:
|
|
case Q2MZ2_BOSS2_MACHINEGUN_R5:
|
|
case Q2MZ2_CARRIER_MACHINEGUN_R1: // PMM
|
|
case Q2MZ2_CARRIER_MACHINEGUN_R2: // PMM
|
|
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
|
|
P_RunParticleEffect (origin, vec3_origin, 0, 40);
|
|
CL_SmokeAndFlash(origin);
|
|
break;
|
|
|
|
// ======
|
|
// ROGUE
|
|
case Q2MZ2_STALKER_BLASTER:
|
|
case Q2MZ2_DAEDALUS_BLASTER:
|
|
case Q2MZ2_MEDIC_BLASTER_2:
|
|
case Q2MZ2_WIDOW_BLASTER:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP1:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP2:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP3:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP4:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP5:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP6:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP7:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP8:
|
|
case Q2MZ2_WIDOW_BLASTER_SWEEP9:
|
|
case Q2MZ2_WIDOW_BLASTER_100:
|
|
case Q2MZ2_WIDOW_BLASTER_90:
|
|
case Q2MZ2_WIDOW_BLASTER_80:
|
|
case Q2MZ2_WIDOW_BLASTER_70:
|
|
case Q2MZ2_WIDOW_BLASTER_60:
|
|
case Q2MZ2_WIDOW_BLASTER_50:
|
|
case Q2MZ2_WIDOW_BLASTER_40:
|
|
case Q2MZ2_WIDOW_BLASTER_30:
|
|
case Q2MZ2_WIDOW_BLASTER_20:
|
|
case Q2MZ2_WIDOW_BLASTER_10:
|
|
case Q2MZ2_WIDOW_BLASTER_0:
|
|
case Q2MZ2_WIDOW_BLASTER_10L:
|
|
case Q2MZ2_WIDOW_BLASTER_20L:
|
|
case Q2MZ2_WIDOW_BLASTER_30L:
|
|
case Q2MZ2_WIDOW_BLASTER_40L:
|
|
case Q2MZ2_WIDOW_BLASTER_50L:
|
|
case Q2MZ2_WIDOW_BLASTER_60L:
|
|
case Q2MZ2_WIDOW_BLASTER_70L:
|
|
case Q2MZ2_WIDOW_RUN_1:
|
|
case Q2MZ2_WIDOW_RUN_2:
|
|
case Q2MZ2_WIDOW_RUN_3:
|
|
case Q2MZ2_WIDOW_RUN_4:
|
|
case Q2MZ2_WIDOW_RUN_5:
|
|
case Q2MZ2_WIDOW_RUN_6:
|
|
case Q2MZ2_WIDOW_RUN_7:
|
|
case Q2MZ2_WIDOW_RUN_8:
|
|
dl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_WIDOW_DISRUPTOR:
|
|
dl->color[0] = -1;dl->color[1] = -1;dl->color[2] = -1;
|
|
Q2S_StartSound (NULL, ent, CHAN_WEAPON, S_PrecacheSound("weapons/disint2.wav"), 1, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ2_WIDOW_PLASMABEAM:
|
|
case Q2MZ2_WIDOW2_BEAMER_1:
|
|
case Q2MZ2_WIDOW2_BEAMER_2:
|
|
case Q2MZ2_WIDOW2_BEAMER_3:
|
|
case Q2MZ2_WIDOW2_BEAMER_4:
|
|
case Q2MZ2_WIDOW2_BEAMER_5:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_1:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_2:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_3:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_4:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_5:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_6:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_7:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_8:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_9:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_10:
|
|
case Q2MZ2_WIDOW2_BEAM_SWEEP_11:
|
|
dl->radius = 300 + (rand()&100);
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
dl->die = cl.time + 200;
|
|
break;
|
|
// ROGUE
|
|
// ======
|
|
|
|
// --- Xian's shit ends ---
|
|
|
|
//hmm... he must take AGES on the loo.... :p
|
|
}
|
|
}
|
|
|
|
void CLQ2_ParseMuzzleFlash (void)
|
|
{
|
|
vec3_t fv, rv, dummy;
|
|
dlight_t *dl;
|
|
int i, weapon;
|
|
vec3_t org, ang;
|
|
int silenced;
|
|
float volume;
|
|
char soundname[64];
|
|
|
|
i = (unsigned short)(short)MSG_ReadShort ();
|
|
if (i < 1 || i >= Q2MAX_EDICTS)
|
|
Host_Error ("CL_ParseMuzzleFlash: bad entity");
|
|
|
|
weapon = MSG_ReadByte ();
|
|
silenced = weapon & Q2MZ_SILENCED;
|
|
weapon &= ~Q2MZ_SILENCED;
|
|
|
|
CL_GetNumberedEntityInfo(i, org, ang);
|
|
|
|
dl = CL_AllocDlight (i);
|
|
VectorCopy (org, dl->origin);
|
|
AngleVectors (ang, fv, rv, dummy);
|
|
VectorMA (dl->origin, 18, fv, dl->origin);
|
|
VectorMA (dl->origin, 16, rv, dl->origin);
|
|
if (silenced)
|
|
dl->radius = 100 + (rand()&31);
|
|
else
|
|
dl->radius = 200 + (rand()&31);
|
|
dl->minlight = 32;
|
|
dl->die = cl.time+0.05; //+ 0.1;
|
|
dl->decay = 1;
|
|
|
|
dl->channelfade[0] = 2;
|
|
dl->channelfade[1] = 2;
|
|
dl->channelfade[2] = 2;
|
|
|
|
if (silenced)
|
|
volume = 0.2;
|
|
else
|
|
volume = 1;
|
|
|
|
|
|
switch (weapon)
|
|
{
|
|
case Q2MZ_BLASTER:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_BLUEHYPERBLASTER:
|
|
dl->color[0] = 0;dl->color[1] = 0;dl->color[2] = 1;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_HYPERBLASTER:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_MACHINEGUN:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q_snprintfz(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ_SHOTGUN:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/shotgf1b.wav"), volume, ATTN_NORM, 0);
|
|
Q2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound("weapons/shotgr1b.wav"), volume, ATTN_NORM, 0.1);
|
|
break;
|
|
case Q2MZ_SSHOTGUN:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/sshotf1b.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_CHAINGUN1:
|
|
dl->radius = 200 + (rand()&31);
|
|
dl->color[0] = 1;dl->color[1] = 0.25;dl->color[2] = 0;
|
|
Q_snprintfz(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_CHAINGUN2:
|
|
dl->radius = 225 + (rand()&31);
|
|
dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0;
|
|
dl->die = cl.time + 0.1; // long delay
|
|
Q_snprintfz(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);
|
|
Q_snprintfz(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound(soundname), volume, ATTN_NORM, 0.05);
|
|
break;
|
|
case Q2MZ_CHAINGUN3:
|
|
dl->radius = 250 + (rand()&31);
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
dl->die = cl.time + 0.1; // long delay
|
|
Q_snprintfz(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound(soundname), volume, ATTN_NORM, 0);
|
|
Q_snprintfz(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound(soundname), volume, ATTN_NORM, 0.033);
|
|
Q_snprintfz(soundname, sizeof(soundname), "weapons/machgf%ib.wav", (rand() % 5) + 1);
|
|
Q2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound(soundname), volume, ATTN_NORM, 0.066);
|
|
break;
|
|
|
|
case Q2MZ_RAILGUN:
|
|
dl->color[0] = 0.5;dl->color[1] = 0.5;dl->color[2] = 1;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/railgf1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_ROCKET:
|
|
dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0.2;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/rocklf1a.wav"), volume, ATTN_NORM, 0);
|
|
Q2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound("weapons/rocklr1b.wav"), volume, ATTN_NORM, 0.1);
|
|
break;
|
|
case Q2MZ_GRENADE:
|
|
dl->color[0] = 1;dl->color[1] = 0.5;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/grenlf1a.wav"), volume, ATTN_NORM, 0);
|
|
Q2S_StartSound (NULL, i, CHAN_AUTO, S_PrecacheSound("weapons/grenlr1b.wav"), volume, ATTN_NORM, 0.1);
|
|
break;
|
|
case Q2MZ_BFG:
|
|
dl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/bfg__f1y.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
|
|
case Q2MZ_LOGIN:
|
|
dl->color[0] = 0;dl->color[1] = 1; dl->color[2] = 0;
|
|
dl->die = cl.time + 1.0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0);
|
|
// CL_LogoutEffect (pl->current.origin, weapon);
|
|
break;
|
|
case Q2MZ_LOGOUT:
|
|
dl->color[0] = 1;dl->color[1] = 0; dl->color[2] = 0;
|
|
dl->die = cl.time + 1.0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0);
|
|
// CL_LogoutEffect (pl->current.origin, weapon);
|
|
break;
|
|
case Q2MZ_RESPAWN:
|
|
dl->color[0] = 1;dl->color[1] = 1; dl->color[2] = 0;
|
|
dl->die = cl.time + 1.0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0);
|
|
// CL_LogoutEffect (pl->current.origin, weapon);
|
|
break;
|
|
// RAFAEL
|
|
case Q2MZ_PHALANX:
|
|
dl->color[0] = 1;dl->color[1] = 0.5; dl->color[2] = 0.5;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/plasshot.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
// RAFAEL
|
|
case Q2MZ_IONRIPPER:
|
|
dl->color[0] = 1;dl->color[1] = 0.5; dl->color[2] = 0.5;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/rippfire.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
|
|
// ======================
|
|
// PGM
|
|
case Q2MZ_ETF_RIFLE:
|
|
dl->color[0] = 0.9;dl->color[1] = 0.7;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/nail1.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_SHOTGUN2:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/shotg2.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_HEATBEAM:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
dl->die = cl.time + 100;
|
|
// Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/bfg__l1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_BLASTER2:
|
|
dl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 0;
|
|
// FIXME - different sound for blaster2 ??
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_TRACKER:
|
|
// negative flashes handled the same in gl/soft until CL_AddDLights
|
|
dl->color[0] = -1;dl->color[1] = -1;dl->color[2] = -1;
|
|
Q2S_StartSound (NULL, i, CHAN_WEAPON, S_PrecacheSound("weapons/disint2.wav"), volume, ATTN_NORM, 0);
|
|
break;
|
|
case Q2MZ_NUKE1:
|
|
dl->color[0] = 1;dl->color[1] = 0;dl->color[2] = 0;
|
|
dl->die = cl.time + 100;
|
|
break;
|
|
case Q2MZ_NUKE2:
|
|
dl->color[0] = 1;dl->color[1] = 1;dl->color[2] = 0;
|
|
dl->die = cl.time + 100;
|
|
break;
|
|
case Q2MZ_NUKE4:
|
|
dl->color[0] = 0;dl->color[1] = 0;dl->color[2] = 1;
|
|
dl->die = cl.time + 100;
|
|
break;
|
|
case Q2MZ_NUKE8:
|
|
dl->color[0] = 0;dl->color[1] = 1;dl->color[2] = 1;
|
|
dl->die = cl.time + 100;
|
|
break;
|
|
// PGM
|
|
// ======================
|
|
}
|
|
}
|
|
|
|
void CLQ2_ParseMuzzleFlash2 (void)
|
|
{
|
|
int ent;
|
|
int flash_number;
|
|
|
|
ent = (unsigned short)(short)MSG_ReadShort ();
|
|
if (ent < 1 || ent >= Q2MAX_EDICTS)
|
|
Host_EndGame ("CL_ParseMuzzleFlash2: bad entity");
|
|
|
|
flash_number = MSG_ReadByte ();
|
|
|
|
CLQ2_RunMuzzleFlash2(ent, flash_number);
|
|
}
|
|
|
|
void CLQ2_ParseInventory (int seat)
|
|
{
|
|
unsigned int i;
|
|
for (i=0 ; i<Q2MAX_ITEMS ; i++)
|
|
cl.inventory[seat][i] = MSG_ReadShort ();
|
|
}
|
|
|
|
/*
|
|
=========================================================================
|
|
|
|
FRAME PARSING
|
|
|
|
=========================================================================
|
|
*/
|
|
|
|
/*
|
|
=================
|
|
CL_ParseEntityBits
|
|
|
|
Returns the entity number and the header bits
|
|
=================
|
|
*/
|
|
//static int bitcounts[32]; /// just for protocol profiling
|
|
static int CLQ2_ParseEntityBits (unsigned int *bits)
|
|
{
|
|
unsigned b, total;
|
|
// int i;
|
|
int number;
|
|
|
|
total = MSG_ReadByte ();
|
|
if (total & Q2U_MOREBITS1)
|
|
{
|
|
b = MSG_ReadByte ();
|
|
total |= b<<8;
|
|
}
|
|
if (total & Q2U_MOREBITS2)
|
|
{
|
|
b = MSG_ReadByte ();
|
|
total |= b<<16;
|
|
}
|
|
if (total & Q2U_MOREBITS3)
|
|
{
|
|
b = MSG_ReadByte ();
|
|
total |= b<<24;
|
|
}
|
|
|
|
// count the bits for net profiling
|
|
/* for (i=0 ; i<32 ; i++)
|
|
if (total&(1<<i))
|
|
bitcounts[i]++;
|
|
*/
|
|
if (total & Q2U_NUMBER16)
|
|
number = (unsigned short)MSG_ReadShort ();
|
|
else
|
|
number = MSG_ReadByte ();
|
|
|
|
*bits = total;
|
|
|
|
return number;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_ParseDelta
|
|
|
|
Can go from either a baseline or a previous packet_entity
|
|
==================
|
|
*/
|
|
static void CLQ2_ParseDelta (entity_state_t *from, entity_state_t *to, int number, int bits)
|
|
{
|
|
// set everything to the state we are delta'ing from
|
|
*to = *from;
|
|
|
|
VectorCopy (from->origin, to->u.q2.old_origin);
|
|
to->number = number;
|
|
|
|
if (bits & Q2U_MODEL)
|
|
{
|
|
if (bits & Q2UX_INDEX16)
|
|
to->modelindex = MSG_ReadShort();
|
|
else
|
|
to->modelindex = MSG_ReadByte ();
|
|
}
|
|
if (bits & Q2U_MODEL2)
|
|
{
|
|
if (bits & Q2UX_INDEX16)
|
|
to->modelindex2 = MSG_ReadShort();
|
|
else
|
|
to->modelindex2 = MSG_ReadByte ();
|
|
}
|
|
if (bits & Q2U_MODEL3)
|
|
{
|
|
if (bits & Q2UX_INDEX16)
|
|
to->u.q2.modelindex3 = MSG_ReadShort();
|
|
else
|
|
to->u.q2.modelindex3 = MSG_ReadByte ();
|
|
}
|
|
if (bits & Q2U_MODEL4)
|
|
{
|
|
if (bits & Q2UX_INDEX16)
|
|
to->u.q2.modelindex4 = MSG_ReadShort();
|
|
else
|
|
to->u.q2.modelindex4 = MSG_ReadByte ();
|
|
}
|
|
|
|
if (bits & Q2U_FRAME8)
|
|
to->frame = MSG_ReadByte ();
|
|
if (bits & Q2U_FRAME16)
|
|
to->frame = MSG_ReadUInt16();
|
|
|
|
if ((bits & Q2U_SKIN8) && (bits & Q2U_SKIN16)) //used for laser colors
|
|
to->skinnum = MSG_ReadLong();
|
|
else if (bits & Q2U_SKIN8)
|
|
to->skinnum = MSG_ReadByte();
|
|
else if (bits & Q2U_SKIN16)
|
|
to->skinnum = MSG_ReadUInt16();
|
|
|
|
if ( (bits & (Q2U_EFFECTS8|Q2U_EFFECTS16)) == (Q2U_EFFECTS8|Q2U_EFFECTS16) )
|
|
to->effects = MSG_ReadLong();
|
|
else if (bits & Q2U_EFFECTS8)
|
|
to->effects = MSG_ReadByte();
|
|
else if (bits & Q2U_EFFECTS16)
|
|
to->effects = MSG_ReadUInt16();
|
|
|
|
if ( (bits & (Q2U_RENDERFX8|Q2U_RENDERFX16)) == (Q2U_RENDERFX8|Q2U_RENDERFX16) )
|
|
to->u.q2.renderfx = MSG_ReadLong() & 0x0007ffff; //only the standard ones actually supported by vanilla q2.
|
|
else if (bits & Q2U_RENDERFX8)
|
|
to->u.q2.renderfx = MSG_ReadByte();
|
|
else if (bits & Q2U_RENDERFX16)
|
|
to->u.q2.renderfx = MSG_ReadUInt16();
|
|
|
|
if (bits & Q2U_ORIGIN1)
|
|
to->origin[0] = MSG_ReadCoord ();
|
|
if (bits & Q2U_ORIGIN2)
|
|
to->origin[1] = MSG_ReadCoord ();
|
|
if (bits & Q2U_ORIGIN3)
|
|
to->origin[2] = MSG_ReadCoord ();
|
|
|
|
if ((bits & Q2UX_ANGLE16) && (net_message.prim.flags & NPQ2_ANG16))
|
|
{
|
|
if (bits & Q2U_ANGLE1)
|
|
to->angles[0] = MSG_ReadAngle16();
|
|
if (bits & Q2U_ANGLE2)
|
|
to->angles[1] = MSG_ReadAngle16();
|
|
if (bits & Q2U_ANGLE3)
|
|
to->angles[2] = MSG_ReadAngle16();
|
|
}
|
|
else
|
|
{
|
|
if (bits & Q2U_ANGLE1)
|
|
to->angles[0] = MSG_ReadAngle();
|
|
if (bits & Q2U_ANGLE2)
|
|
to->angles[1] = MSG_ReadAngle();
|
|
if (bits & Q2U_ANGLE3)
|
|
to->angles[2] = MSG_ReadAngle();
|
|
}
|
|
|
|
if (bits & Q2U_OLDORIGIN)
|
|
MSG_ReadPos (to->u.q2.old_origin);
|
|
|
|
if (bits & Q2U_SOUND)
|
|
{
|
|
if (bits & Q2UX_INDEX16)
|
|
to->u.q2.sound = MSG_ReadUInt16();
|
|
else
|
|
to->u.q2.sound = MSG_ReadByte ();
|
|
}
|
|
|
|
if (bits & Q2U_EVENT)
|
|
to->u.q2.event = MSG_ReadByte ();
|
|
else
|
|
to->u.q2.event = 0;
|
|
|
|
if (bits & Q2U_SOLID)
|
|
{
|
|
if (net_message.prim.flags & NPQ2_SOLID32)
|
|
to->solidsize = MSG_ReadLong();
|
|
else
|
|
to->solidsize = MSG_ReadSize16 (&net_message);
|
|
}
|
|
}
|
|
|
|
void CLQ2_ClearParticleState(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX_Q2EDICTS; i++)
|
|
{
|
|
P_DelinkTrailstate(&cl_entities[i].trailstate);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_DeltaEntity
|
|
|
|
Parses deltas from the given base and adds the resulting entity
|
|
to the current frame
|
|
==================
|
|
*/
|
|
static void CLQ2_DeltaEntity (q2frame_t *frame, int newnum, entity_state_t *old, int bits)
|
|
{
|
|
q2centity_t *ent;
|
|
entity_state_t *state;
|
|
|
|
ent = &cl_entities[newnum];
|
|
|
|
state = &clq2_parse_entities[cl.parse_entities & (MAX_PARSE_ENTITIES-1)];
|
|
cl.parse_entities++;
|
|
frame->num_entities++;
|
|
|
|
CLQ2_ParseDelta (old, state, newnum, bits);
|
|
|
|
// some data changes will force no lerping
|
|
if (state->modelindex != ent->current.modelindex
|
|
|| state->modelindex2 != ent->current.modelindex2
|
|
|| state->u.q2.modelindex3 != ent->current.u.q2.modelindex3
|
|
|| state->u.q2.modelindex4 != ent->current.u.q2.modelindex4
|
|
|| fabs(state->origin[0] - ent->current.origin[0]) > 512
|
|
|| fabs(state->origin[1] - ent->current.origin[1]) > 512
|
|
|| fabs(state->origin[2] - ent->current.origin[2]) > 512
|
|
|| state->u.q2.event == Q2EV_PLAYER_TELEPORT
|
|
|| state->u.q2.event == Q2EV_OTHER_TELEPORT
|
|
)
|
|
{
|
|
ent->serverframe = -99;
|
|
}
|
|
|
|
if (ent->serverframe != cl.q2frame.serverframe - 1)
|
|
{ // wasn't in last update, so initialize some things
|
|
// clear trailstate
|
|
P_DelinkTrailstate(&ent->trailstate);
|
|
|
|
// duplicate the current state so lerping doesn't hurt anything
|
|
ent->prev = *state;
|
|
if (state->u.q2.event == Q2EV_OTHER_TELEPORT)
|
|
{
|
|
VectorCopy (state->origin, ent->prev.origin);
|
|
VectorCopy (state->origin, ent->lerp_origin);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (state->u.q2.old_origin, ent->prev.origin);
|
|
VectorCopy (state->u.q2.old_origin, ent->lerp_origin);
|
|
}
|
|
}
|
|
else
|
|
{ // shuffle the last state to previous
|
|
ent->prev = ent->current;
|
|
}
|
|
|
|
ent->serverframe = cl.q2frame.serverframe;
|
|
ent->current = *state;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_ParsePacketEntities
|
|
|
|
An svc_packetentities has just been parsed, deal with the
|
|
rest of the data stream.
|
|
==================
|
|
*/
|
|
static void CLQ2_ParsePacketEntities (q2frame_t *oldframe, q2frame_t *newframe)
|
|
{
|
|
unsigned int newnum;
|
|
unsigned int bits;
|
|
entity_state_t *oldstate=NULL;
|
|
unsigned int oldindex, oldnum;
|
|
|
|
cl.validsequence = cls.netchan.incoming_sequence;
|
|
cl.ackedmovesequence = cl.validsequence;
|
|
|
|
cl.outframes[cl.ackedmovesequence&UPDATE_MASK].latency = realtime - cl.outframes[cl.ackedmovesequence&UPDATE_MASK].senttime;
|
|
|
|
newframe->parse_entities = cl.parse_entities;
|
|
newframe->num_entities = 0;
|
|
|
|
// delta from the entities present in oldframe
|
|
oldindex = 0;
|
|
if (!oldframe)
|
|
oldnum = 99999;
|
|
else
|
|
{
|
|
if (oldindex >= oldframe->num_entities)
|
|
oldnum = 99999;
|
|
else
|
|
{
|
|
oldstate = &clq2_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
|
|
oldnum = oldstate->number;
|
|
}
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
newnum = CLQ2_ParseEntityBits (&bits);
|
|
if (newnum >= MAX_Q2EDICTS)
|
|
Host_EndGame ("CL_ParsePacketEntities: bad number:%i", newnum);
|
|
|
|
if (MSG_GetReadCount() > net_message.cursize)
|
|
Host_EndGame ("CL_ParsePacketEntities: end of message");
|
|
|
|
if (!newnum)
|
|
break;
|
|
|
|
while (oldnum < newnum)
|
|
{ // one or more entities from the old packet are unchanged
|
|
if (cl_shownet.ival == 3)
|
|
Con_Printf (" unchanged: %i\n", oldnum);
|
|
CLQ2_DeltaEntity (newframe, oldnum, oldstate, 0);
|
|
|
|
oldindex++;
|
|
|
|
if (!oldframe || oldindex >= oldframe->num_entities)
|
|
oldnum = 99999;
|
|
else
|
|
{
|
|
oldstate = &clq2_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
|
|
oldnum = oldstate->number;
|
|
}
|
|
}
|
|
|
|
if (bits & Q2U_REMOVE)
|
|
{ // the entity present in oldframe is not in the current frame
|
|
if (cl_shownet.ival == 3)
|
|
Con_Printf (" remove: %i\n", newnum);
|
|
if (oldnum != newnum)
|
|
Con_Printf ("U_REMOVE: oldnum != newnum\n");
|
|
|
|
oldindex++;
|
|
|
|
if (!oldframe || oldindex >= oldframe->num_entities)
|
|
oldnum = 99999;
|
|
else
|
|
{
|
|
oldstate = &clq2_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
|
|
oldnum = oldstate->number;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (oldnum == newnum)
|
|
{ // delta from previous state
|
|
if (cl_shownet.ival == 3)
|
|
Con_Printf (" delta: %i\n", newnum);
|
|
CLQ2_DeltaEntity (newframe, newnum, oldstate, bits);
|
|
|
|
oldindex++;
|
|
|
|
if (oldindex >= oldframe->num_entities)
|
|
oldnum = 99999;
|
|
else
|
|
{
|
|
oldstate = &clq2_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
|
|
oldnum = oldstate->number;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (oldnum > newnum)
|
|
{ // delta from baseline
|
|
if (cl_shownet.ival == 3)
|
|
Con_Printf (" baseline: %i\n", newnum);
|
|
CLQ2_DeltaEntity (newframe, newnum, &cl_entities[newnum].baseline, bits);
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
// any remaining entities in the old frame are copied over
|
|
while (oldnum != 99999)
|
|
{ // one or more entities from the old packet are unchanged
|
|
if (cl_shownet.ival == 3)
|
|
Con_Printf (" unchanged: %i\n", oldnum);
|
|
CLQ2_DeltaEntity (newframe, oldnum, oldstate, 0);
|
|
|
|
oldindex++;
|
|
|
|
if (oldindex >= oldframe->num_entities)
|
|
oldnum = 99999;
|
|
else
|
|
{
|
|
oldstate = &clq2_parse_entities[(oldframe->parse_entities+oldindex) & (MAX_PARSE_ENTITIES-1)];
|
|
oldnum = oldstate->number;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CLQ2_ParseBaseline (void)
|
|
{
|
|
entity_state_t *es;
|
|
int bits;
|
|
int newnum;
|
|
entity_state_t nullstate;
|
|
|
|
memset (&nullstate, 0, sizeof(nullstate));
|
|
|
|
newnum = CLQ2_ParseEntityBits (&bits);
|
|
es = &cl_entities[newnum].baseline;
|
|
CLQ2_ParseDelta (&nullstate, es, newnum, bits);
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
CL_ParsePlayerstate
|
|
===================
|
|
*/
|
|
void CLQ2_ParsePlayerstate (int seat, q2frame_t *oldframe, q2frame_t *newframe, int extflags)
|
|
{
|
|
int flags;
|
|
q2player_state_t *state;
|
|
int i;
|
|
int statbits;
|
|
|
|
state = &newframe->playerstate[seat];
|
|
|
|
// clear to old value before delta parsing
|
|
if (oldframe)
|
|
{
|
|
*state = oldframe->playerstate[seat];
|
|
newframe->clientnum[seat] = oldframe->clientnum[seat];
|
|
}
|
|
else
|
|
{
|
|
memset (state, 0, sizeof(*state));
|
|
newframe->clientnum[seat] = cl.playerview[seat].playernum;
|
|
}
|
|
|
|
flags = (unsigned short)MSG_ReadShort ();
|
|
if (flags & Q2PS_EXTRABITS)
|
|
flags |= MSG_ReadByte()<<16;
|
|
|
|
//
|
|
// parse the pmove_state_t
|
|
//
|
|
if (flags & Q2PS_M_TYPE)
|
|
state->pmove.pm_type = MSG_ReadByte ();
|
|
|
|
if (flags & Q2PS_M_ORIGIN)
|
|
{
|
|
state->pmove.origin[0] = MSG_ReadShort ();
|
|
state->pmove.origin[1] = MSG_ReadShort ();
|
|
if (extflags & Q2PSX_OLD)
|
|
state->pmove.origin[2] = MSG_ReadShort ();
|
|
}
|
|
if (extflags & Q2PSX_M_ORIGIN2)
|
|
state->pmove.origin[2] = MSG_ReadShort ();
|
|
|
|
if (flags & Q2PS_M_VELOCITY)
|
|
{
|
|
state->pmove.velocity[0] = MSG_ReadShort ();
|
|
state->pmove.velocity[1] = MSG_ReadShort ();
|
|
if (extflags & Q2PSX_OLD)
|
|
state->pmove.velocity[2] = MSG_ReadShort ();
|
|
}
|
|
if (extflags & Q2PSX_M_VELOCITY2)
|
|
state->pmove.velocity[2] = MSG_ReadShort ();
|
|
|
|
if (flags & Q2PS_M_TIME)
|
|
state->pmove.pm_time = MSG_ReadByte ();
|
|
|
|
if (flags & Q2PS_M_FLAGS)
|
|
state->pmove.pm_flags = MSG_ReadByte ();
|
|
|
|
if (flags & Q2PS_M_GRAVITY)
|
|
state->pmove.gravity = MSG_ReadShort ();
|
|
|
|
if (flags & Q2PS_M_DELTA_ANGLES)
|
|
{
|
|
state->pmove.delta_angles[0] = MSG_ReadShort ();
|
|
state->pmove.delta_angles[1] = MSG_ReadShort ();
|
|
state->pmove.delta_angles[2] = MSG_ReadShort ();
|
|
}
|
|
|
|
// if (cl.attractloop)
|
|
// state->pmove.pm_type = Q2PM_FREEZE; // demo playback
|
|
|
|
//
|
|
// parse the rest of the player_state_t
|
|
//
|
|
if (flags & Q2PS_VIEWOFFSET)
|
|
{
|
|
state->viewoffset[0] = MSG_ReadChar () * 0.25;
|
|
state->viewoffset[1] = MSG_ReadChar () * 0.25;
|
|
state->viewoffset[2] = MSG_ReadChar () * 0.25;
|
|
}
|
|
|
|
if (flags & Q2PS_VIEWANGLES)
|
|
{
|
|
state->viewangles[0] = MSG_ReadAngle16 ();
|
|
state->viewangles[1] = MSG_ReadAngle16 ();
|
|
if (extflags & Q2PSX_OLD)
|
|
state->viewangles[2] = MSG_ReadAngle16 ();
|
|
}
|
|
if (extflags & Q2PSX_VIEWANGLE2)
|
|
state->viewangles[2] = MSG_ReadAngle16 ();
|
|
|
|
if (flags & Q2PS_KICKANGLES)
|
|
{
|
|
state->kick_angles[0] = MSG_ReadChar () * 0.25;
|
|
state->kick_angles[1] = MSG_ReadChar () * 0.25;
|
|
state->kick_angles[2] = MSG_ReadChar () * 0.25;
|
|
}
|
|
|
|
if (flags & Q2PS_WEAPONINDEX)
|
|
{
|
|
if (flags & Q2PS_INDEX16)
|
|
state->gunindex = MSG_ReadShort ();
|
|
else
|
|
state->gunindex = MSG_ReadByte ();
|
|
}
|
|
|
|
if (flags & Q2PS_WEAPONFRAME)
|
|
{
|
|
if (flags & Q2PS_INDEX16)
|
|
state->gunframe = MSG_ReadShort ();
|
|
else
|
|
state->gunframe = MSG_ReadByte ();
|
|
if (extflags & Q2PSX_OLD)
|
|
{
|
|
state->gunoffset[0] = MSG_ReadChar ()*0.25;
|
|
state->gunoffset[1] = MSG_ReadChar ()*0.25;
|
|
state->gunoffset[2] = MSG_ReadChar ()*0.25;
|
|
state->gunangles[0] = MSG_ReadChar ()*0.25;
|
|
state->gunangles[1] = MSG_ReadChar ()*0.25;
|
|
state->gunangles[2] = MSG_ReadChar ()*0.25;
|
|
}
|
|
}
|
|
if (extflags & Q2PSX_GUNOFFSET)
|
|
{
|
|
state->gunoffset[0] = MSG_ReadChar ()*0.25;
|
|
state->gunoffset[1] = MSG_ReadChar ()*0.25;
|
|
state->gunoffset[2] = MSG_ReadChar ()*0.25;
|
|
}
|
|
if (extflags & Q2PSX_GUNANGLES)
|
|
{
|
|
state->gunangles[0] = MSG_ReadChar ()*0.25;
|
|
state->gunangles[1] = MSG_ReadChar ()*0.25;
|
|
state->gunangles[2] = MSG_ReadChar ()*0.25;
|
|
}
|
|
|
|
if (flags & Q2PS_BLEND)
|
|
{
|
|
state->blend[0] = MSG_ReadByte ()/255.0;
|
|
state->blend[1] = MSG_ReadByte ()/255.0;
|
|
state->blend[2] = MSG_ReadByte ()/255.0;
|
|
state->blend[3] = MSG_ReadByte ()/255.0;
|
|
}
|
|
|
|
if (flags & Q2PS_FOV)
|
|
state->fov = MSG_ReadByte ();
|
|
|
|
if (flags & Q2PS_RDFLAGS)
|
|
state->rdflags = MSG_ReadByte ();
|
|
|
|
// parse stats
|
|
if (extflags & (Q2PSX_OLD|Q2PSX_STATS))
|
|
statbits = MSG_ReadLong ();
|
|
else
|
|
statbits = 0;
|
|
if (statbits)
|
|
{
|
|
for (i=0 ; i<Q2MAX_STATS ; i++)
|
|
if (statbits & (1<<i) )
|
|
state->stats[i] = MSG_ReadShort();
|
|
}
|
|
|
|
if ((extflags & Q2PSX_CLIENTNUM) || (flags & Q2PS_CLIENTNUM))
|
|
newframe->clientnum[seat] = MSG_ReadByte();
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
CL_FireEntityEvents
|
|
|
|
==================
|
|
*/
|
|
void CLQ2_FireEntityEvents (q2frame_t *frame)
|
|
{
|
|
entity_state_t *s1;
|
|
int pnum, num;
|
|
|
|
for (pnum = 0 ; pnum<frame->num_entities ; pnum++)
|
|
{
|
|
num = (frame->parse_entities + pnum)&(MAX_PARSE_ENTITIES-1);
|
|
s1 = &clq2_parse_entities[num];
|
|
if (s1->u.q2.event)
|
|
CLQ2_EntityEvent (s1);
|
|
|
|
// EF_TELEPORTER acts like an event, but is not cleared each frame
|
|
if (s1->effects & Q2EF_TELEPORTER)
|
|
CLQ2_TeleporterParticles (s1);
|
|
}
|
|
}
|
|
|
|
void CLR1Q2_ParsePlayerUpdate(void)
|
|
{
|
|
unsigned int framenum = MSG_ReadLong();
|
|
q2frame_t *frame = &cl.q2frames[framenum & Q2UPDATE_MASK];
|
|
int seat;
|
|
if (frame->serverframe != framenum)
|
|
Con_DPrintf("svcr1q2_playerupdate: stale frame\n");
|
|
else if (!frame->valid)
|
|
Con_DPrintf("svcr1q2_playerupdate: invalid frame\n"); //excrement happens.
|
|
else
|
|
{
|
|
int pnum;
|
|
vec3_t neworg;
|
|
entity_state_t *st;
|
|
for (pnum = 0; pnum < frame->num_entities; pnum++)
|
|
{
|
|
st = &clq2_parse_entities[(frame->parse_entities+pnum) & (MAX_PARSE_ENTITIES-1)];
|
|
|
|
//I don't like how r1q2 does its maxclients, so I'm just going to go on message size instead
|
|
if (MSG_GetReadCount() == net_message.cursize)
|
|
break;
|
|
|
|
//the local client(s) is not included, thanks to prediction covering that.
|
|
for (seat = 0; seat < cl.splitclients; seat++)
|
|
{
|
|
if (st->number == cl.playerview[0].playernum+1)
|
|
break;
|
|
}
|
|
if (seat != cl.splitclients)
|
|
continue;
|
|
|
|
if (st->number != 1)
|
|
continue;
|
|
|
|
//FIXME: handle this, with lerping and stuff.
|
|
MSG_ReadPos(neworg);
|
|
}
|
|
|
|
//just for sanity's sake
|
|
if (MSG_GetReadCount() != net_message.cursize)
|
|
msg_badread = true;
|
|
}
|
|
//this should be the only/last thing in these packets, because if it isn't then we're screwed when a packet got lost
|
|
net_message.currentbit = net_message.cursize<<3;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CL_ParseFrame
|
|
================
|
|
*/
|
|
void CLQ2_ParseFrame (int extrabits)
|
|
{
|
|
int cmd;
|
|
int len;
|
|
q2frame_t *old;
|
|
int i,j, chokecount;
|
|
|
|
memset (&cl.q2frame, 0, sizeof(cl.q2frame));
|
|
|
|
#if 0
|
|
CLQ2_ClearProjectiles(); // clear projectiles for new frame
|
|
#endif
|
|
|
|
if (cls.protocol_q2 == PROTOCOL_VERSION_R1Q2 || cls.protocol_q2 == PROTOCOL_VERSION_Q2PRO)
|
|
{
|
|
unsigned int bits = MSG_ReadLong();
|
|
cl.q2frame.serverframe = bits & 0x07ffffff;
|
|
i = bits >> 27;
|
|
if (i == 31)
|
|
cl.q2frame.deltaframe = -1;
|
|
else
|
|
cl.q2frame.deltaframe = cl.q2frame.serverframe - i;
|
|
bits = MSG_ReadByte();
|
|
chokecount = bits & 0xf;
|
|
extrabits = (extrabits<<4) | (bits>>4);
|
|
}
|
|
else
|
|
{
|
|
cl.q2frame.serverframe = MSG_ReadLong ();
|
|
cl.q2frame.deltaframe = MSG_ReadLong ();
|
|
if (cls.protocol_q2 > 26)
|
|
chokecount = MSG_ReadByte ();
|
|
else
|
|
chokecount = 0;
|
|
|
|
extrabits = Q2PSX_OLD;
|
|
}
|
|
|
|
cl.q2frame.servertime = cl.q2frame.serverframe*(1000/cl.q2svnetrate);
|
|
|
|
cl.oldgametime = cl.gametime;
|
|
cl.oldgametimemark = cl.gametimemark;
|
|
cl.gametime = cl.q2frame.servertime/1000.f;
|
|
cl.gametimemark = realtime;
|
|
|
|
for (j=0 ; j<chokecount ; j++)
|
|
cl.outframes[ (cls.netchan.incoming_acknowledged-1-j)&UPDATE_MASK ].latency = -2;
|
|
|
|
if (cl_shownet.value == 3)
|
|
Con_Printf (" frame:%i delta:%i\n", cl.q2frame.serverframe, cl.q2frame.deltaframe);
|
|
|
|
// If the frame is delta compressed from data that we
|
|
// no longer have available, we must suck up the rest of
|
|
// the frame, but not use it, then ask for a non-compressed
|
|
// message
|
|
if (cl.q2frame.deltaframe <= 0)
|
|
{
|
|
cl.q2frame.valid = true; // uncompressed frame
|
|
old = NULL;
|
|
cls.demohadkeyframe = true; //yay! all is right with the world!
|
|
}
|
|
else
|
|
{
|
|
old = &cl.q2frames[cl.q2frame.deltaframe & Q2UPDATE_MASK];
|
|
if (!old->valid)
|
|
{ // should never happen
|
|
Con_Printf ("Delta from invalid frame (not supposed to happen!).\n");
|
|
cl.q2frame.valid = true; // uncompressed frame
|
|
old = NULL;
|
|
}
|
|
else if (old->serverframe != cl.q2frame.deltaframe)
|
|
{ // The frame that the server did the delta from
|
|
// is too old, so we can't reconstruct it properly.
|
|
Con_Printf ("Delta frame too old.\n");
|
|
}
|
|
else if (cl.parse_entities - old->parse_entities > MAX_PARSE_ENTITIES-128)
|
|
{
|
|
Con_Printf ("Delta parse_entities too old.\n");
|
|
}
|
|
else
|
|
cl.q2frame.valid = true; // valid delta parse
|
|
}
|
|
|
|
// clamp time
|
|
if (cl.time > cl.q2frame.servertime/1000.0)
|
|
cl.time = cl.q2frame.servertime/1000.0;
|
|
else if (cl.time < (cl.q2frame.servertime - 100)/1000.0)
|
|
cl.time = (cl.q2frame.servertime - 100)/1000.0;
|
|
|
|
// read areabits
|
|
len = MSG_ReadByte ();
|
|
MSG_ReadData (&cl.q2frame.areabits, len);
|
|
|
|
// normally playerstate then packet entities
|
|
//in splitscreen we may have multiple player states, one per player.
|
|
if (cls.protocol_q2 != PROTOCOL_VERSION_R1Q2 && cls.protocol_q2 != PROTOCOL_VERSION_Q2PRO)
|
|
{
|
|
for (cl.splitclients = 0; ; )
|
|
{
|
|
cmd = MSG_ReadByte ();
|
|
// SHOWNET(svc_strings[cmd]);
|
|
if (cmd == svcq2_playerinfo && cl.splitclients < MAX_SPLITS)
|
|
CLQ2_ParsePlayerstate (cl.splitclients++, old, &cl.q2frame, extrabits);
|
|
else
|
|
break;
|
|
}
|
|
if (!cl.splitclients)
|
|
Host_EndGame ("CL_ParseFrame: no playerinfo");
|
|
if (cmd != svcq2_packetentities)
|
|
Host_EndGame ("CL_ParseFrame: not packetentities");
|
|
}
|
|
else
|
|
{
|
|
cl.splitclients = 1;
|
|
CLQ2_ParsePlayerstate (0, old, &cl.q2frame, extrabits);
|
|
}
|
|
CLQ2_ParsePacketEntities (old, &cl.q2frame);
|
|
|
|
for (cmd = 0; cmd < MAX_SPLITS; cmd++)
|
|
cl.playerview[cmd].viewentity = cl.q2frame.clientnum[cmd]+1;
|
|
|
|
// save the frame off in the backup array for later delta comparisons
|
|
cl.q2frames[cl.q2frame.serverframe & Q2UPDATE_MASK] = cl.q2frame;
|
|
|
|
cl.parsecount = cl.q2frame.serverframe;
|
|
if (cl.q2frame.valid)
|
|
{
|
|
// getting a valid frame message ends the connection process
|
|
if (cls.state != ca_active)
|
|
{
|
|
CL_MakeActive("Quake2");
|
|
|
|
// cl.force_refdef = true;
|
|
// cl.predicted_origin[0] = cl.q2frame.playerstate[0].pmove.origin[0]*0.125;
|
|
// cl.predicted_origin[1] = cl.q2frame.playerstate[0].pmove.origin[1]*0.125;
|
|
// cl.predicted_origin[2] = cl.q2frame.playerstate[0].pmove.origin[2]*0.125;
|
|
// VectorCopy (cl.q2frame.playerstate[0].viewangles, cl.predicted_angles);
|
|
// if (cls.disable_servercount != cl.servercount
|
|
// && cl.refresh_prepped)
|
|
SCR_EndLoadingPlaque (); // get rid of loading plaque
|
|
}
|
|
// cl.sound_prepped = true; // can start mixing ambient sounds
|
|
|
|
// fire entity events
|
|
CLQ2_FireEntityEvents (&cl.q2frame);
|
|
#ifdef Q2BSPS
|
|
CLQ2_CheckPredictionError ();
|
|
#endif
|
|
|
|
cl.validsequence = cl.q2frame.serverframe;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==========================================================================
|
|
|
|
INTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS
|
|
|
|
==========================================================================
|
|
*/
|
|
/*
|
|
struct model_s *S_RegisterSexedModel (entity_state_t *ent, char *base)
|
|
{
|
|
int n;
|
|
char *p;
|
|
struct model_s *mdl;
|
|
char model[MAX_QPATH];
|
|
char buffer[MAX_QPATH];
|
|
|
|
// determine what model the client is using
|
|
model[0] = 0;
|
|
n = CS_PLAYERSKINS + ent->number - 1;
|
|
if (cl.configstrings[n][0])
|
|
{
|
|
p = strchr(cl.configstrings[n], '\\');
|
|
if (p)
|
|
{
|
|
p += 1;
|
|
strcpy(model, p);
|
|
p = strchr(model, '/');
|
|
if (p)
|
|
*p = 0;
|
|
}
|
|
}
|
|
// if we can't figure it out, they're male
|
|
if (!model[0])
|
|
strcpy(model, "male");
|
|
|
|
Com_sprintf (buffer, sizeof(buffer), "players/%s/%s", model, base+1);
|
|
mdl = re.RegisterModel(buffer);
|
|
if (!mdl) {
|
|
// not found, try default weapon model
|
|
Com_sprintf (buffer, sizeof(buffer), "players/%s/weapon.md2", model);
|
|
mdl = re.RegisterModel(buffer);
|
|
if (!mdl) {
|
|
// no, revert to the male model
|
|
Com_sprintf (buffer, sizeof(buffer), "players/%s/%s", "male", base+1);
|
|
mdl = re.RegisterModel(buffer);
|
|
if (!mdl) {
|
|
// last try, default male weapon.md2
|
|
Com_sprintf (buffer, sizeof(buffer), "players/male/weapon.md2");
|
|
mdl = re.RegisterModel(buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
return mdl;
|
|
}
|
|
|
|
*/
|
|
|
|
//returns a list of all the ents currently trying to play a sound.
|
|
unsigned int CLQ2_GatherSounds(vec3_t *positions, unsigned int *entnums, sfx_t **sounds, unsigned int max)
|
|
{
|
|
entity_state_t *s1;
|
|
sfx_t *sfx;
|
|
unsigned int pnum;
|
|
unsigned int count = 0;
|
|
q2frame_t *frame = &cl.q2frame;
|
|
for (pnum = 0 ; pnum<frame->num_entities ; pnum++)
|
|
{
|
|
s1 = &clq2_parse_entities[(frame->parse_entities+pnum)&(MAX_PARSE_ENTITIES-1)];
|
|
if (s1->u.q2.sound > 0 && s1->u.q2.sound < MAX_PRECACHE_SOUNDS)
|
|
{
|
|
sfx = cl.sound_precache[s1->u.q2.sound];
|
|
if (sfx)
|
|
{
|
|
if (count == max)
|
|
{
|
|
Con_DPrintf("Exceeded limit of %d looped sounds\n", max);
|
|
break;
|
|
}
|
|
//fixme: sexed sounds
|
|
entnums[count] = s1->number;
|
|
VectorCopy(s1->origin, positions[count]);
|
|
sounds[count] = sfx;
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_AddPacketEntities
|
|
|
|
===============
|
|
*/
|
|
static void CLQ2_AddPacketEntities (q2frame_t *frame)
|
|
{
|
|
entity_t ent;
|
|
entity_state_t *s1;
|
|
float autorotate;
|
|
int i;
|
|
int pnum;
|
|
q2centity_t *cent;
|
|
int autoanim;
|
|
// q2clientinfo_t *ci;
|
|
player_info_t *player;
|
|
unsigned int effects, renderfx;
|
|
float back, fwds;
|
|
|
|
float timestep = cl.gametime-cl.oldgametime;
|
|
|
|
// bonus items rotate at a fixed rate
|
|
autorotate = anglemod(cl.time*100);
|
|
|
|
// brush models can auto animate their frames
|
|
autoanim = 2*cl.time;
|
|
|
|
memset (&ent, 0, sizeof(ent));
|
|
|
|
for (pnum = 0 ; pnum<frame->num_entities ; pnum++)
|
|
{
|
|
s1 = &clq2_parse_entities[(frame->parse_entities+pnum)&(MAX_PARSE_ENTITIES-1)];
|
|
|
|
cent = &cl_entities[s1->number];
|
|
|
|
effects = s1->effects;
|
|
renderfx = s1->u.q2.renderfx;
|
|
|
|
ent.rtype = RT_MODEL;
|
|
ent.keynum = s1->number;
|
|
|
|
ent.scale = 1;
|
|
ent.shaderRGBAf[0] = 1;
|
|
ent.shaderRGBAf[1] = 1;
|
|
ent.shaderRGBAf[2] = 1;
|
|
ent.shaderRGBAf[3] = 1;
|
|
ent.glowmod[0] = 1;
|
|
ent.glowmod[1] = 1;
|
|
ent.glowmod[2] = 1;
|
|
ent.fatness = 0;
|
|
ent.topcolour = 1;
|
|
ent.bottomcolour = 1;
|
|
#ifdef HEXEN2
|
|
ent.h2playerclass = 0;
|
|
#endif
|
|
ent.playerindex = -1;
|
|
ent.customskin = 0;
|
|
|
|
// set frame
|
|
if (effects & Q2EF_ANIM01)
|
|
ent.framestate.g[FS_REG].frame[0] = autoanim & 1;
|
|
else if (effects & Q2EF_ANIM23)
|
|
ent.framestate.g[FS_REG].frame[0] = 2 + (autoanim & 1);
|
|
else if (effects & Q2EF_ANIM_ALL)
|
|
ent.framestate.g[FS_REG].frame[0] = autoanim;
|
|
else if (effects & Q2EF_ANIM_ALLFAST)
|
|
ent.framestate.g[FS_REG].frame[0] = cl.time / 100;
|
|
else
|
|
ent.framestate.g[FS_REG].frame[0] = s1->frame;
|
|
|
|
// quad and pent can do different things on client
|
|
if (effects & Q2EF_PENT)
|
|
{
|
|
effects &= ~Q2EF_PENT;
|
|
effects |= Q2EF_COLOR_SHELL;
|
|
renderfx |= Q2RF_SHELL_RED;
|
|
}
|
|
|
|
if (effects & Q2EF_QUAD)
|
|
{
|
|
effects &= ~Q2EF_QUAD;
|
|
effects |= Q2EF_COLOR_SHELL;
|
|
renderfx |= Q2RF_SHELL_BLUE;
|
|
}
|
|
//======
|
|
// PMM
|
|
if (effects & Q2EF_DOUBLE)
|
|
{
|
|
effects &= ~Q2EF_DOUBLE;
|
|
effects |= Q2EF_COLOR_SHELL;
|
|
renderfx |= Q2RF_SHELL_DOUBLE;
|
|
}
|
|
|
|
if (effects & Q2EF_HALF_DAMAGE)
|
|
{
|
|
effects &= ~Q2EF_HALF_DAMAGE;
|
|
effects |= Q2EF_COLOR_SHELL;
|
|
renderfx |= Q2RF_SHELL_HALF_DAM;
|
|
}
|
|
// pmm
|
|
//======
|
|
ent.framestate.g[FS_REG].frame[1] = cent->prev.frame;
|
|
ent.framestate.g[FS_REG].lerpweight[0] = 1-cl.lerpfrac;
|
|
ent.framestate.g[FS_REG].lerpweight[1] = cl.lerpfrac;
|
|
|
|
if (renderfx & (Q2RF_FRAMELERP|Q2RF_BEAM))
|
|
{ // step origin discretely, because the frames
|
|
// do the animation properly
|
|
VectorCopy (cent->current.origin, ent.origin);
|
|
VectorCopy (cent->current.u.q2.old_origin, ent.oldorigin);
|
|
}
|
|
else
|
|
{ // interpolate origin
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
ent.origin[i] = ent.oldorigin[i] = cent->prev.origin[i] + cl.lerpfrac *
|
|
(cent->current.origin[i] - cent->prev.origin[i]);
|
|
}
|
|
}
|
|
|
|
// create a new entity
|
|
|
|
// tweak the color of beams
|
|
if ( renderfx & Q2RF_BEAM )
|
|
{ // the four beam colors are encoded in 32 bits of skinnum (hack)
|
|
ent.skinnum = (s1->skinnum >> ((rand() % 4)*8)) & 0xff;
|
|
ent.shaderRGBAf[0] = ((d_8to24srgbtable[ent.skinnum & 0xFF] >> 0) & 0xFF)/255.0;
|
|
ent.shaderRGBAf[1] = ((d_8to24srgbtable[ent.skinnum & 0xFF] >> 8) & 0xFF)/255.0;
|
|
ent.shaderRGBAf[2] = ((d_8to24srgbtable[ent.skinnum & 0xFF] >> 16) & 0xFF)/255.0;
|
|
ent.shaderRGBAf[3] = 0.30;
|
|
ent.model = NULL;
|
|
ent.framestate.g[FS_REG].lerpweight[0] = 0;
|
|
ent.framestate.g[FS_REG].lerpweight[1] = 1;
|
|
ent.rtype = RT_BEAM;
|
|
}
|
|
else
|
|
{
|
|
// set skin
|
|
if (s1->modelindex == 255)
|
|
{ // use custom player skin
|
|
ent.skinnum = 0;
|
|
|
|
player = &cl.players[(s1->skinnum&0xff)%cl.allocated_client_slots];
|
|
ent.model = player->model;
|
|
if (!ent.model || ent.model->loadstate != MLS_LOADED) //we need to do better than this
|
|
{
|
|
ent.model = Mod_ForName("players/male/tris.md2", MLV_SILENT);
|
|
ent.customskin = Mod_RegisterSkinFile("players/male/grunt.skin");
|
|
if (!ent.customskin)
|
|
ent.customskin = Mod_ReadSkinFile("players/male/grunt.skin", "replace \"\" \"players/male/grunt.pcx\"");
|
|
}
|
|
else
|
|
ent.customskin = player->skinid;
|
|
ent.playerindex = (s1->skinnum&0xff)%cl.allocated_client_slots;
|
|
/* ci = &cl.clientinfo[s1->skinnum & 0xff];
|
|
// ent.skin = ci->skin;
|
|
ent.model = ci->model;
|
|
if (!ent.skin || !ent.model)
|
|
{
|
|
ent.skin = cl.baseclientinfo.skin;
|
|
ent.model = cl.baseclientinfo.model;
|
|
}
|
|
|
|
//============
|
|
//PGM
|
|
if (renderfx & Q2RF_USE_DISGUISE)
|
|
{
|
|
if(!strncmp((char *)ent.skin, "players/male", 12))
|
|
{
|
|
ent.skin = re.RegisterSkin ("players/male/disguise.pcx");
|
|
ent.model = re.RegisterModel ("players/male/tris.md2");
|
|
}
|
|
else if(!strncmp((char *)ent.skin, "players/female", 14))
|
|
{
|
|
ent.skin = re.RegisterSkin ("players/female/disguise.pcx");
|
|
ent.model = re.RegisterModel ("players/female/tris.md2");
|
|
}
|
|
else if(!strncmp((char *)ent.skin, "players/cyborg", 14))
|
|
{
|
|
ent.skin = re.RegisterSkin ("players/cyborg/disguise.pcx");
|
|
ent.model = re.RegisterModel ("players/cyborg/tris.md2");
|
|
}
|
|
}*/
|
|
//PGM
|
|
//============
|
|
}
|
|
else
|
|
{
|
|
ent.skinnum = s1->skinnum;
|
|
// ent.skin = NULL;
|
|
ent.model = cl.model_precache[s1->modelindex];
|
|
}
|
|
}
|
|
|
|
// only used for black hole model right now, FIXME: do better
|
|
if (renderfx == RF_TRANSLUCENT)
|
|
ent.shaderRGBAf[3] = 0.70;
|
|
|
|
// render effects (fullbright, translucent, etc)
|
|
if ((effects & Q2EF_COLOR_SHELL))
|
|
ent.flags = 0; // renderfx go on color shell entity
|
|
else
|
|
ent.flags = renderfx;
|
|
|
|
// calculate angles
|
|
if (effects & Q2EF_ROTATE)
|
|
{ // some bonus items auto-rotate
|
|
ent.angles[0] = 0;
|
|
ent.angles[1] = autorotate;
|
|
ent.angles[2] = 0;
|
|
}
|
|
// RAFAEL
|
|
else if (effects & Q2EF_SPINNINGLIGHTS)
|
|
{
|
|
ent.angles[0] = 0;
|
|
ent.angles[1] = anglemod(cl.time/2) + s1->angles[1];
|
|
ent.angles[2] = 180;
|
|
{
|
|
vec3_t forward;
|
|
vec3_t start;
|
|
|
|
AngleVectors (ent.angles, forward, NULL, NULL);
|
|
VectorMA (ent.origin, 64, forward, start);
|
|
V_AddLight (ent.keynum, start, 100, 0.2, 0, 0);
|
|
}
|
|
}
|
|
else
|
|
{ // interpolate angles
|
|
float a1, a2;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
a1 = cent->current.angles[i];
|
|
a2 = cent->prev.angles[i];
|
|
ent.angles[i] = LerpAngle (a2, a1, cl.lerpfrac);
|
|
}
|
|
}
|
|
|
|
ent.angles[0]*=r_meshpitch.value; //q2 has it fixed. consistency...
|
|
ent.angles[2]*=r_meshroll.value; //h2 doesn't. consistency...
|
|
|
|
if (s1->number == cl.playerview[0].playernum+1) //woo! this is us!
|
|
{
|
|
// VectorCopy(cl.predicted_origin, ent.origin);
|
|
// VectorCopy(cl.predicted_origin, ent.oldorigin);
|
|
ent.flags |= RF_EXTERNALMODEL; // only draw from mirrors
|
|
renderfx |= RF_EXTERNALMODEL;
|
|
|
|
if (effects & Q2EF_FLAG1)
|
|
V_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.05, 0.05);
|
|
else if (effects & Q2EF_FLAG2)
|
|
V_AddLight (ent.keynum, ent.origin, 225, 0.05, 0.05, 0.2);
|
|
else if (effects & Q2EF_TAGTRAIL) //PGM
|
|
V_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.2, 0.0); //PGM
|
|
else if (effects & Q2EF_TRACKERTRAIL) //PGM
|
|
V_AddLight (ent.keynum, ent.origin, 225, -0.2, -0.2, -0.2); //PGM
|
|
}
|
|
|
|
// if set to invisible, skip
|
|
if (!s1->modelindex)
|
|
continue;
|
|
|
|
if (effects & Q2EF_BFG)
|
|
{
|
|
ent.flags |= RF_TRANSLUCENT;
|
|
ent.shaderRGBAf[3] = 0.30;
|
|
}
|
|
|
|
// RAFAEL
|
|
if (effects & Q2EF_PLASMA)
|
|
{
|
|
ent.flags |= RF_TRANSLUCENT;
|
|
ent.shaderRGBAf[3] = 0.6;
|
|
}
|
|
|
|
if (effects & Q2EF_SPHERETRANS)
|
|
{
|
|
ent.flags |= RF_TRANSLUCENT;
|
|
// PMM - *sigh* yet more EF overloading
|
|
if (effects & Q2EF_TRACKERTRAIL)
|
|
ent.shaderRGBAf[3] = 0.6;
|
|
else
|
|
ent.shaderRGBAf[3] = 0.3;
|
|
}
|
|
//pmm
|
|
|
|
/*lerp the ent now*/
|
|
fwds = ent.framestate.g[FS_REG].lerpweight[1];
|
|
back = ent.framestate.g[FS_REG].lerpweight[0];
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
ent.origin[i] = ent.origin[i]*fwds + ent.oldorigin[i]*back;
|
|
}
|
|
ent.framestate.g[FS_REG].lerpweight[0] = fwds;
|
|
ent.framestate.g[FS_REG].lerpweight[1] = back;
|
|
|
|
if ((renderfx & Q2RF_IR_VISIBLE) && (r_refdef.flags & Q2RDF_IRGOGGLES))
|
|
{
|
|
//IR googles make ir visible ents visible in pure red.
|
|
ent.shaderRGBAf[0] = 1;
|
|
ent.shaderRGBAf[1] = 0;
|
|
ent.shaderRGBAf[2] = 0;
|
|
//bypasses world lighting
|
|
ent.light_known = true;
|
|
VectorSet(ent.light_avg, 1, 1, 1);
|
|
VectorSet(ent.light_range, 0, 0, 0);
|
|
//(yes, its a bit shit. not even a post-process thing)
|
|
}
|
|
|
|
// add to refresh list
|
|
V_AddEntity (&ent);
|
|
ent.light_known = false;
|
|
ent.customskin = 0;
|
|
|
|
|
|
// color shells generate a seperate entity for the main model
|
|
if (effects & Q2EF_COLOR_SHELL)
|
|
{
|
|
// PMM - at this point, all of the shells have been handled
|
|
// if we're in the rogue pack, set up the custom mixing, otherwise just
|
|
// keep going
|
|
|
|
// all of the solo colors are fine. we need to catch any of the combinations that look bad
|
|
// (double & half) and turn them into the appropriate color, and make double/quad something special
|
|
if (renderfx & Q2RF_SHELL_HALF_DAM)
|
|
{
|
|
|
|
{
|
|
// ditch the half damage shell if any of red, blue, or double are on
|
|
if (renderfx & (Q2RF_SHELL_RED|Q2RF_SHELL_BLUE|Q2RF_SHELL_DOUBLE))
|
|
renderfx &= ~Q2RF_SHELL_HALF_DAM;
|
|
}
|
|
}
|
|
|
|
if (renderfx & Q2RF_SHELL_DOUBLE)
|
|
{
|
|
|
|
{
|
|
// lose the yellow shell if we have a red, blue, or green shell
|
|
if (renderfx & (Q2RF_SHELL_RED|Q2RF_SHELL_BLUE|Q2RF_SHELL_GREEN))
|
|
renderfx &= ~Q2RF_SHELL_DOUBLE;
|
|
// if we have a red shell, turn it to purple by adding blue
|
|
if (renderfx & Q2RF_SHELL_RED)
|
|
renderfx |= Q2RF_SHELL_BLUE;
|
|
// if we have a blue shell (and not a red shell), turn it to cyan by adding green
|
|
else if (renderfx & Q2RF_SHELL_BLUE)
|
|
{
|
|
// go to green if it's on already, otherwise do cyan (flash green)
|
|
if (renderfx & Q2RF_SHELL_GREEN)
|
|
renderfx &= ~Q2RF_SHELL_BLUE;
|
|
else
|
|
renderfx |= Q2RF_SHELL_GREEN;
|
|
}
|
|
}
|
|
}
|
|
// pmm
|
|
ent.flags = renderfx;
|
|
ent.shaderRGBAf[3] = 0.20;
|
|
ent.shaderRGBAf[0] = (!!(renderfx & Q2RF_SHELL_RED));
|
|
ent.shaderRGBAf[1] = (!!(renderfx & Q2RF_SHELL_GREEN));
|
|
ent.shaderRGBAf[2] = (!!(renderfx & Q2RF_SHELL_BLUE));
|
|
ent.forcedshader = R_RegisterCustom(NULL, "q2/shell", SUF_NONE, Shader_DefaultSkinShell, NULL);
|
|
ent.fatness = 2;
|
|
V_AddEntity (&ent);
|
|
ent.fatness = 0;
|
|
}
|
|
ent.forcedshader = NULL;
|
|
|
|
// ent.skin = NULL; // never use a custom skin on others
|
|
ent.skinnum = 0;
|
|
ent.flags &= RF_EXTERNALMODEL;
|
|
ent.shaderRGBAf[3] = 1;
|
|
|
|
// duplicate for linked models
|
|
if (s1->modelindex2)
|
|
{
|
|
if (s1->modelindex2 == 255)
|
|
{ // custom weapon
|
|
char *modelname;
|
|
char *skin;
|
|
ent.model=NULL;
|
|
|
|
player = &cl.players[(s1->skinnum&0xff)%MAX_CLIENTS];
|
|
modelname = InfoBuf_ValueForKey(&player->userinfo, "skin");
|
|
if (!modelname[0])
|
|
modelname = "male";
|
|
skin = strchr(modelname, '/');
|
|
if (skin) *skin = '\0';
|
|
|
|
i = (s1->skinnum >> 8); // 0 is default weapon model
|
|
if (i < 0 || i >= cl.numq2visibleweapons)
|
|
i = 0; //0 is always valid
|
|
ent.model = Mod_ForName(va("players/%s/%s", modelname, cl.q2visibleweapons[i]), MLV_WARN);
|
|
if (ent.model->loadstate == MLS_FAILED && i)
|
|
{
|
|
i = 0;
|
|
ent.model = Mod_ForName(va("players/%s/%s", modelname, cl.q2visibleweapons[i]), MLV_WARN);
|
|
}
|
|
if (ent.model->loadstate == MLS_FAILED && strcmp(modelname, "male"))
|
|
ent.model = Mod_ForName(va("players/%s/%s", "male", cl.q2visibleweapons[i]), MLV_WARN);
|
|
}
|
|
else
|
|
ent.model = cl.model_precache[s1->modelindex2];
|
|
|
|
// PMM - check for the defender sphere shell .. make it translucent
|
|
// replaces the previous version which used the high bit on modelindex2 to determine transparency
|
|
/* if (!Q_strcasecmp (cl.model_name[(s1->modelindex2)], "models/items/shell/tris.md2"))
|
|
{
|
|
ent.alpha = 0.32;
|
|
ent.flags |= Q2RF_TRANSLUCENT;
|
|
|
|
V_AddEntity (&ent);
|
|
|
|
// make sure these get reset.
|
|
ent.flags &= RF_EXTERNALMODEL;
|
|
ent.shaderRGBAf[3] = 1;
|
|
}
|
|
else
|
|
*/ // pmm
|
|
|
|
V_AddEntity (&ent);
|
|
}
|
|
if (s1->u.q2.modelindex3)
|
|
{
|
|
ent.model = cl.model_precache[s1->u.q2.modelindex3];
|
|
V_AddEntity (&ent);
|
|
}
|
|
if (s1->u.q2.modelindex4)
|
|
{
|
|
ent.model = cl.model_precache[s1->u.q2.modelindex4];
|
|
V_AddEntity (&ent);
|
|
}
|
|
|
|
if ( effects & Q2EF_POWERSCREEN )
|
|
{
|
|
ent.model = Mod_ForName("models/items/armor/effect/tris.md2", MLV_WARN);
|
|
ent.framestate.g[FS_REG].frame[0] = 0;
|
|
ent.framestate.g[FS_REG].frame[0] = 0;
|
|
ent.flags |= (RF_TRANSLUCENT | Q2RF_SHELL_GREEN);
|
|
ent.shaderRGBAf[3] = 0.30;
|
|
V_AddEntity (&ent);
|
|
}
|
|
|
|
// add automatic particle trails
|
|
if ( (effects&~Q2EF_ROTATE) )
|
|
{
|
|
if (effects & Q2EF_ROCKET)
|
|
{
|
|
//FIXME: cubemap orientation
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_ROCKET], timestep, ent.keynum, NULL, ¢->trailstate))
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_rocket, timestep, ent.keynum, NULL, ¢->trailstate))
|
|
{
|
|
P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xdc, 4, ¢->trailstate);
|
|
V_AddLight (ent.keynum, ent.origin, 200, 0.2, 0.1, 0.05);
|
|
}
|
|
}
|
|
// PGM - Do not reorder EF_BLASTER and EF_HYPERBLASTER.
|
|
// EF_BLASTER | EF_TRACKER is a special case for EF_BLASTER2... Cheese!
|
|
else if (effects & Q2EF_BLASTER)
|
|
{
|
|
//PGM
|
|
if (effects & Q2EF_TRACKER) // lame... problematic?
|
|
{
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_BLASTERTRAIL2], timestep, ent.keynum, NULL, ¢->trailstate))
|
|
{
|
|
P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xd0, 1, ¢->trailstate);
|
|
V_AddLight (ent.keynum, ent.origin, 200, 0, 0.2, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_BLASTERTRAIL], timestep, ent.keynum, NULL, ¢->trailstate))
|
|
{
|
|
P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xe0, 1, ¢->trailstate);
|
|
V_AddLight (ent.keynum, ent.origin, 200, 0.2, 0.2, 0);
|
|
}
|
|
}
|
|
//PGM
|
|
}
|
|
else if (effects & Q2EF_HYPERBLASTER)
|
|
{
|
|
if (effects & Q2EF_TRACKER) // PGM overloaded for blaster2.
|
|
V_AddLight (ent.keynum, ent.origin, 200, 0, 0.2, 0); // PGM
|
|
else // PGM
|
|
V_AddLight (ent.keynum, ent.origin, 200, 0.2, 0.2, 0);
|
|
}
|
|
else if (effects & Q2EF_GIB)
|
|
{
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GIB], timestep, ent.keynum, NULL, ¢->trailstate))
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_blood, timestep, ent.keynum, NULL, ¢->trailstate))
|
|
P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0xe8, 8, ¢->trailstate);
|
|
}
|
|
else if (effects & Q2EF_GRENADE)
|
|
{
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GRENADE], timestep, ent.keynum, NULL, ¢->trailstate))
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, rt_grenade, timestep, ent.keynum, NULL, ¢->trailstate))
|
|
P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 4, 8, ¢->trailstate);
|
|
}
|
|
else if (effects & Q2EF_FLIES)
|
|
{
|
|
CLQ2_FlyEffect (cent, ent.origin);
|
|
}
|
|
else if (effects & Q2EF_BFG)
|
|
{
|
|
static int bfg_lightramp[6] = {300, 400, 600, 300, 150, 75};
|
|
|
|
if (effects & Q2EF_ANIM_ALLFAST)
|
|
{
|
|
CLQ2_BfgParticles (cent, ent.origin);
|
|
i = 200;
|
|
}
|
|
else
|
|
{
|
|
i = bfg_lightramp[s1->frame];
|
|
}
|
|
V_AddLight (ent.keynum, ent.origin, i, 0, 0.2, 0);
|
|
}
|
|
// RAFAEL
|
|
else if (effects & Q2EF_TRAP)
|
|
{
|
|
ent.origin[2] += 32;
|
|
P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TRAP], timestep, ent.keynum, NULL, ¢->trailstate);
|
|
}
|
|
else if (effects & Q2EF_FLAG1)
|
|
{
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_FLAG1], timestep, ent.keynum, NULL, ¢->trailstate))
|
|
{
|
|
P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 242, 1, ¢->trailstate);
|
|
V_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.05, 0.05);
|
|
}
|
|
}
|
|
else if (effects & Q2EF_FLAG2)
|
|
{
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_FLAG2], timestep, ent.keynum, NULL, ¢->trailstate))
|
|
{
|
|
P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 115, 1, ¢->trailstate);
|
|
V_AddLight (ent.keynum, ent.origin, 225, 0.05, 0.05, 0.2);
|
|
}
|
|
}
|
|
//======
|
|
//ROGUE
|
|
else if (effects & Q2EF_TAGTRAIL)
|
|
{
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TAGTRAIL], timestep, ent.keynum, NULL, ¢->trailstate))
|
|
{
|
|
P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 220, 1, ¢->trailstate);
|
|
V_AddLight (ent.keynum, ent.origin, 225, 0.2, 0.2, 0.0);
|
|
}
|
|
}
|
|
else if (effects & Q2EF_TRACKERTRAIL)
|
|
{
|
|
if (effects & Q2EF_TRACKER)
|
|
{
|
|
float intensity;
|
|
|
|
intensity = 50 + (500 * (sin(cl.time/500.0) + 1.0));
|
|
|
|
// FIXME - check out this effect in rendition
|
|
V_AddLight (ent.keynum, ent.origin, intensity, -0.2, -0.2, -0.2);
|
|
}
|
|
else
|
|
{
|
|
CLQ2_Tracker_Shell (cent, cent->lerp_origin);
|
|
V_AddLight (ent.keynum, ent.origin, 155, -0.2, -0.2, -0.2);
|
|
}
|
|
}
|
|
else if (effects & Q2EF_TRACKER)
|
|
{
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_TRACKER], timestep, ent.keynum, NULL, ¢->trailstate))
|
|
{
|
|
P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 0, 1, ¢->trailstate);
|
|
V_AddLight (ent.keynum, ent.origin, 200, -0.2, -0.2, -0.2);
|
|
}
|
|
}
|
|
//ROGUE
|
|
//======
|
|
// RAFAEL
|
|
else if (effects & Q2EF_GREENGIB)
|
|
{
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_GREENGIB], timestep, ent.keynum, NULL, ¢->trailstate))
|
|
P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 219, 8, ¢->trailstate);
|
|
}
|
|
// RAFAEL
|
|
else if (effects & Q2EF_IONRIPPER)
|
|
{
|
|
if (P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_IONRIPPER], timestep, ent.keynum, NULL, ¢->trailstate))
|
|
{
|
|
P_ParticleTrailIndex(cent->lerp_origin, ent.origin, P_INVALID, timestep, 228, 4, ¢->trailstate);
|
|
V_AddLight (ent.keynum, ent.origin, 100, 0.2, 0.1, 0.1);
|
|
}
|
|
}
|
|
// RAFAEL
|
|
else if (effects & Q2EF_BLUEHYPERBLASTER)
|
|
{
|
|
V_AddLight (ent.keynum, ent.origin, 200, 0, 0, 0.2);
|
|
}
|
|
// RAFAEL
|
|
else if (effects & Q2EF_PLASMA)
|
|
{
|
|
if (effects & Q2EF_ANIM_ALLFAST)
|
|
{
|
|
P_ParticleTrail(cent->lerp_origin, ent.origin, pt_q2[Q2RT_PLASMA], timestep, ent.keynum, NULL, ¢->trailstate);
|
|
}
|
|
V_AddLight (ent.keynum, ent.origin, 130, 0.2, 0.1, 0.1);
|
|
}
|
|
}
|
|
|
|
VectorCopy (ent.origin, cent->lerp_origin);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
==============
|
|
CL_AddViewWeapon
|
|
==============
|
|
*/
|
|
static void CLQ2_AddViewWeapon (int seat, q2player_state_t *ps, q2player_state_t *ops)
|
|
{
|
|
playerview_t *pv = &cl.playerview[seat];
|
|
|
|
pv->vm.oldmodel = NULL;
|
|
|
|
// allow the gun to be completely removed
|
|
if (!r_drawviewmodel.value)
|
|
return;
|
|
|
|
if (!Cam_DrawViewModel(pv))
|
|
return;
|
|
|
|
// don't draw gun if in wide angle view
|
|
if (ps->fov > 90)
|
|
return;
|
|
|
|
//generate root matrix..
|
|
VectorCopy(pv->simorg, r_refdef.weaponmatrix[3]);
|
|
AngleVectors(pv->simangles, r_refdef.weaponmatrix[0], r_refdef.weaponmatrix[1], r_refdef.weaponmatrix[2]);
|
|
VectorInverse(r_refdef.weaponmatrix[1]);
|
|
memcpy(r_refdef.weaponmatrix_bob, r_refdef.weaponmatrix, sizeof(r_refdef.weaponmatrix_bob));
|
|
|
|
pv->vm.oldmodel = cl.model_precache[ps->gunindex];
|
|
if (!pv->vm.oldmodel)
|
|
return;
|
|
|
|
pv->vm.oldframe = ps->gunframe;
|
|
if (ps->gunindex != ops->gunindex)
|
|
pv->vm.prevframe = ps->gunframe;
|
|
else
|
|
pv->vm.prevframe = ops->gunframe;
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
CL_CalcViewValues
|
|
|
|
Sets r_refdef view values
|
|
===============
|
|
*/
|
|
void CLQ2_CalcViewValues (int seat)
|
|
{
|
|
int i;
|
|
float lerp, backlerp;
|
|
q2frame_t *oldframe;
|
|
q2player_state_t *ps, *ops;
|
|
playerview_t *pv = &cl.playerview[seat];
|
|
|
|
r_refdef.areabitsknown = true;
|
|
memcpy(r_refdef.areabits, cl.q2frame.areabits, sizeof(r_refdef.areabits));
|
|
|
|
r_refdef.useperspective = true;
|
|
r_refdef.mindist = bound(0.1, gl_mindist.value, 4);
|
|
r_refdef.maxdist = gl_maxdist.value;
|
|
|
|
// find the previous frame to interpolate from
|
|
ps = &cl.q2frame.playerstate[seat];
|
|
i = (cl.q2frame.serverframe - 1) & Q2UPDATE_MASK;
|
|
oldframe = &cl.q2frames[i];
|
|
if (oldframe->serverframe != cl.q2frame.serverframe-1 || !oldframe->valid)
|
|
oldframe = &cl.q2frame; // previous frame was dropped or involid
|
|
ops = &oldframe->playerstate[seat];
|
|
|
|
// see if the player entity was teleported this frame
|
|
if ( abs(ops->pmove.origin[0] - ps->pmove.origin[0]) > 256*8
|
|
|| abs(ops->pmove.origin[1] - ps->pmove.origin[1]) > 256*8
|
|
|| abs(ops->pmove.origin[2] - ps->pmove.origin[2]) > 256*8)
|
|
ops = ps; // don't interpolate
|
|
|
|
lerp = cl.lerpfrac;
|
|
|
|
// calculate the origin
|
|
if (cl.worldmodel && (!cl_nopred.value) && !(cl.q2frame.playerstate[seat].pmove.pm_flags & Q2PMF_NO_PREDICTION) && !cls.demoplayback)
|
|
{ // use predicted values
|
|
float delta;
|
|
|
|
backlerp = 1.0 - lerp;
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
pv->simorg[i] = pv->predicted_origin[i] + ops->viewoffset[i]
|
|
+ lerp * (ps->viewoffset[i] - ops->viewoffset[i])
|
|
- backlerp * pv->prediction_error[i];
|
|
}
|
|
|
|
// smooth out stair climbing
|
|
delta = realtime - pv->predicted_step_time;
|
|
if (delta < 0.1)
|
|
pv->simorg[2] -= pv->predicted_step * (0.1 - delta)*10;
|
|
}
|
|
else
|
|
{ // just use interpolated values
|
|
for (i=0 ; i<3 ; i++)
|
|
pv->simorg
|
|
[i] = ops->pmove.origin[i]*0.125 + ops->viewoffset[i]
|
|
+ lerp * (ps->pmove.origin[i]*0.125 + ps->viewoffset[i]
|
|
- (ops->pmove.origin[i]*0.125 + ops->viewoffset[i]) );
|
|
}
|
|
|
|
// if not running a demo or on a locked frame, add the local angle movement
|
|
if (cl.worldmodel && ps->pmove.pm_type < Q2PM_DEAD && !cls.demoplayback)
|
|
{ // use predicted values
|
|
for (i=0 ; i<3 ; i++)
|
|
pv->simangles[i] = pv->predicted_angles[i];
|
|
}
|
|
else
|
|
{ // just use interpolated values
|
|
for (i=0 ; i<3 ; i++)
|
|
pv->simangles[i] = LerpAngle (ops->viewangles[i], ps->viewangles[i], lerp);
|
|
}
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
pv->simangles[i] += v_gunkick_q2.value * LerpAngle (ops->kick_angles[i], ps->kick_angles[i], lerp);
|
|
|
|
// VectorCopy(r_refdef.viewangles, cl.viewangles);
|
|
|
|
// AngleVectors (r_refdef.viewangles, v_forward, v_right, v_up);
|
|
|
|
// interpolate field of view
|
|
pv->forcefov = ops->fov + lerp * (ps->fov - ops->fov);
|
|
pv->handedness = atoi(InfoBuf_ValueForKey(&cls.userinfo[seat], "hand"));
|
|
|
|
//do interpolate blend alpha, but only if the rgb didn't change
|
|
// don't interpolate blend color
|
|
for (i=0 ; i<3 ; i++)
|
|
pv->screentint[i] = ps->blend[i];
|
|
if (ps->blend[0] == ops->blend[0] && ps->blend[1] == ops->blend[1] && ps->blend[2] == ops->blend[2] && (!ps->blend[3]) == (!ops->blend[3]))
|
|
pv->screentint[3] = (ops->blend[3] + lerp * (ps->blend[3]-ops->blend[3]))*gl_cshiftenabled.value;
|
|
else
|
|
pv->screentint[3] = ps->blend[3]*gl_cshiftenabled.value;
|
|
|
|
// add the weapon
|
|
CLQ2_AddViewWeapon (seat, ps, ops);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CL_AddEntities
|
|
|
|
Emits all entities, particles, and lights to the refresh
|
|
===============
|
|
*/
|
|
void CLQ2_AddEntities (void)
|
|
{
|
|
int seat;
|
|
if (cls.state != ca_active)
|
|
return;
|
|
|
|
cl.lerpfrac = 1.0 - (cl.q2frame.servertime/1000.0 - cl.time) * cl.q2svnetrate;
|
|
// Con_Printf("%g: %g\n", cl.q2frame.servertime - (cl.time*1000), cl.lerpfrac);
|
|
cl.lerpfrac = bound(0, cl.lerpfrac, 1);
|
|
|
|
for (seat = 0; seat < cl.splitclients; seat++)
|
|
CLQ2_CalcViewValues (seat);
|
|
CLQ2_AddPacketEntities (&cl.q2frame);
|
|
#if 0
|
|
CLQ2_AddProjectiles ();
|
|
#endif
|
|
CL_UpdateTEnts ();
|
|
|
|
#ifdef _DEBUG
|
|
if (chase_active.ival)
|
|
{
|
|
playerview_t *pv = &cl.playerview[0];
|
|
vec3_t axis[3];
|
|
vec3_t camorg;
|
|
// trace_t tr;
|
|
AngleVectors(r_refdef.viewangles, axis[0], axis[1], axis[2]);
|
|
VectorMA(r_refdef.vieworg, -chase_back.value, axis[0], camorg);
|
|
VectorMA(camorg, -chase_up.value, pv->gravitydir, camorg);
|
|
// if (cl.worldmodel && cl.worldmodel->funcs.NativeTrace(cl.worldmodel, 0, NULLFRAMESTATE, NULL, r_refdef.vieworg, camorg, vec3_origin, vec3_origin, true, MASK_WORLDSOLID, &tr))
|
|
VectorCopy(camorg, r_refdef.vieworg);
|
|
|
|
V_EditExternalModels(0, NULL, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif
|