From 0023db327bc1db00068284b70e1db45857aeee35 Mon Sep 17 00:00:00 2001 From: Travis Bradshaw Date: Tue, 31 Jan 2012 14:32:09 -0600 Subject: The Quake sources as originally release under the GPL license on December 21, 1999 --- QW/client/cl_ents.c | 1083 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1083 insertions(+) create mode 100644 QW/client/cl_ents.c (limited to 'QW/client/cl_ents.c') diff --git a/QW/client/cl_ents.c b/QW/client/cl_ents.c new file mode 100644 index 0000000..80d28c4 --- /dev/null +++ b/QW/client/cl_ents.c @@ -0,0 +1,1083 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// cl_ents.c -- entity parsing and management + +#include "quakedef.h" + +extern cvar_t cl_predict_players; +extern cvar_t cl_predict_players2; +extern cvar_t cl_solid_players; + +static struct predicted_player { + int flags; + qboolean active; + vec3_t origin; // predicted origin +} predicted_players[MAX_CLIENTS]; + +//============================================================ + +/* +=============== +CL_AllocDlight + +=============== +*/ +dlight_t *CL_AllocDlight (int key) +{ + int i; + dlight_t *dl; + +// first look for an exact key match + if (key) + { + dl = cl_dlights; + for (i=0 ; ikey == key) + { + memset (dl, 0, sizeof(*dl)); + dl->key = key; + return dl; + } + } + } + +// then look for anything else + dl = cl_dlights; + for (i=0 ; idie < cl.time) + { + memset (dl, 0, sizeof(*dl)); + dl->key = key; + return dl; + } + } + + dl = &cl_dlights[0]; + memset (dl, 0, sizeof(*dl)); + dl->key = key; + return dl; +} + +/* +=============== +CL_NewDlight +=============== +*/ +void CL_NewDlight (int key, float x, float y, float z, float radius, float time, + int type) +{ + dlight_t *dl; + + dl = CL_AllocDlight (key); + dl->origin[0] = x; + dl->origin[1] = y; + dl->origin[2] = z; + dl->radius = radius; + dl->die = cl.time + time; + if (type == 0) { + dl->color[0] = 0.2; + dl->color[1] = 0.1; + dl->color[2] = 0.05; + dl->color[3] = 0.7; + } else if (type == 1) { + dl->color[0] = 0.05; + dl->color[1] = 0.05; + dl->color[2] = 0.3; + dl->color[3] = 0.7; + } else if (type == 2) { + dl->color[0] = 0.5; + dl->color[1] = 0.05; + dl->color[2] = 0.05; + dl->color[3] = 0.7; + } else if (type == 3) { + dl->color[0]=0.5; + dl->color[1] = 0.05; + dl->color[2] = 0.4; + dl->color[3] = 0.7; + } +} + + +/* +=============== +CL_DecayLights + +=============== +*/ +void CL_DecayLights (void) +{ + int i; + dlight_t *dl; + + dl = cl_dlights; + for (i=0 ; idie < cl.time || !dl->radius) + continue; + + dl->radius -= host_frametime*dl->decay; + if (dl->radius < 0) + dl->radius = 0; + } +} + + +/* +========================================================================= + +PACKET ENTITY PARSING / LINKING + +========================================================================= +*/ + +/* +================== +CL_ParseDelta + +Can go from either a baseline or a previous packet_entity +================== +*/ +int bitcounts[32]; /// just for protocol profiling +void CL_ParseDelta (entity_state_t *from, entity_state_t *to, int bits) +{ + int i; + + // set everything to the state we are delta'ing from + *to = *from; + + to->number = bits & 511; + bits &= ~511; + + if (bits & U_MOREBITS) + { // read in the low order bits + i = MSG_ReadByte (); + bits |= i; + } + + // count the bits for net profiling + for (i=0 ; i<16 ; i++) + if (bits&(1<flags = bits; + + if (bits & U_MODEL) + to->modelindex = MSG_ReadByte (); + + if (bits & U_FRAME) + to->frame = MSG_ReadByte (); + + if (bits & U_COLORMAP) + to->colormap = MSG_ReadByte(); + + if (bits & U_SKIN) + to->skinnum = MSG_ReadByte(); + + if (bits & U_EFFECTS) + to->effects = MSG_ReadByte(); + + if (bits & U_ORIGIN1) + to->origin[0] = MSG_ReadCoord (); + + if (bits & U_ANGLE1) + to->angles[0] = MSG_ReadAngle(); + + if (bits & U_ORIGIN2) + to->origin[1] = MSG_ReadCoord (); + + if (bits & U_ANGLE2) + to->angles[1] = MSG_ReadAngle(); + + if (bits & U_ORIGIN3) + to->origin[2] = MSG_ReadCoord (); + + if (bits & U_ANGLE3) + to->angles[2] = MSG_ReadAngle(); + + if (bits & U_SOLID) + { + // FIXME + } +} + + +/* +================= +FlushEntityPacket +================= +*/ +void FlushEntityPacket (void) +{ + int word; + entity_state_t olde, newe; + + Con_DPrintf ("FlushEntityPacket\n"); + + memset (&olde, 0, sizeof(olde)); + + cl.validsequence = 0; // can't render a frame + cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].invalid = true; + + // read it all, but ignore it + while (1) + { + word = (unsigned short)MSG_ReadShort (); + if (msg_badread) + { // something didn't parse right... + Host_EndGame ("msg_badread in packetentities"); + return; + } + + if (!word) + break; // done + + CL_ParseDelta (&olde, &newe, word); + } +} + +/* +================== +CL_ParsePacketEntities + +An svc_packetentities has just been parsed, deal with the +rest of the data stream. +================== +*/ +void CL_ParsePacketEntities (qboolean delta) +{ + int oldpacket, newpacket; + packet_entities_t *oldp, *newp, dummy; + int oldindex, newindex; + int word, newnum, oldnum; + qboolean full; + byte from; + + newpacket = cls.netchan.incoming_sequence&UPDATE_MASK; + newp = &cl.frames[newpacket].packet_entities; + cl.frames[newpacket].invalid = false; + + if (delta) + { + from = MSG_ReadByte (); + + oldpacket = cl.frames[newpacket].delta_sequence; + + if ( (from&UPDATE_MASK) != (oldpacket&UPDATE_MASK) ) + Con_DPrintf ("WARNING: from mismatch\n"); + } + else + oldpacket = -1; + + full = false; + if (oldpacket != -1) + { + if (cls.netchan.outgoing_sequence - oldpacket >= UPDATE_BACKUP-1) + { // we can't use this, it is too old + FlushEntityPacket (); + return; + } + cl.validsequence = cls.netchan.incoming_sequence; + oldp = &cl.frames[oldpacket&UPDATE_MASK].packet_entities; + } + else + { // this is a full update that we can start delta compressing from now + oldp = &dummy; + dummy.num_entities = 0; + cl.validsequence = cls.netchan.incoming_sequence; + full = true; + } + + oldindex = 0; + newindex = 0; + newp->num_entities = 0; + + while (1) + { + word = (unsigned short)MSG_ReadShort (); + if (msg_badread) + { // something didn't parse right... + Host_EndGame ("msg_badread in packetentities"); + return; + } + + if (!word) + { + while (oldindex < oldp->num_entities) + { // copy all the rest of the entities from the old packet +//Con_Printf ("copy %i\n", oldp->entities[oldindex].number); + if (newindex >= MAX_PACKET_ENTITIES) + Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); + newp->entities[newindex] = oldp->entities[oldindex]; + newindex++; + oldindex++; + } + break; + } + newnum = word&511; + oldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number; + + while (newnum > oldnum) + { + if (full) + { + Con_Printf ("WARNING: oldcopy on full update"); + FlushEntityPacket (); + return; + } + +//Con_Printf ("copy %i\n", oldnum); + // copy one of the old entities over to the new packet unchanged + if (newindex >= MAX_PACKET_ENTITIES) + Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); + newp->entities[newindex] = oldp->entities[oldindex]; + newindex++; + oldindex++; + oldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number; + } + + if (newnum < oldnum) + { // new from baseline +//Con_Printf ("baseline %i\n", newnum); + if (word & U_REMOVE) + { + if (full) + { + cl.validsequence = 0; + Con_Printf ("WARNING: U_REMOVE on full update\n"); + FlushEntityPacket (); + return; + } + continue; + } + if (newindex >= MAX_PACKET_ENTITIES) + Host_EndGame ("CL_ParsePacketEntities: newindex == MAX_PACKET_ENTITIES"); + CL_ParseDelta (&cl_baselines[newnum], &newp->entities[newindex], word); + newindex++; + continue; + } + + if (newnum == oldnum) + { // delta from previous + if (full) + { + cl.validsequence = 0; + Con_Printf ("WARNING: delta on full update"); + } + if (word & U_REMOVE) + { + oldindex++; + continue; + } +//Con_Printf ("delta %i\n",newnum); + CL_ParseDelta (&oldp->entities[oldindex], &newp->entities[newindex], word); + newindex++; + oldindex++; + } + + } + + newp->num_entities = newindex; +} + + +/* +=============== +CL_LinkPacketEntities + +=============== +*/ +void CL_LinkPacketEntities (void) +{ + entity_t *ent; + packet_entities_t *pack; + entity_state_t *s1, *s2; + float f; + model_t *model; + vec3_t old_origin; + float autorotate; + int i; + int pnum; + dlight_t *dl; + + pack = &cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities; + + autorotate = anglemod(100*cl.time); + + f = 0; // FIXME: no interpolation right now + + for (pnum=0 ; pnumnum_entities ; pnum++) + { + s1 = &pack->entities[pnum]; + s2 = s1; // FIXME: no interpolation right now + + // spawn light flashes, even ones coming from invisible objects + if ((s1->effects & (EF_BLUE | EF_RED)) == (EF_BLUE | EF_RED)) + CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 3); + else if (s1->effects & EF_BLUE) + CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 1); + else if (s1->effects & EF_RED) + CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 2); + else if (s1->effects & EF_BRIGHTLIGHT) + CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2] + 16, 400 + (rand()&31), 0.1, 0); + else if (s1->effects & EF_DIMLIGHT) + CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand()&31), 0.1, 0); + + // if set to invisible, skip + if (!s1->modelindex) + continue; + + // create a new entity + if (cl_numvisedicts == MAX_VISEDICTS) + break; // object list is full + + ent = &cl_visedicts[cl_numvisedicts]; + cl_numvisedicts++; + + ent->keynum = s1->number; + ent->model = model = cl.model_precache[s1->modelindex]; + + // set colormap + if (s1->colormap && (s1->colormap < MAX_CLIENTS) + && !strcmp(ent->model->name,"progs/player.mdl") ) + { + ent->colormap = cl.players[s1->colormap-1].translations; + ent->scoreboard = &cl.players[s1->colormap-1]; + } + else + { + ent->colormap = vid.colormap; + ent->scoreboard = NULL; + } + + // set skin + ent->skinnum = s1->skinnum; + + // set frame + ent->frame = s1->frame; + + // rotate binary objects locally + if (model->flags & EF_ROTATE) + { + ent->angles[0] = 0; + ent->angles[1] = autorotate; + ent->angles[2] = 0; + } + else + { + float a1, a2; + + for (i=0 ; i<3 ; i++) + { + a1 = s1->angles[i]; + a2 = s2->angles[i]; + if (a1 - a2 > 180) + a1 -= 360; + if (a1 - a2 < -180) + a1 += 360; + ent->angles[i] = a2 + f * (a1 - a2); + } + } + + // calculate origin + for (i=0 ; i<3 ; i++) + ent->origin[i] = s2->origin[i] + + f * (s1->origin[i] - s2->origin[i]); + + // add automatic particle trails + if (!model->flags) + continue; + + // scan the old entity display list for a matching + for (i=0 ; ikeynum) + { + VectorCopy (cl_oldvisedicts[i].origin, old_origin); + break; + } + } + if (i == cl_oldnumvisedicts) + continue; // not in last message + + for (i=0 ; i<3 ; i++) + if ( abs(old_origin[i] - ent->origin[i]) > 128) + { // no trail if too far + VectorCopy (ent->origin, old_origin); + break; + } + if (model->flags & EF_ROCKET) + { + R_RocketTrail (old_origin, ent->origin, 0); + dl = CL_AllocDlight (s1->number); + VectorCopy (ent->origin, dl->origin); + dl->radius = 200; + dl->die = cl.time + 0.1; + } + else if (model->flags & EF_GRENADE) + R_RocketTrail (old_origin, ent->origin, 1); + else if (model->flags & EF_GIB) + R_RocketTrail (old_origin, ent->origin, 2); + else if (model->flags & EF_ZOMGIB) + R_RocketTrail (old_origin, ent->origin, 4); + else if (model->flags & EF_TRACER) + R_RocketTrail (old_origin, ent->origin, 3); + else if (model->flags & EF_TRACER2) + R_RocketTrail (old_origin, ent->origin, 5); + else if (model->flags & EF_TRACER3) + R_RocketTrail (old_origin, ent->origin, 6); + } +} + + +/* +========================================================================= + +PROJECTILE PARSING / LINKING + +========================================================================= +*/ + +typedef struct +{ + int modelindex; + vec3_t origin; + vec3_t angles; +} projectile_t; + +#define MAX_PROJECTILES 32 +projectile_t cl_projectiles[MAX_PROJECTILES]; +int cl_num_projectiles; + +extern int cl_spikeindex; + +void CL_ClearProjectiles (void) +{ + cl_num_projectiles = 0; +} + +/* +===================== +CL_ParseProjectiles + +Nails are passed as efficient temporary entities +===================== +*/ +void CL_ParseProjectiles (void) +{ + int i, c, j; + byte bits[6]; + projectile_t *pr; + + c = MSG_ReadByte (); + for (i=0 ; imodelindex = cl_spikeindex; + pr->origin[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096; + pr->origin[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096; + pr->origin[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096; + pr->angles[0] = 360*(bits[4]>>4)/16; + pr->angles[1] = 360*bits[5]/256; + } +} + +/* +============= +CL_LinkProjectiles + +============= +*/ +void CL_LinkProjectiles (void) +{ + int i; + projectile_t *pr; + entity_t *ent; + + for (i=0, pr=cl_projectiles ; ikeynum = 0; + + if (pr->modelindex < 1) + continue; + ent->model = cl.model_precache[pr->modelindex]; + ent->skinnum = 0; + ent->frame = 0; + ent->colormap = vid.colormap; + ent->scoreboard = NULL; + VectorCopy (pr->origin, ent->origin); + VectorCopy (pr->angles, ent->angles); + } +} + +//======================================== + +extern int cl_spikeindex, cl_playerindex, cl_flagindex; + +entity_t *CL_NewTempEntity (void); + +/* +=================== +CL_ParsePlayerinfo +=================== +*/ +extern int parsecountmod; +extern double parsecounttime; +void CL_ParsePlayerinfo (void) +{ + int msec; + int flags; + player_info_t *info; + player_state_t *state; + int num; + int i; + + num = MSG_ReadByte (); + if (num > MAX_CLIENTS) + Sys_Error ("CL_ParsePlayerinfo: bad num"); + + info = &cl.players[num]; + + state = &cl.frames[parsecountmod].playerstate[num]; + + flags = state->flags = MSG_ReadShort (); + + state->messagenum = cl.parsecount; + state->origin[0] = MSG_ReadCoord (); + state->origin[1] = MSG_ReadCoord (); + state->origin[2] = MSG_ReadCoord (); + + state->frame = MSG_ReadByte (); + + // the other player's last move was likely some time + // before the packet was sent out, so accurately track + // the exact time it was valid at + if (flags & PF_MSEC) + { + msec = MSG_ReadByte (); + state->state_time = parsecounttime - msec*0.001; + } + else + state->state_time = parsecounttime; + + if (flags & PF_COMMAND) + MSG_ReadDeltaUsercmd (&nullcmd, &state->command); + + for (i=0 ; i<3 ; i++) + { + if (flags & (PF_VELOCITY1<velocity[i] = MSG_ReadShort(); + else + state->velocity[i] = 0; + } + if (flags & PF_MODEL) + state->modelindex = MSG_ReadByte (); + else + state->modelindex = cl_playerindex; + + if (flags & PF_SKINNUM) + state->skinnum = MSG_ReadByte (); + else + state->skinnum = 0; + + if (flags & PF_EFFECTS) + state->effects = MSG_ReadByte (); + else + state->effects = 0; + + if (flags & PF_WEAPONFRAME) + state->weaponframe = MSG_ReadByte (); + else + state->weaponframe = 0; + + VectorCopy (state->command.angles, state->viewangles); +} + + +/* +================ +CL_AddFlagModels + +Called when the CTF flags are set +================ +*/ +void CL_AddFlagModels (entity_t *ent, int team) +{ + int i; + float f; + vec3_t v_forward, v_right, v_up; + entity_t *newent; + + if (cl_flagindex == -1) + return; + + f = 14; + if (ent->frame >= 29 && ent->frame <= 40) { + if (ent->frame >= 29 && ent->frame <= 34) { //axpain + if (ent->frame == 29) f = f + 2; + else if (ent->frame == 30) f = f + 8; + else if (ent->frame == 31) f = f + 12; + else if (ent->frame == 32) f = f + 11; + else if (ent->frame == 33) f = f + 10; + else if (ent->frame == 34) f = f + 4; + } else if (ent->frame >= 35 && ent->frame <= 40) { // pain + if (ent->frame == 35) f = f + 2; + else if (ent->frame == 36) f = f + 10; + else if (ent->frame == 37) f = f + 10; + else if (ent->frame == 38) f = f + 8; + else if (ent->frame == 39) f = f + 4; + else if (ent->frame == 40) f = f + 2; + } + } else if (ent->frame >= 103 && ent->frame <= 118) { + if (ent->frame >= 103 && ent->frame <= 104) f = f + 6; //nailattack + else if (ent->frame >= 105 && ent->frame <= 106) f = f + 6; //light + else if (ent->frame >= 107 && ent->frame <= 112) f = f + 7; //rocketattack + else if (ent->frame >= 112 && ent->frame <= 118) f = f + 7; //shotattack + } + + newent = CL_NewTempEntity (); + newent->model = cl.model_precache[cl_flagindex]; + newent->skinnum = team; + + AngleVectors (ent->angles, v_forward, v_right, v_up); + v_forward[2] = -v_forward[2]; // reverse z component + for (i=0 ; i<3 ; i++) + newent->origin[i] = ent->origin[i] - f*v_forward[i] + 22*v_right[i]; + newent->origin[2] -= 16; + + VectorCopy (ent->angles, newent->angles) + newent->angles[2] -= 45; +} + +/* +============= +CL_LinkPlayers + +Create visible entities in the correct position +for all current players +============= +*/ +void CL_LinkPlayers (void) +{ + int j; + player_info_t *info; + player_state_t *state; + player_state_t exact; + double playertime; + entity_t *ent; + int msec; + frame_t *frame; + int oldphysent; + + playertime = realtime - cls.latency + 0.02; + if (playertime > realtime) + playertime = realtime; + + frame = &cl.frames[cl.parsecount&UPDATE_MASK]; + + for (j=0, info=cl.players, state=frame->playerstate ; j < MAX_CLIENTS + ; j++, info++, state++) + { + if (state->messagenum != cl.parsecount) + continue; // not present this frame + + // spawn light flashes, even ones coming from invisible objects +#ifdef GLQUAKE + if (!gl_flashblend.value || j != cl.playernum) { +#endif + if ((state->effects & (EF_BLUE | EF_RED)) == (EF_BLUE | EF_RED)) + CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 3); + else if (state->effects & EF_BLUE) + CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 1); + else if (state->effects & EF_RED) + CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 2); + else if (state->effects & EF_BRIGHTLIGHT) + CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2] + 16, 400 + (rand()&31), 0.1, 0); + else if (state->effects & EF_DIMLIGHT) + CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand()&31), 0.1, 0); +#ifdef GLQUAKE + } +#endif + + // the player object never gets added + if (j == cl.playernum) + continue; + + if (!state->modelindex) + continue; + + if (!Cam_DrawPlayer(j)) + continue; + + // grab an entity to fill in + if (cl_numvisedicts == MAX_VISEDICTS) + break; // object list is full + ent = &cl_visedicts[cl_numvisedicts]; + cl_numvisedicts++; + ent->keynum = 0; + + ent->model = cl.model_precache[state->modelindex]; + ent->skinnum = state->skinnum; + ent->frame = state->frame; + ent->colormap = info->translations; + if (state->modelindex == cl_playerindex) + ent->scoreboard = info; // use custom skin + else + ent->scoreboard = NULL; + + // + // angles + // + ent->angles[PITCH] = -state->viewangles[PITCH]/3; + ent->angles[YAW] = state->viewangles[YAW]; + ent->angles[ROLL] = 0; + ent->angles[ROLL] = V_CalcRoll (ent->angles, state->velocity)*4; + + // only predict half the move to minimize overruns + msec = 500*(playertime - state->state_time); + if (msec <= 0 || (!cl_predict_players.value && !cl_predict_players2.value)) + { + VectorCopy (state->origin, ent->origin); +//Con_DPrintf ("nopredict\n"); + } + else + { + // predict players movement + if (msec > 255) + msec = 255; + state->command.msec = msec; +//Con_DPrintf ("predict: %i\n", msec); + + oldphysent = pmove.numphysent; + CL_SetSolidPlayers (j); + CL_PredictUsercmd (state, &exact, &state->command, false); + pmove.numphysent = oldphysent; + VectorCopy (exact.origin, ent->origin); + } + + if (state->effects & EF_FLAG1) + CL_AddFlagModels (ent, 0); + else if (state->effects & EF_FLAG2) + CL_AddFlagModels (ent, 1); + + } +} + +//====================================================================== + +/* +=============== +CL_SetSolid + +Builds all the pmove physents for the current frame +=============== +*/ +void CL_SetSolidEntities (void) +{ + int i; + frame_t *frame; + packet_entities_t *pak; + entity_state_t *state; + + pmove.physents[0].model = cl.worldmodel; + VectorCopy (vec3_origin, pmove.physents[0].origin); + pmove.physents[0].info = 0; + pmove.numphysent = 1; + + frame = &cl.frames[parsecountmod]; + pak = &frame->packet_entities; + + for (i=0 ; inum_entities ; i++) + { + state = &pak->entities[i]; + + if (!state->modelindex) + continue; + if (!cl.model_precache[state->modelindex]) + continue; + if ( cl.model_precache[state->modelindex]->hulls[1].firstclipnode + || cl.model_precache[state->modelindex]->clipbox ) + { + pmove.physents[pmove.numphysent].model = cl.model_precache[state->modelindex]; + VectorCopy (state->origin, pmove.physents[pmove.numphysent].origin); + pmove.numphysent++; + } + } + +} + +/* +=== +Calculate the new position of players, without other player clipping + +We do this to set up real player prediction. +Players are predicted twice, first without clipping other players, +then with clipping against them. +This sets up the first phase. +=== +*/ +void CL_SetUpPlayerPrediction(qboolean dopred) +{ + int j; + player_state_t *state; + player_state_t exact; + double playertime; + int msec; + frame_t *frame; + struct predicted_player *pplayer; + + playertime = realtime - cls.latency + 0.02; + if (playertime > realtime) + playertime = realtime; + + frame = &cl.frames[cl.parsecount&UPDATE_MASK]; + + for (j=0, pplayer = predicted_players, state=frame->playerstate; + j < MAX_CLIENTS; + j++, pplayer++, state++) { + + pplayer->active = false; + + if (state->messagenum != cl.parsecount) + continue; // not present this frame + + if (!state->modelindex) + continue; + + pplayer->active = true; + pplayer->flags = state->flags; + + // note that the local player is special, since he moves locally + // we use his last predicted postition + if (j == cl.playernum) { + VectorCopy(cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].playerstate[cl.playernum].origin, + pplayer->origin); + } else { + // only predict half the move to minimize overruns + msec = 500*(playertime - state->state_time); + if (msec <= 0 || + (!cl_predict_players.value && !cl_predict_players2.value) || + !dopred) + { + VectorCopy (state->origin, pplayer->origin); + //Con_DPrintf ("nopredict\n"); + } + else + { + // predict players movement + if (msec > 255) + msec = 255; + state->command.msec = msec; + //Con_DPrintf ("predict: %i\n", msec); + + CL_PredictUsercmd (state, &exact, &state->command, false); + VectorCopy (exact.origin, pplayer->origin); + } + } + } +} + +/* +=============== +CL_SetSolid + +Builds all the pmove physents for the current frame +Note that CL_SetUpPlayerPrediction() must be called first! +pmove must be setup with world and solid entity hulls before calling +(via CL_PredictMove) +=============== +*/ +void CL_SetSolidPlayers (int playernum) +{ + int j; + extern vec3_t player_mins; + extern vec3_t player_maxs; + struct predicted_player *pplayer; + physent_t *pent; + + if (!cl_solid_players.value) + return; + + pent = pmove.physents + pmove.numphysent; + + for (j=0, pplayer = predicted_players; j < MAX_CLIENTS; j++, pplayer++) { + + if (!pplayer->active) + continue; // not present this frame + + // the player object never gets added + if (j == playernum) + continue; + + if (pplayer->flags & PF_DEAD) + continue; // dead players aren't solid + + pent->model = 0; + VectorCopy(pplayer->origin, pent->origin); + VectorCopy(player_mins, pent->mins); + VectorCopy(player_maxs, pent->maxs); + pmove.numphysent++; + pent++; + } +} + + +/* +=============== +CL_EmitEntities + +Builds the visedicts array for cl.time + +Made up of: clients, packet_entities, nails, and tents +=============== +*/ +void CL_EmitEntities (void) +{ + if (cls.state != ca_active) + return; + if (!cl.validsequence) + return; + + cl_oldnumvisedicts = cl_numvisedicts; + cl_oldvisedicts = cl_visedicts_list[(cls.netchan.incoming_sequence-1)&1]; + cl_visedicts = cl_visedicts_list[cls.netchan.incoming_sequence&1]; + + cl_numvisedicts = 0; + + CL_LinkPlayers (); + CL_LinkPacketEntities (); + CL_LinkProjectiles (); + CL_UpdateTEnts (); +} + -- cgit v1.2.3