aboutsummaryrefslogtreecommitdiffstats
path: root/src/jake2/client/CL_ents.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jake2/client/CL_ents.java')
-rw-r--r--src/jake2/client/CL_ents.java1287
1 files changed, 1287 insertions, 0 deletions
diff --git a/src/jake2/client/CL_ents.java b/src/jake2/client/CL_ents.java
new file mode 100644
index 0000000..135f3fe
--- /dev/null
+++ b/src/jake2/client/CL_ents.java
@@ -0,0 +1,1287 @@
+/*
+ * CL_ents.java
+ * Copyright (C) 2004
+ *
+ * $Id: CL_ents.java,v 1.1 2004-07-07 19:58:35 hzi Exp $
+ */
+/*
+Copyright (C) 1997-2001 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.
+
+*/
+package jake2.client;
+
+import jake2.game.entity_state_t;
+import jake2.game.player_state_t;
+import jake2.qcommon.*;
+import jake2.render.model_t;
+
+/**
+ * CL_ents
+ */
+// cl_ents.c -- entity parsing and management
+public class CL_ents extends CL_inv {
+
+ // PGM
+ static int vidref_val;
+ // PGM
+
+ /*
+ =========================================================================
+
+ FRAME PARSING
+
+ =========================================================================
+ */
+
+ /*
+ =================
+ CL_ParseEntityBits
+
+ Returns the entity number and the header bits
+ =================
+ */
+ static int bitcounts[] = new int[32]; /// just for protocol profiling
+ public static int ParseEntityBits(CM.intwrap bits) {
+ int b, total;
+ int i;
+ int number;
+
+ total = MSG.ReadByte(net_message);
+ if ((total & U_MOREBITS1) != 0) {
+ b = MSG.ReadByte(net_message);
+ total |= b << 8;
+ }
+ if ((total & U_MOREBITS2) != 0) {
+ b = MSG.ReadByte(net_message);
+ total |= b << 16;
+ }
+ if ((total & U_MOREBITS3) != 0) {
+ b = MSG.ReadByte(net_message);
+ total |= b << 24;
+ }
+
+ // count the bits for net profiling
+ for (i = 0; i < 32; i++)
+ if ((total & (1 << i)) != 0)
+ bitcounts[i]++;
+
+ if ((total & U_NUMBER16) != 0)
+ number = MSG.ReadShort(net_message);
+ else
+ number = MSG.ReadByte(net_message);
+
+ bits.i = total;
+
+ return number;
+ }
+
+ /*
+ ==================
+ CL_ParseDelta
+
+ Can go from either a baseline or a previous packet_entity
+ ==================
+ */
+ public static void ParseDelta(entity_state_t from, entity_state_t to, int number, int bits) {
+ // set everything to the state we are delta'ing from
+ to.set(from);
+
+ VectorCopy(from.origin, to.old_origin);
+ to.number = number;
+
+ if ((bits & U_MODEL) != 0)
+ to.modelindex = MSG.ReadByte(net_message);
+ if ((bits & U_MODEL2) != 0)
+ to.modelindex2 = MSG.ReadByte(net_message);
+ if ((bits & U_MODEL3) != 0)
+ to.modelindex3 = MSG.ReadByte(net_message);
+ if ((bits & U_MODEL4) != 0)
+ to.modelindex4 = MSG.ReadByte(net_message);
+
+ if ((bits & U_FRAME8) != 0)
+ to.frame = MSG.ReadByte(net_message);
+ if ((bits & U_FRAME16) != 0)
+ to.frame = MSG.ReadShort(net_message);
+
+ if ((bits & U_SKIN8) != 0 && (bits & U_SKIN16) != 0) //used for laser colors
+ to.skinnum = MSG.ReadLong(net_message);
+ else if ((bits & U_SKIN8) != 0)
+ to.skinnum = MSG.ReadByte(net_message);
+ else if ((bits & U_SKIN16) != 0)
+ to.skinnum = MSG.ReadShort(net_message);
+
+ if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16))
+ to.effects = MSG.ReadLong(net_message);
+ else if ((bits & U_EFFECTS8) != 0)
+ to.effects = MSG.ReadByte(net_message);
+ else if ((bits & U_EFFECTS16) != 0)
+ to.effects = MSG.ReadShort(net_message);
+
+ if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16))
+ to.renderfx = MSG.ReadLong(net_message);
+ else if ((bits & U_RENDERFX8) != 0)
+ to.renderfx = MSG.ReadByte(net_message);
+ else if ((bits & U_RENDERFX16) != 0)
+ to.renderfx = MSG.ReadShort(net_message);
+
+ if ((bits & U_ORIGIN1) != 0)
+ to.origin[0] = MSG.ReadCoord(net_message);
+ if ((bits & U_ORIGIN2) != 0)
+ to.origin[1] = MSG.ReadCoord(net_message);
+ if ((bits & U_ORIGIN3) != 0)
+ to.origin[2] = MSG.ReadCoord(net_message);
+
+ if ((bits & U_ANGLE1) != 0)
+ to.angles[0] = MSG.ReadAngle(net_message);
+ if ((bits & U_ANGLE2) != 0)
+ to.angles[1] = MSG.ReadAngle(net_message);
+ if ((bits & U_ANGLE3) != 0)
+ to.angles[2] = MSG.ReadAngle(net_message);
+
+ if ((bits & U_OLDORIGIN) != 0)
+ MSG.ReadPos(net_message, to.old_origin);
+
+ if ((bits & U_SOUND) != 0)
+ to.sound = MSG.ReadByte(net_message);
+
+ if ((bits & U_EVENT) != 0)
+ to.event = MSG.ReadByte(net_message);
+ else
+ to.event = 0;
+
+ if ((bits & U_SOLID) != 0)
+ to.solid = MSG.ReadShort(net_message);
+ }
+
+ /*
+ ==================
+ CL_DeltaEntity
+
+ Parses deltas from the given base and adds the resulting entity
+ to the current frame
+ ==================
+ */
+ public static void DeltaEntity(frame_t frame, int newnum, entity_state_t old, int bits) {
+ centity_t ent;
+ entity_state_t state;
+
+ ent = cl_entities[newnum];
+
+ state = cl_parse_entities[cl.parse_entities & (MAX_PARSE_ENTITIES - 1)];
+ cl.parse_entities++;
+ frame.num_entities++;
+
+ ParseDelta(old, state, newnum, bits);
+
+ // some data changes will force no lerping
+ if (state.modelindex != ent.current.modelindex
+ || state.modelindex2 != ent.current.modelindex2
+ || state.modelindex3 != ent.current.modelindex3
+ || state.modelindex4 != ent.current.modelindex4
+ || Math.abs(state.origin[0] - ent.current.origin[0]) > 512
+ || Math.abs(state.origin[1] - ent.current.origin[1]) > 512
+ || Math.abs(state.origin[2] - ent.current.origin[2]) > 512
+ || state.event == EV_PLAYER_TELEPORT
+ || state.event == EV_OTHER_TELEPORT) {
+ ent.serverframe = -99;
+ }
+
+ if (ent.serverframe != cl.frame.serverframe - 1) { // wasn't in last update, so initialize some things
+ ent.trailcount = 1024; // for diminishing rocket / grenade trails
+ // duplicate the current state so lerping doesn't hurt anything
+ ent.prev.set(state);
+ if (state.event == EV_OTHER_TELEPORT) {
+ VectorCopy(state.origin, ent.prev.origin);
+ VectorCopy(state.origin, ent.lerp_origin);
+ }
+ else {
+ VectorCopy(state.old_origin, ent.prev.origin);
+ VectorCopy(state.old_origin, ent.lerp_origin);
+ }
+ }
+ else { // shuffle the last state to previous
+ // Copy !
+ ent.prev.set(ent.current);
+ }
+
+ ent.serverframe = cl.frame.serverframe;
+ // Copy !
+ ent.current.set(state);
+ }
+
+ /*
+ ==================
+ CL_ParsePacketEntities
+
+ An svc_packetentities has just been parsed, deal with the
+ rest of the data stream.
+ ==================
+ */
+ public static void ParsePacketEntities(frame_t oldframe, frame_t newframe) {
+ int newnum;
+ int bits=0;
+
+ entity_state_t oldstate=null;
+ int oldindex, oldnum;
+
+ newframe.parse_entities = cl.parse_entities;
+ newframe.num_entities = 0;
+
+ // delta from the entities present in oldframe
+ oldindex = 0;
+ if (oldframe == null)
+ oldnum = 99999;
+ else {
+ if (oldindex >= oldframe.num_entities)
+ oldnum = 99999;
+ else {
+ oldstate = cl_parse_entities[(oldframe.parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)];
+ oldnum = oldstate.number;
+ }
+ }
+
+ while (true) {
+ CM.intwrap iw = new CM.intwrap(bits);
+ newnum = ParseEntityBits(iw);
+ bits = iw.i;
+
+ if (newnum >= MAX_EDICTS)
+ Com.Error(ERR_DROP, "CL_ParsePacketEntities: bad number:" + newnum);
+
+ if (net_message.readcount > net_message.cursize)
+ Com.Error(ERR_DROP, "CL_ParsePacketEntities: end of message");
+
+ if (0 == newnum)
+ break;
+
+ while (oldnum < newnum) { // one or more entities from the old packet are unchanged
+ if (cl_shownet.value == 3)
+ Com.Printf(" unchanged: " + oldnum + "\n");
+ DeltaEntity(newframe, oldnum, oldstate, 0);
+
+ oldindex++;
+
+ if (oldindex >= oldframe.num_entities)
+ oldnum = 99999;
+ else {
+ oldstate = cl_parse_entities[(oldframe.parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)];
+ oldnum = oldstate.number;
+ }
+ }
+
+ if ((bits & U_REMOVE) != 0) { // the entity present in oldframe is not in the current frame
+ if (cl_shownet.value == 3)
+ Com.Printf(" remove: " + newnum + "\n");
+ if (oldnum != newnum)
+ Com.Printf("U_REMOVE: oldnum != newnum\n");
+
+ oldindex++;
+
+ if (oldindex >= oldframe.num_entities)
+ oldnum = 99999;
+ else {
+ oldstate = cl_parse_entities[(oldframe.parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)];
+ oldnum = oldstate.number;
+ }
+ continue;
+ }
+
+ if (oldnum == newnum) { // delta from previous state
+ if (cl_shownet.value == 3)
+ Com.Printf(" delta: " + newnum + "\n");
+ DeltaEntity(newframe, newnum, oldstate, bits);
+
+ oldindex++;
+
+ if (oldindex >= oldframe.num_entities)
+ oldnum = 99999;
+ else {
+ oldstate = cl_parse_entities[(oldframe.parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)];
+ oldnum = oldstate.number;
+ }
+ continue;
+ }
+
+ if (oldnum > newnum) { // delta from baseline
+ if (cl_shownet.value == 3)
+ Com.Printf(" baseline: " + newnum + "\n");
+ 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.value == 3)
+ Com.Printf(" unchanged: " + oldnum + "\n");
+ DeltaEntity(newframe, oldnum, oldstate, 0);
+
+ oldindex++;
+
+ if (oldindex >= oldframe.num_entities)
+ oldnum = 99999;
+ else {
+ oldstate = cl_parse_entities[(oldframe.parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)];
+ oldnum = oldstate.number;
+ }
+ }
+ }
+
+ /*
+ ===================
+ CL_ParsePlayerstate
+ ===================
+ */
+ public static void ParsePlayerstate(frame_t oldframe, frame_t newframe) {
+ int flags;
+ player_state_t state;
+ int i;
+ int statbits;
+
+ state = newframe.playerstate;
+
+ // clear to old value before delta parsing
+ if (oldframe != null)
+ state.set(oldframe.playerstate);
+ else
+ //memset (state, 0, sizeof(*state));
+ state.clear();
+
+ flags = MSG.ReadShort(net_message);
+
+ //
+ // parse the pmove_state_t
+ //
+ if ((flags & PS_M_TYPE) != 0)
+ state.pmove.pm_type = MSG.ReadByte(net_message);
+
+ if ((flags & PS_M_ORIGIN) != 0) {
+ state.pmove.origin[0] = MSG.ReadShort(net_message);
+ state.pmove.origin[1] = MSG.ReadShort(net_message);
+ state.pmove.origin[2] = MSG.ReadShort(net_message);
+ }
+
+ if ((flags & PS_M_VELOCITY) != 0) {
+ state.pmove.velocity[0] = MSG.ReadShort(net_message);
+ state.pmove.velocity[1] = MSG.ReadShort(net_message);
+ state.pmove.velocity[2] = MSG.ReadShort(net_message);
+ }
+
+ if ((flags & PS_M_TIME) != 0)
+ {
+ state.pmove.pm_time = (byte) MSG.ReadByte(net_message);
+ }
+
+ if ((flags & PS_M_FLAGS) != 0)
+ state.pmove.pm_flags = (byte) MSG.ReadByte(net_message);
+
+ if ((flags & PS_M_GRAVITY) != 0)
+ state.pmove.gravity = MSG.ReadShort(net_message);
+
+ if ((flags & PS_M_DELTA_ANGLES) != 0) {
+ state.pmove.delta_angles[0] = MSG.ReadShort(net_message);
+ state.pmove.delta_angles[1] = MSG.ReadShort(net_message);
+ state.pmove.delta_angles[2] = MSG.ReadShort(net_message);
+ }
+
+ if (cl.attractloop)
+ state.pmove.pm_type = PM_FREEZE; // demo playback
+
+ //
+ // parse the rest of the player_state_t
+ //
+ if ((flags & PS_VIEWOFFSET) != 0) {
+ state.viewoffset[0] = MSG.ReadChar(net_message) * 0.25f;
+ state.viewoffset[1] = MSG.ReadChar(net_message) * 0.25f;
+ state.viewoffset[2] = MSG.ReadChar(net_message) * 0.25f;
+ }
+
+ if ((flags & PS_VIEWANGLES) != 0) {
+ state.viewangles[0] = MSG.ReadAngle16(net_message);
+ state.viewangles[1] = MSG.ReadAngle16(net_message);
+ state.viewangles[2] = MSG.ReadAngle16(net_message);
+ }
+
+ if ((flags & PS_KICKANGLES) != 0) {
+
+ state.kick_angles[0] = MSG.ReadChar(net_message) * 0.25f;
+ state.kick_angles[1] = MSG.ReadChar(net_message) * 0.25f;
+ state.kick_angles[2] = MSG.ReadChar(net_message) * 0.25f;
+
+ }
+
+ if ((flags & PS_WEAPONINDEX) != 0) {
+ state.gunindex = MSG.ReadByte(net_message);
+ }
+
+ if ((flags & PS_WEAPONFRAME) != 0) {
+ state.gunframe = MSG.ReadByte(net_message);
+ state.gunoffset[0] = MSG.ReadChar(net_message) * 0.25f;
+ state.gunoffset[1] = MSG.ReadChar(net_message) * 0.25f;
+ state.gunoffset[2] = MSG.ReadChar(net_message) * 0.25f;
+ state.gunangles[0] = MSG.ReadChar(net_message) * 0.25f;
+ state.gunangles[1] = MSG.ReadChar(net_message) * 0.25f;
+ state.gunangles[2] = MSG.ReadChar(net_message) * 0.25f;
+ }
+
+ if ((flags & PS_BLEND) != 0) {
+ state.blend[0] = MSG.ReadByte(net_message) / 255.0f;
+ state.blend[1] = MSG.ReadByte(net_message) / 255.0f;
+ state.blend[2] = MSG.ReadByte(net_message) / 255.0f;
+ state.blend[3] = MSG.ReadByte(net_message) / 255.0f;
+ }
+
+ if ((flags & PS_FOV) != 0)
+ state.fov = MSG.ReadByte(net_message);
+
+ if ((flags & PS_RDFLAGS) != 0)
+ state.rdflags = MSG.ReadByte(net_message);
+
+ // parse stats
+ statbits = MSG.ReadLong(net_message);
+ for (i = 0; i < MAX_STATS; i++)
+ if ((statbits & (1 << i))!=0)
+ state.stats[i] = MSG.ReadShort(net_message);
+ }
+
+ /*
+ ==================
+ CL_FireEntityEvents
+
+ ==================
+ */
+ public static void FireEntityEvents(frame_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 = cl_parse_entities[num];
+ if (s1.event!=0)
+ EntityEvent(s1);
+
+ // EF_TELEPORTER acts like an event, but is not cleared each frame
+ if ((s1.effects & EF_TELEPORTER)!=0)
+ CL_fx.TeleporterParticles(s1);
+ }
+ }
+
+ /*
+ ================
+ CL_ParseFrame
+ ================
+ */
+ public static void ParseFrame() {
+ int cmd;
+ int len;
+ frame_t old;
+
+ //memset( cl.frame, 0, sizeof(cl.frame));
+ cl.frame.reset();
+
+ cl.frame.serverframe = MSG.ReadLong(net_message);
+ cl.frame.deltaframe = MSG.ReadLong(net_message);
+ cl.frame.servertime = cl.frame.serverframe * 100;
+
+ // BIG HACK to let old demos continue to work
+ if (cls.serverProtocol != 26)
+ cl.surpressCount = MSG.ReadByte(net_message);
+
+ if (cl_shownet.value == 3)
+ Com.Printf(" frame:" + cl.frame.serverframe + " delta:" + cl.frame.deltaframe + "\n");
+
+ // 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.frame.deltaframe <= 0) {
+ cl.frame.valid = true; // uncompressed frame
+ old = null;
+ cls.demowaiting = false; // we can start recording now
+ }
+ else {
+ old = cl.frames[cl.frame.deltaframe & UPDATE_MASK];
+ if (!old.valid) { // should never happen
+ Com.Printf("Delta from invalid frame (not supposed to happen!).\n");
+ }
+ if (old.serverframe != cl.frame.deltaframe) { // The frame that the server did the delta from
+ // is too old, so we can't reconstruct it properly.
+ Com.Printf("Delta frame too old.\n");
+ }
+ else if (cl.parse_entities - old.parse_entities > MAX_PARSE_ENTITIES - 128) {
+ Com.Printf("Delta parse_entities too old.\n");
+ }
+ else
+ cl.frame.valid = true; // valid delta parse
+ }
+
+ // clamp time
+ if (cl.time > cl.frame.servertime)
+ cl.time = cl.frame.servertime;
+ else if (cl.time < cl.frame.servertime - 100)
+ cl.time = cl.frame.servertime - 100;
+
+ // read areabits
+ len = MSG.ReadByte(net_message);
+ MSG.ReadData(net_message, cl.frame.areabits, len);
+
+ // read playerinfo
+ cmd = MSG.ReadByte(net_message);
+ CL_parse.SHOWNET(CL_parse.svc_strings[cmd]);
+ if (cmd != svc_playerinfo)
+ Com.Error(ERR_DROP, "CL_ParseFrame: not playerinfo");
+ ParsePlayerstate(old, cl.frame);
+
+ // read packet entities
+ cmd = MSG.ReadByte(net_message);
+ CL_parse.SHOWNET(CL_parse.svc_strings[cmd]);
+ if (cmd != svc_packetentities)
+ Com.Error(ERR_DROP, "CL_ParseFrame: not packetentities");
+
+ ParsePacketEntities(old, cl.frame);
+
+ // save the frame off in the backup array for later delta comparisons
+ cl.frames[cl.frame.serverframe & UPDATE_MASK].set(cl.frame);
+
+ if (cl.frame.valid) {
+ // getting a valid frame message ends the connection process
+ if (cls.state != ca_active) {
+ cls.state = ca_active;
+ cl.force_refdef = true;
+
+ cl.predicted_origin[0] = cl.frame.playerstate.pmove.origin[0] * 0.125f;
+ cl.predicted_origin[1] = cl.frame.playerstate.pmove.origin[1] * 0.125f;
+ cl.predicted_origin[2] = cl.frame.playerstate.pmove.origin[2] * 0.125f;
+
+ VectorCopy(cl.frame.playerstate.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
+ FireEntityEvents(cl.frame);
+ CL_pred.CheckPredictionError();
+ }
+ }
+
+ /*
+ ==========================================================================
+
+ INTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS
+
+ ==========================================================================
+ */
+
+ public static model_t S_RegisterSexedModel(entity_state_t ent, String base) {
+ int n;
+ String p;
+ model_t mdl;
+ String model;
+ String buffer;
+
+ // determine what model the client is using
+ model = "";
+
+ n = CS_PLAYERSKINS + ent.number - 1;
+
+ if (cl.configstrings[n].length() >0) {
+
+ int pos = cl.configstrings[n].indexOf('\\');
+ if (pos!=-1) {
+ pos++;
+ model = cl.configstrings[n].substring(pos);
+ pos = model.indexOf('/');
+ if (pos !=-1)
+ model = model.substring(0,pos);
+ }
+ }
+ // if we can't figure it out, they're male
+ if (model.length()==0)
+ model = "male";
+
+ buffer= "players/" + model + "/" + base + 1;
+ mdl = re.RegisterModel(buffer);
+ if (mdl==null) {
+ // not found, try default weapon model
+ buffer = "players/" + model + "/weapon.md2";
+ mdl = re.RegisterModel(buffer);
+ if (mdl==null) {
+ // no, revert to the male model
+ buffer="players/male/" + base + 1;
+ mdl = re.RegisterModel(buffer);
+ if (mdl==null) {
+ // last try, default male weapon.md2
+ buffer = "players/male/weapon.md2";
+ mdl = re.RegisterModel(buffer);
+ }
+ }
+ }
+
+ return mdl;
+ }
+
+ // PMM - used in shell code
+
+ /*
+ ===============
+ CL_AddPacketEntities
+
+ ===============
+ */
+ static int bfg_lightramp[] = { 300, 400, 600, 300, 150, 75 };
+
+ static void AddPacketEntities(frame_t frame) {
+ entity_t ent;
+ entity_state_t s1;
+ float autorotate;
+ int i;
+ int pnum;
+ centity_t cent;
+ int autoanim;
+ clientinfo_t ci;
+ int effects, renderfx;
+
+ // bonus items rotate at a fixed rate
+ autorotate = anglemod(cl.time / 10);
+
+ // brush models can auto animate their frames
+ autoanim = 2 * cl.time / 1000;
+
+ //memset( ent, 0, sizeof(ent));
+ ent = new entity_t();
+
+ for (pnum = 0; pnum < frame.num_entities; pnum++) {
+ s1 = cl_parse_entities[(frame.parse_entities + pnum) & (MAX_PARSE_ENTITIES - 1)];
+
+ cent = cl_entities[s1.number];
+
+ effects = s1.effects;
+ renderfx = s1.renderfx;
+
+ // set frame
+ if ((effects & EF_ANIM01)!=0)
+ ent.frame = autoanim & 1;
+ else if ((effects & EF_ANIM23)!=0)
+ ent.frame = 2 + (autoanim & 1);
+ else if ((effects & EF_ANIM_ALL)!=0)
+ ent.frame = autoanim;
+ else if ((effects & EF_ANIM_ALLFAST)!=0)
+ ent.frame = cl.time / 100;
+ else
+ ent.frame = s1.frame;
+
+ // quad and pent can do different things on client
+ if ((effects & EF_PENT)!=0) {
+ effects &= ~EF_PENT;
+ effects |= EF_COLOR_SHELL;
+ renderfx |= RF_SHELL_RED;
+ }
+
+ if ((effects & EF_QUAD)!=0) {
+ effects &= ~EF_QUAD;
+ effects |= EF_COLOR_SHELL;
+ renderfx |= RF_SHELL_BLUE;
+ }
+ // ======
+ // PMM
+ if ((effects & EF_DOUBLE)!=0) {
+ effects &= ~EF_DOUBLE;
+ effects |= EF_COLOR_SHELL;
+ renderfx |= RF_SHELL_DOUBLE;
+ }
+
+ if ((effects & EF_HALF_DAMAGE) !=0){
+ effects &= ~EF_HALF_DAMAGE;
+ effects |= EF_COLOR_SHELL;
+ renderfx |= RF_SHELL_HALF_DAM;
+ }
+ // pmm
+ // ======
+ ent.oldframe = cent.prev.frame;
+ ent.backlerp = 1.0f - cl.lerpfrac;
+
+ if ((renderfx & (RF_FRAMELERP | RF_BEAM))!=0) { // step origin discretely, because the frames
+ // do the animation properly
+ VectorCopy(cent.current.origin, ent.origin);
+ VectorCopy(cent.current.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 & RF_BEAM)!=0) { // the four beam colors are encoded in 32 bits of skinnum (hack)
+ ent.alpha = 0.30f;
+ ent.skinnum = (s1.skinnum >> ((rand() % 4) * 8)) & 0xff;
+ ent.model = null;
+ }
+ else {
+ // set skin
+ if (s1.modelindex == 255) { // use custom player skin
+ ent.skinnum = 0;
+ ci = cl.clientinfo[s1.skinnum & 0xff];
+ ent.skin = ci.skin;
+ ent.model = ci.model;
+ if (null==ent.skin || null==ent.model) {
+ ent.skin = cl.baseclientinfo.skin;
+ ent.model = cl.baseclientinfo.model;
+ }
+
+ // ============
+ // PGM
+ if ((renderfx & RF_USE_DISGUISE)!=0) {
+ if (ent.skin.name.startsWith("players/male")) {
+ ent.skin = re.RegisterSkin("players/male/disguise.pcx");
+ ent.model = re.RegisterModel("players/male/tris.md2");
+ }
+ else if (ent.skin.name.startsWith( "players/female")) {
+ ent.skin = re.RegisterSkin("players/female/disguise.pcx");
+ ent.model = re.RegisterModel("players/female/tris.md2");
+ }
+ else if (ent.skin.name.startsWith("players/cyborg")) {
+ 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_draw[s1.modelindex];
+ }
+ }
+
+ // only used for black hole model right now, FIXME: do better
+ if (renderfx == RF_TRANSLUCENT)
+ ent.alpha = 0.70f;
+
+ // render effects (fullbright, translucent, etc)
+ if ((effects & EF_COLOR_SHELL)!=0)
+ ent.flags = 0; // renderfx go on color shell entity
+ else
+ ent.flags = renderfx;
+
+ // calculate angles
+ if ((effects & EF_ROTATE)!=0) { // some bonus items auto-rotate
+ ent.angles[0] = 0;
+ ent.angles[1] = autorotate;
+ ent.angles[2] = 0;
+ }
+ // RAFAEL
+ else if ((effects & EF_SPINNINGLIGHTS)!=0) {
+ ent.angles[0] = 0;
+ ent.angles[1] = anglemod(cl.time / 2) + s1.angles[1];
+ ent.angles[2] = 180;
+ {
+ float[] forward={0,0,0};
+ float[] start={0,0,0};
+
+ AngleVectors(ent.angles, forward, null, null);
+ VectorMA(ent.origin, 64, forward, start);
+ V.AddLight(start, 100, 1, 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);
+ }
+ }
+
+ if (s1.number == cl.playernum + 1) {
+ ent.flags |= RF_VIEWERMODEL; // only draw from mirrors
+ // FIXME: still pass to refresh
+
+ if ((effects & EF_FLAG1)!=0)
+ V.AddLight(ent.origin, 225, 1.0f, 0.1f, 0.1f);
+ else if ((effects & EF_FLAG2)!=0)
+ V.AddLight(ent.origin, 225, 0.1f, 0.1f, 1.0f);
+ else if ((effects & EF_TAGTRAIL)!=0) //PGM
+ V.AddLight(ent.origin, 225, 1.0f, 1.0f, 0.0f); //PGM
+ else if ((effects & EF_TRACKERTRAIL)!=0) //PGM
+ V.AddLight(ent.origin, 225, -1.0f, -1.0f, -1.0f); //PGM
+
+ continue;
+ }
+
+ // if set to invisible, skip
+ if (s1.modelindex==0)
+ continue;
+
+ if ((effects & EF_BFG)!=0) {
+ ent.flags |= RF_TRANSLUCENT;
+ ent.alpha = 0.30f;
+ }
+
+ // RAFAEL
+ if ((effects & EF_PLASMA)!=0) {
+ ent.flags |= RF_TRANSLUCENT;
+ ent.alpha = 0.6f;
+ }
+
+ if ((effects & EF_SPHERETRANS)!=0) {
+ ent.flags |= RF_TRANSLUCENT;
+ // PMM - *sigh* yet more EF overloading
+ if ((effects & EF_TRACKERTRAIL)!=0)
+ ent.alpha = 0.6f;
+ else
+ ent.alpha = 0.3f;
+ }
+ // pmm
+
+ // add to refresh list
+ V.AddEntity( ent);
+
+ // color shells generate a seperate entity for the main model
+ if ((effects & EF_COLOR_SHELL)!=0) {
+ /*
+ 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
+ if(Developer_searchpath(2) == 2)
+ {
+ 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 & RF_SHELL_HALF_DAM)!=0) {
+ if (FS.Developer_searchpath(2) == 2) {
+ // ditch the half damage shell if any of red, blue, or double are on
+ if ((renderfx & (RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_DOUBLE))!=0)
+ renderfx &= ~RF_SHELL_HALF_DAM;
+ }
+ }
+
+ if ((renderfx & RF_SHELL_DOUBLE)!=0) {
+ if (FS.Developer_searchpath(2) == 2) {
+ // lose the yellow shell if we have a red, blue, or green shell
+ if ((renderfx & (RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_GREEN))!=0)
+ renderfx &= ~RF_SHELL_DOUBLE;
+ // if we have a red shell, turn it to purple by adding blue
+ if ((renderfx & RF_SHELL_RED)!=0)
+ renderfx |= RF_SHELL_BLUE;
+ // if we have a blue shell (and not a red shell), turn it to cyan by adding green
+ else if ((renderfx & RF_SHELL_BLUE)!=0)
+ // go to green if it's on already, otherwise do cyan (flash green)
+ if ((renderfx & RF_SHELL_GREEN)!=0)
+ renderfx &= ~RF_SHELL_BLUE;
+ else
+ renderfx |= RF_SHELL_GREEN;
+ }
+ }
+ // }
+ // pmm
+ ent.flags = renderfx | RF_TRANSLUCENT;
+ ent.alpha = 0.30f;
+ V.AddEntity( ent);
+ }
+
+ ent.skin = null; // never use a custom skin on others
+ ent.skinnum = 0;
+ ent.flags = 0;
+ ent.alpha = 0;
+
+ // duplicate for linked models
+ if (s1.modelindex2!=0) {
+ if (s1.modelindex2 == 255) { // custom weapon
+ ci = cl.clientinfo[s1.skinnum & 0xff];
+ i = (s1.skinnum >> 8); // 0 is default weapon model
+ if (0==cl_vwep.value || i > MAX_CLIENTWEAPONMODELS - 1)
+ i = 0;
+ ent.model = ci.weaponmodel[i];
+ if (null==ent.model) {
+ if (i != 0)
+ ent.model = ci.weaponmodel[0];
+ if (null==ent.model)
+ ent.model = cl.baseclientinfo.weaponmodel[0];
+ }
+ }
+ else
+ ent.model = cl.model_draw[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 (cl.configstrings[CS_MODELS + (s1.modelindex2)].equalsIgnoreCase( "models/items/shell/tris.md2")) {
+ ent.alpha = 0.32f;
+ ent.flags = RF_TRANSLUCENT;
+ }
+ // pmm
+
+ V.AddEntity( ent);
+
+ //PGM - make sure these get reset.
+ ent.flags = 0;
+ ent.alpha = 0;
+ //PGM
+ }
+ if (s1.modelindex3!=0) {
+ ent.model = cl.model_draw[s1.modelindex3];
+ V.AddEntity( ent);
+ }
+ if (s1.modelindex4!=0) {
+ ent.model = cl.model_draw[s1.modelindex4];
+ V.AddEntity( ent);
+ }
+
+ if ((effects & EF_POWERSCREEN)!=0) {
+ ent.model = CL_tent.cl_mod_powerscreen;
+ ent.oldframe = 0;
+ ent.frame = 0;
+ ent.flags |= (RF_TRANSLUCENT | RF_SHELL_GREEN);
+ ent.alpha = 0.30f;
+ V.AddEntity( ent);
+ }
+
+ // add automatic particle trails
+ if ((effects & ~EF_ROTATE)!=0) {
+ if ((effects & EF_ROCKET)!=0) {
+ RocketTrail(cent.lerp_origin, ent.origin, cent);
+ V.AddLight(ent.origin, 200, 1, 1, 0);
+ }
+ // PGM - Do not reorder EF_BLASTER and EF_HYPERBLASTER.
+ // EF_BLASTER | EF_TRACKER is a special case for EF_BLASTER2... Cheese!
+ else if ((effects & EF_BLASTER)!=0) {
+ // CL_BlasterTrail (cent.lerp_origin, ent.origin);
+ // PGM
+ if ((effects & EF_TRACKER)!=0) // lame... problematic?
+ {
+ CL_newfx.BlasterTrail2(cent.lerp_origin, ent.origin);
+ V.AddLight(ent.origin, 200, 0, 1, 0);
+ }
+ else {
+ BlasterTrail(cent.lerp_origin, ent.origin);
+ V.AddLight(ent.origin, 200, 1, 1, 0);
+ }
+ // PGM
+ }
+ else if ((effects & EF_HYPERBLASTER)!=0) {
+ if ((effects & EF_TRACKER)!=0) // PGM overloaded for blaster2.
+ V.AddLight(ent.origin, 200, 0, 1, 0); // PGM
+ else // PGM
+ V.AddLight(ent.origin, 200, 1, 1, 0);
+ }
+ else if ((effects & EF_GIB)!=0) {
+ DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects);
+ }
+ else if ((effects & EF_GRENADE)!=0) {
+ DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects);
+ }
+ else if ((effects & EF_FLIES)!=0) {
+ FlyEffect(cent, ent.origin);
+ }
+ else if ((effects & EF_BFG)!=0) {
+
+
+ if ((effects & EF_ANIM_ALLFAST)!=0) {
+ BfgParticles( ent);
+ i = 200;
+ }
+ else {
+ i = bfg_lightramp[s1.frame];
+ }
+ V.AddLight(ent.origin, i, 0, 1, 0);
+ }
+ // RAFAEL
+ else if ((effects & EF_TRAP)!=0) {
+ ent.origin[2] += 32;
+ TrapParticles( ent);
+ i = (rand() % 100) + 100;
+ V.AddLight(ent.origin, i, 1, 0.8f, 0.1f);
+ }
+ else if ((effects & EF_FLAG1)!=0) {
+ FlagTrail(cent.lerp_origin, ent.origin, 242);
+ V.AddLight(ent.origin, 225, 1, 0.1f, 0.1f);
+ }
+ else if ((effects & EF_FLAG2)!=0) {
+ FlagTrail(cent.lerp_origin, ent.origin, 115);
+ V.AddLight(ent.origin, 225, 0.1f, 0.1f, 1);
+ }
+ // ======
+ // ROGUE
+ else if ((effects & EF_TAGTRAIL)!=0) {
+ CL_newfx.TagTrail(cent.lerp_origin, ent.origin, 220);
+ V.AddLight(ent.origin, 225, 1.0f, 1.0f, 0.0f);
+ }
+ else if ((effects & EF_TRACKERTRAIL)!=0) {
+ if ((effects & EF_TRACKER)!=0) {
+ float intensity;
+
+ intensity = (float) (50 + (500 * (Math.sin(cl.time / 500.0) + 1.0)));
+ // FIXME - check out this effect in rendition
+ if (vidref_val == VIDREF_GL)
+ V.AddLight(ent.origin, intensity, -1.0f, -1.0f, -1.0f);
+ else
+ V.AddLight(ent.origin, -1.0f * intensity, 1.0f, 1.0f, 1.0f);
+ }
+ else {
+ CL_newfx.Tracker_Shell(cent.lerp_origin);
+ V.AddLight(ent.origin, 155, -1.0f, -1.0f, -1.0f);
+ }
+ }
+ else if ((effects & EF_TRACKER)!=0) {
+ CL_newfx.TrackerTrail(cent.lerp_origin, ent.origin, 0);
+ // FIXME - check out this effect in rendition
+ if (vidref_val == VIDREF_GL)
+ V.AddLight(ent.origin, 200, -1, -1, -1);
+ else
+ V.AddLight(ent.origin, -200, 1, 1, 1);
+ }
+ // ROGUE
+ // ======
+ // RAFAEL
+ else if ((effects & EF_GREENGIB)!=0) {
+ DiminishingTrail(cent.lerp_origin, ent.origin, cent, effects);
+ }
+ // RAFAEL
+ else if ((effects & EF_IONRIPPER)!=0) {
+ IonripperTrail(cent.lerp_origin, ent.origin);
+ V.AddLight(ent.origin, 100, 1, 0.5f, 0.5f);
+ }
+ // RAFAEL
+ else if ((effects & EF_BLUEHYPERBLASTER)!=0) {
+ V.AddLight(ent.origin, 200, 0, 0, 1);
+ }
+ // RAFAEL
+ else if ((effects & EF_PLASMA)!=0) {
+ if ((effects & EF_ANIM_ALLFAST)!=0) {
+ BlasterTrail(cent.lerp_origin, ent.origin);
+ }
+ V.AddLight(ent.origin, 130, 1, 0.5f, 0.5f);
+ }
+ }
+
+ VectorCopy(ent.origin, cent.lerp_origin);
+ }
+ }
+
+ /*
+ ==============
+ CL_AddViewWeapon
+ ==============
+ */
+ static void AddViewWeapon(player_state_t ps, player_state_t ops) {
+ entity_t gun; // view model
+ int i;
+
+ // allow the gun to be completely removed
+ if (0==cl_gun.value)
+ return;
+
+ // don't draw gun if in wide angle view
+ if (ps.fov > 90)
+ return;
+
+ //memset( gun, 0, sizeof(gun));
+ gun = new entity_t();
+
+ if (gun_model!=null)
+ gun.model = gun_model; // development tool
+ else
+ gun.model = cl.model_draw[ps.gunindex];
+
+ if (gun.model==null)
+ return;
+
+ // set up gun position
+ for (i = 0; i < 3; i++) {
+ gun.origin[i] = cl.refdef.vieworg[i] + ops.gunoffset[i] + cl.lerpfrac * (ps.gunoffset[i] - ops.gunoffset[i]);
+ gun.angles[i] = cl.refdef.viewangles[i] + LerpAngle(ops.gunangles[i], ps.gunangles[i], cl.lerpfrac);
+ }
+
+ if (gun_frame!=0) {
+ gun.frame = gun_frame; // development tool
+ gun.oldframe = gun_frame; // development tool
+ }
+ else {
+ gun.frame = ps.gunframe;
+ if (gun.frame == 0)
+ gun.oldframe = 0; // just changed weapons, don't lerp from old
+ else
+ gun.oldframe = ops.gunframe;
+ }
+
+ gun.flags = RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL;
+ gun.backlerp = 1.0f - cl.lerpfrac;
+ VectorCopy(gun.origin, gun.oldorigin); // don't lerp at all
+ V.AddEntity( gun);
+ }
+
+ /*
+ ===============
+ CL_CalcViewValues
+
+ Sets cl.refdef view values
+ ===============
+ */
+ static void CalcViewValues() {
+ int i;
+ float lerp, backlerp;
+ centity_t ent;
+ frame_t oldframe;
+ player_state_t ps, ops;
+
+ // find the previous frame to interpolate from
+ ps = cl.frame.playerstate;
+
+ i = (cl.frame.serverframe - 1) & UPDATE_MASK;
+ oldframe = cl.frames[i];
+
+ if (oldframe.serverframe != cl.frame.serverframe - 1 || !oldframe.valid)
+ oldframe = cl.frame; // previous frame was dropped or involid
+ ops = oldframe.playerstate;
+
+ // see if the player entity was teleported this frame
+ if (Math.abs(ops.pmove.origin[0] - ps.pmove.origin[0]) > 256 * 8
+ || Math.abs(ops.pmove.origin[1] - ps.pmove.origin[1]) > 256 * 8
+ || Math.abs(ops.pmove.origin[2] - ps.pmove.origin[2]) > 256 * 8)
+ ops = ps; // don't interpolate
+
+ ent = cl_entities[cl.playernum + 1];
+ lerp = cl.lerpfrac;
+
+ // calculate the origin
+ if ((cl_predict.value!=0) && 0==(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION)) { // use predicted values
+ int delta;
+
+ backlerp = 1.0f - lerp;
+ for (i = 0; i < 3; i++) {
+ cl.refdef.vieworg[i] =
+ cl.predicted_origin[i]
+ + ops.viewoffset[i]
+ + cl.lerpfrac * (ps.viewoffset[i] - ops.viewoffset[i])
+ - backlerp * cl.prediction_error[i];
+ }
+
+ // smooth out stair climbing
+ delta = (int) (cls.realtime - cl.predicted_step_time);
+ if (delta < 100)
+ cl.refdef.vieworg[2] -= cl.predicted_step * (100 - delta) * 0.01;
+ }
+ else { // just use interpolated values
+ for (i = 0; i < 3; i++)
+ cl.refdef.vieworg[i] =
+ ops.pmove.origin[i] * 0.125f
+ + ops.viewoffset[i]
+ + lerp * (ps.pmove.origin[i] * 0.125f + ps.viewoffset[i] - (ops.pmove.origin[i] * 0.125f + ops.viewoffset[i]));
+ }
+
+ // if not running a demo or on a locked frame, add the local angle movement
+ if (cl.frame.playerstate.pmove.pm_type < PM_DEAD) { // use predicted values
+ for (i = 0; i < 3; i++)
+ cl.refdef.viewangles[i] = cl.predicted_angles[i];
+ }
+ else { // just use interpolated values
+ for (i = 0; i < 3; i++)
+ cl.refdef.viewangles[i] = LerpAngle(ops.viewangles[i], ps.viewangles[i], lerp);
+ }
+
+ for (i = 0; i < 3; i++)
+ cl.refdef.viewangles[i] += LerpAngle(ops.kick_angles[i], ps.kick_angles[i], lerp);
+
+ AngleVectors(cl.refdef.viewangles, cl.v_forward, cl.v_right, cl.v_up);
+
+ // interpolate field of view
+ cl.refdef.fov_x = ops.fov + lerp * (ps.fov - ops.fov);
+
+ // don't interpolate blend color
+ for (i = 0; i < 4; i++)
+ cl.refdef.blend[i] = ps.blend[i];
+
+ // add the weapon
+ AddViewWeapon(ps, ops);
+ }
+
+ /*
+ ===============
+ CL_AddEntities
+
+ Emits all entities, particles, and lights to the refresh
+ ===============
+ */
+ static void AddEntities() {
+ if (cls.state != ca_active)
+ return;
+
+ if (cl.time > cl.frame.servertime) {
+ if (cl_showclamp.value!=0)
+ Com.Printf("high clamp " + (cl.time - cl.frame.servertime) + "\n");
+ cl.time = cl.frame.servertime;
+ cl.lerpfrac = 1.0f;
+ }
+ else if (cl.time < cl.frame.servertime - 100) {
+ if (cl_showclamp.value!=0)
+ Com.Printf("low clamp " + (cl.frame.servertime - 100 - cl.time)+"\n");
+ cl.time = cl.frame.servertime - 100;
+ cl.lerpfrac = 0;
+ }
+ else
+ cl.lerpfrac = 1.0f - (cl.frame.servertime - cl.time) * 0.01f;
+
+ if (cl_timedemo.value!=0)
+ cl.lerpfrac = 1.0f;
+
+
+ /* is ok..
+ CL_AddPacketEntities (cl.frame);
+ CL_AddTEnts ();
+ CL_AddParticles ();
+ CL_AddDLights ();
+ CL_AddLightStyles ();
+ */
+
+ CalcViewValues();
+ // PMM - moved this here so the heat beam has the right values for the vieworg, and can lock the beam to the gun
+ AddPacketEntities( cl.frame);
+
+ CL_tent.AddTEnts();
+ AddParticles();
+ CL_fx.AddDLights();
+ AddLightStyles();
+ }
+
+ /*
+ ===============
+ CL_GetEntitySoundOrigin
+
+ Called to get the sound spatialization origin
+ ===============
+ */
+ void GetEntitySoundOrigin(int ent, float[] org) {
+ centity_t old;
+
+ if (ent < 0 || ent >= MAX_EDICTS)
+ Com.Error(ERR_DROP, "CL_GetEntitySoundOrigin: bad ent");
+ old = cl_entities[ent];
+ VectorCopy(old.lerp_origin, org);
+
+ // FIXME: bmodel issues...
+ }
+
+}