diff --git a/include/client/entities.h b/include/client/entities.h index 7fb867ca1..ae046b8c4 100644 --- a/include/client/entities.h +++ b/include/client/entities.h @@ -53,10 +53,6 @@ typedef struct entity_state_s { byte glow_size; byte glow_color; byte colormod; - - struct skin_s *skin; //FIXME this should not be here, but better state - //change tracking in the client is needed for this - //to be moved } entity_state_t; typedef struct { diff --git a/include/qw/protocol.h b/include/qw/protocol.h index cfd1c22b9..76f2c625d 100644 --- a/include/qw/protocol.h +++ b/include/qw/protocol.h @@ -306,6 +306,7 @@ #define MAX_DEMO_PACKET_ENTITIES 196 // doesn't count nails typedef struct { int num_entities; + int ent_nums[MAX_DEMO_PACKET_ENTITIES]; entity_state_t *entities; } packet_entities_t; diff --git a/nq/source/cl_ents.c b/nq/source/cl_ents.c index 2d8a17abd..0ac26a263 100644 --- a/nq/source/cl_ents.c +++ b/nq/source/cl_ents.c @@ -362,6 +362,7 @@ CL_RelinkEntities (void) if (cl_forcelink[i]) { // The entity was not updated in the last message so move to the // final spot + ent->pose1 = ent->pose2 = -1; VectorCopy (new->origin, ent->origin); if (!(ent->model->flags & EF_ROTATE)) CL_TransformEntity (ent, new->angles, true); @@ -372,10 +373,10 @@ CL_RelinkEntities (void) } VectorCopy (ent->origin, ent->old_origin); } else { - // If the delta is large, assume a teleport and don't lerp f = frac; VectorCopy (ent->origin, ent->old_origin); VectorSubtract (new->origin, old->origin, delta); + // If the delta is large, assume a teleport and don't lerp if (fabs (delta[0]) > 100 || fabs (delta[1] > 100) || fabs (delta[2]) > 100) { // assume a teleportation, not a motion @@ -419,7 +420,8 @@ CL_RelinkEntities (void) CL_TransformEntity (ent, angles, false); } CL_EntityEffects (i, ent, new); - CL_NewDlight (i, ent->origin, new->effects, 0, 0); + CL_NewDlight (i, ent->origin, new->effects, new->glow_size, + new->glow_color); if (VectorDistance_fast (old->origin, ent->origin) > (256 * 256)) VectorCopy (ent->origin, old->origin); if (ent->model->flags & ~EF_ROTATE) diff --git a/qw/include/client.h b/qw/include/client.h index dd1095d14..4c55ff7b0 100644 --- a/qw/include/client.h +++ b/qw/include/client.h @@ -203,6 +203,8 @@ typedef struct { // accidentally do something the first frame // sentcmds[cl.netchan.outgoing_sequence & UPDATE_MASK] = cmd frame_t frames[UPDATE_BACKUP]; + int link_sequence; + int prev_sequence; // information for local display int stats[MAX_CL_STATS]; // health, etc @@ -340,6 +342,7 @@ extern client_state_t cl; extern entity_t *cl_static_entities; extern entity_t cl_entities[512]; +extern byte cl_entity_valid[2][512]; extern qboolean nomaster; extern char *server_version; // version of server we connected to diff --git a/qw/source/cl_entparse.c b/qw/source/cl_entparse.c index fa006c0d0..ffad98174 100644 --- a/qw/source/cl_entparse.c +++ b/qw/source/cl_entparse.c @@ -103,12 +103,8 @@ CL_ParseDelta (entity_state_t *from, entity_state_t *to, int bits) if (bits & U_FRAME) to->frame = MSG_ReadByte (net_message); - if (bits & U_COLORMAP) { - byte cmap = MSG_ReadByte (net_message); - if (cmap != to->colormap) - to->skin = mod_funcs->Skin_SetColormap (to->skin, cmap); - to->colormap = cmap; - } + if (bits & U_COLORMAP) + to->colormap = MSG_ReadByte (net_message); if (bits & U_SKIN) to->skinnum = MSG_ReadByte (net_message); @@ -193,22 +189,32 @@ FlushEntityPacket (void) } } +static void +copy_state (packet_entities_t *newp, packet_entities_t *oldp, int newindex, + int oldindex, int num) +{ + newp->entities[num] = oldp->entities[num]; + newp->ent_nums[newindex] = num; + cl_entity_valid[0][num] = 1; +} + void CL_ParsePacketEntities (qboolean delta) { - byte from; - int oldindex, newindex, newnum, oldnum, oldpacket, newpacket, word; + byte from; + int oldindex, newindex, newnum, oldnum, oldpacket, word; packet_entities_t *oldp, *newp, dummy; - qboolean full; + qboolean full; - newpacket = cls.netchan.incoming_sequence & UPDATE_MASK; - newp = &cl.frames[newpacket].packet_entities; - cl.frames[newpacket].invalid = false; + cl.prev_sequence = cl.link_sequence; + cl.link_sequence = cls.netchan.incoming_sequence; + newp = &cl.frames[cl.link_sequence & UPDATE_MASK].packet_entities; + cl.frames[cl.link_sequence & UPDATE_MASK].invalid = false; if (delta) { from = MSG_ReadByte (net_message); - oldpacket = cl.frames[newpacket].delta_sequence; + oldpacket = cl.frames[cl.link_sequence & UPDATE_MASK].delta_sequence; if (cls.demoplayback2) from = oldpacket = (cls.netchan.incoming_sequence - 1); if ((from & UPDATE_MASK) != (oldpacket & UPDATE_MASK)) @@ -217,6 +223,8 @@ CL_ParsePacketEntities (qboolean delta) oldpacket = -1; full = false; + //memcpy (cl_entity_valid[1], cl_entity_valid[0], + // sizeof (cl_entity_valid[0])); if (oldpacket != -1) { if (cls.netchan.outgoing_sequence - oldpacket >= UPDATE_BACKUP - 1) { // we can't use this, it is too old @@ -230,12 +238,14 @@ CL_ParsePacketEntities (qboolean delta) dummy.num_entities = 0; cl.validsequence = cls.netchan.incoming_sequence; full = true; + memset (cl_entity_valid[0], 0, sizeof (cl_entity_valid[0])); } oldindex = 0; newindex = 0; newp->num_entities = 0; + newnum = 0; while (1) { word = MSG_ReadShort (net_message); if (net_message->badread) { // something didn't parse right... @@ -248,15 +258,14 @@ CL_ParsePacketEntities (qboolean delta) if (newindex >= MAX_DEMO_PACKET_ENTITIES) Host_Error ("CL_ParsePacketEntities: newindex == " "MAX_DEMO_PACKET_ENTITIES"); - newp->entities[newindex] = oldp->entities[oldindex]; - newindex++; - oldindex++; + oldnum = oldp->ent_nums[oldindex]; + copy_state (newp, oldp, newindex++, oldindex++, oldnum); } break; } newnum = word & 511; oldnum = oldindex >= oldp->num_entities ? 9999 : - oldp->entities[oldindex].number; + oldp->ent_nums[oldindex]; while (newnum > oldnum) { if (full) { @@ -268,15 +277,14 @@ CL_ParsePacketEntities (qboolean delta) if (newindex >= MAX_DEMO_PACKET_ENTITIES) Host_Error ("CL_ParsePacketEntities: newindex == " "MAX_DEMO_PACKET_ENTITIES"); - newp->entities[newindex] = oldp->entities[oldindex]; - newindex++; - oldindex++; + copy_state (newp, oldp, newindex++, oldindex++, oldnum); oldnum = oldindex >= oldp->num_entities ? 9999 : - oldp->entities[oldindex].number; + oldp->ent_nums[oldindex]; } if (newnum < oldnum) { // new from baseline if (word & U_REMOVE) { + cl_entity_valid[0][newnum] = 0; if (full) { cl.validsequence = 0; Sys_Printf ("WARNING: U_REMOVE on full update\n"); @@ -290,8 +298,9 @@ CL_ParsePacketEntities (qboolean delta) Host_Error ("CL_ParsePacketEntities: newindex == " "MAX_DEMO_PACKET_ENTITIES"); CL_ParseDelta (&qw_entstates.baseline[newnum], - &newp->entities[newindex], - word); + &newp->entities[newnum], word); + newp->ent_nums[newindex] = newnum; + cl_entity_valid[0][newnum] = 1; newindex++; continue; } @@ -302,20 +311,20 @@ CL_ParsePacketEntities (qboolean delta) Sys_Printf ("WARNING: delta on full update"); } if (word & U_REMOVE) { // Clear the entity - entity_t *ent = &cl_entities[newnum]; - if (ent->efrag) - r_funcs->R_RemoveEfrags (ent); - memset (ent, 0, sizeof (entity_t)); + cl_entity_valid[0][newnum] = 0; oldindex++; continue; } - CL_ParseDelta (&oldp->entities[oldindex], - &newp->entities[newindex], word); + CL_ParseDelta (&oldp->entities[oldnum], + &newp->entities[newnum], word); + newp->ent_nums[newindex] = newnum; + cl_entity_valid[0][newnum] = 1; newindex++; oldindex++; } } + cl.num_entities = max (cl.num_entities, newnum); newp->num_entities = newindex; } diff --git a/qw/source/cl_ents.c b/qw/source/cl_ents.c index fa9f219c1..c397fde2e 100644 --- a/qw/source/cl_ents.c +++ b/qw/source/cl_ents.c @@ -45,6 +45,7 @@ #include "qw/msg_ucmd.h" #include "qw/bothdefs.h" +#include "chase.h" #include "cl_cam.h" #include "cl_ents.h" #include "cl_main.h" @@ -60,6 +61,7 @@ entity_t cl_player_ents[MAX_CLIENTS]; entity_t cl_flag_ents[MAX_CLIENTS]; entity_t cl_entities[512]; // FIXME: magic number +byte cl_entity_valid[2][512]; void CL_ClearEnts (void) @@ -68,6 +70,7 @@ CL_ClearEnts (void) i = qw_entstates.num_frames * qw_entstates.num_entities; memset (qw_entstates.frame[0], 0, i * sizeof (entity_state_t)); + memset (cl_entity_valid, 0, sizeof (cl_entity_valid)); for (i = 0; i < sizeof (cl_entities) / sizeof (cl_entities[0]); i++) CL_Init_Entity (&cl_entities[i]); for (i = 0; i < sizeof (cl_flag_ents) / sizeof (cl_flag_ents[0]); i++) @@ -232,90 +235,163 @@ CL_ModelEffects (entity_t *ent, int num, int glow_color) r_funcs->particles->R_GlowTrail (ent, glow_color); } +static void +set_entity_model (entity_t *ent, int modelindex) +{ + ent->model = cl.model_precache[modelindex]; + // automatic animation (torches, etc) can be either all together + // or randomized + if (ent->model) { + if (ent->model->synctype == ST_RAND) + ent->syncbase = (float) (rand () & 0x7fff) / 0x7fff; + else + ent->syncbase = 0.0; + } +} + static void CL_LinkPacketEntities (void) { - int pnum; - entity_t *ent; - entity_state_t *s1; - model_t *model; - packet_entities_t *pack; + int i, j, forcelink; + float frac, f; + entity_t *ent; + entity_state_t *new, *old; + vec3_t delta; - pack = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK].packet_entities; - - for (pnum = 0; pnum < pack->num_entities; pnum++) { - s1 = &pack->entities[pnum]; + frac = 1; + for (i = 0; i < 512; i++) { + new = &qw_entstates.frame[cl.link_sequence & UPDATE_MASK][i]; + old = &qw_entstates.frame[cl.prev_sequence & UPDATE_MASK][i]; + ent = &cl_entities[i]; + forcelink = cl_entity_valid[0][i] != cl_entity_valid[1][i]; + cl_entity_valid[1][i] = cl_entity_valid[0][i]; + // if the object wasn't included in the last packet, remove it + if (!cl_entity_valid[0][i]) { + ent->model = NULL; + ent->pose1 = ent->pose2 = -1; + if (ent->efrag) + r_funcs->R_RemoveEfrags (ent); // just became empty + continue; + } // spawn light flashes, even ones coming from invisible objects - CL_NewDlight (s1->number, s1->origin, s1->effects, s1->glow_size, - s1->glow_color); - - ent = &cl_entities[s1->number]; + CL_NewDlight (i, new->origin, new->effects, new->glow_size, + new->glow_color); // if set to invisible, skip - if (!s1->modelindex - || (cl_deadbodyfilter->int_val && is_dead_body (s1)) - || (cl_gibfilter->int_val && is_gib (s1))) { + if (!new->modelindex + || (cl_deadbodyfilter->int_val && is_dead_body (new)) + || (cl_gibfilter->int_val && is_gib (new))) { if (ent->efrag) r_funcs->R_RemoveEfrags (ent); continue; } - ent->model = model = cl.model_precache[s1->modelindex]; + if (forcelink) + *old = *new; + + if (forcelink || new->modelindex != old->modelindex) { + old->modelindex = new->modelindex; + set_entity_model (ent, new->modelindex); + } + ent->frame = new->frame; + if (forcelink || new->colormap != old->colormap + || new->skinnum != old->skinnum) { + old->skinnum = new->skinnum; + ent->skinnum = new->skinnum; + old->colormap = new->colormap; + if (new->colormap && (new->colormap <= MAX_CLIENTS) + && cl.players[new->colormap - 1].name + && cl.players[new->colormap - 1].name->value[0]) { + player_info_t *player = &cl.players[new->colormap - 1]; + ent->skin = mod_funcs->Skin_SetSkin (ent->skin, new->colormap, + player->skinname->value); + ent->skin = mod_funcs->Skin_SetColormap (ent->skin, + new->colormap); + } else { + ent->skin = mod_funcs->Skin_SetColormap (ent->skin, 0); + } + } + ent->scale = new->scale / 16.0; + + VectorCopy (ent_colormod[new->colormod], ent->colormod); + ent->colormod[3] = new->alpha / 255.0; ent->min_light = 0; ent->fullbright = 0; - - if (s1->modelindex == cl_playerindex) { + if (new->modelindex == cl_playerindex) { ent->min_light = min (cl.fbskins, cl_fb_players->value); if (ent->min_light >= 1.0) ent->fullbright = 1; } - // set colormap - ent->skin = s1->skin; - - VectorCopy (ent_colormod[s1->colormod], ent->colormod); - ent->colormod[3] = s1->alpha / 255.0; - ent->scale = s1->scale / 16.0; - // Ender: Extend (Colormod) [QSG - End] - - // set skin - ent->skinnum = s1->skinnum; - - // set frame - ent->frame = s1->frame; - - if (!ent->efrag) { + if (forcelink) { ent->pose1 = ent->pose2 = -1; - - // No trail if new this frame - VectorCopy (s1->origin, ent->origin); + VectorCopy (new->origin, ent->origin); + if (!(ent->model->flags & EF_ROTATE)) + CL_TransformEntity (ent, new->angles, true); + if (i != cl.viewentity || chase_active->int_val) { + if (ent->efrag) + r_funcs->R_RemoveEfrags (ent); + r_funcs->R_AddEfrags (ent); + } VectorCopy (ent->origin, ent->old_origin); } else { + f = frac; VectorCopy (ent->origin, ent->old_origin); - VectorCopy (s1->origin, ent->origin); - if (!VectorCompare (ent->origin, ent->old_origin)) { - // the entity moved, it must be relinked - r_funcs->R_RemoveEfrags (ent); + VectorSubtract (new->origin, old->origin, delta); + // If the delta is large, assume a teleport and don't lerp + if (fabs (delta[0]) > 100 || fabs (delta[1] > 100) + || fabs (delta[2]) > 100) { + // assume a teleportation, not a motion + VectorCopy (new->origin, ent->origin); + if (!(ent->model->flags & EF_ROTATE)) + CL_TransformEntity (ent, new->angles, true); + ent->pose1 = ent->pose2 = -1; + } else { + vec3_t angles, d; + // interpolate the origin and angles + VectorMultAdd (old->origin, f, delta, ent->origin); + if (!(ent->model->flags & EF_ROTATE)) { + VectorSubtract (new->angles, old->angles, d); + for (j = 0; j < 3; j++) { + if (d[j] > 180) + d[j] -= 360; + else if (d[j] < -180) + d[j] += 360; + } + VectorMultAdd (old->angles, f, d, angles); + CL_TransformEntity (ent, angles, false); + } + } + if (i != cl.viewentity || chase_active->int_val) { + if (ent->efrag) { + if (!VectorCompare (ent->origin, ent->old_origin)) { + r_funcs->R_RemoveEfrags (ent); + r_funcs->R_AddEfrags (ent); + } + } else { + r_funcs->R_AddEfrags (ent); + } } } if (!ent->efrag) r_funcs->R_AddEfrags (ent); // rotate binary objects locally - if (model->flags & EF_ROTATE) { + if (ent->model->flags & EF_ROTATE) { vec3_t angles; angles[PITCH] = 0; angles[YAW] = anglemod (100 * cl.time); angles[ROLL] = 0; CL_TransformEntity (ent, angles, false); - } else { - CL_TransformEntity (ent, s1->angles, false); } - - if (model->flags & ~EF_ROTATE) - CL_ModelEffects (ent, -s1->number, s1->glow_color); + //CL_EntityEffects (i, ent, new); + //CL_NewDlight (i, ent->origin, new->effects, 0, 0); + if (VectorDistance_fast (old->origin, ent->origin) > (256 * 256)) + VectorCopy (ent->origin, old->origin); + if (ent->model->flags & ~EF_ROTATE) + CL_ModelEffects (ent, -new->number, new->glow_color); } }