1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "client.h"
26
27 /*
28 ===================
29 CL_CheckPredictionError
30 ===================
31 */
CL_CheckPredictionError(void)32 void CL_CheckPredictionError (void)
33 {
34 int frame;
35 int delta[3];
36 int i;
37 int len;
38
39 if (!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
40 return;
41
42 // calculate the last usercmd_t we sent that the server has processed
43 frame = cls.netchan.incoming_acknowledged;
44 frame &= (CMD_BACKUP-1);
45
46 // compare what the server returned with what we had predicted it to be
47 VectorSubtract (cl.frame.playerstate.pmove.origin, cl.predicted_origins[frame], delta);
48
49 // save the prediction error for interpolation
50 len = abs(delta[0]) + abs(delta[1]) + abs(delta[2]);
51 if (len > 640) // 80 world units
52 { // a teleport or something
53 VectorClear (cl.prediction_error);
54 }
55 else
56 {
57 if (cl_showmiss->value && (delta[0] || delta[1] || delta[2]) )
58 { // use sum of absolute differences from above, scaled to quake units
59 Com_Printf("prediction miss: frame %i: %0.3f\n",
60 cl.frame.serverframe, ((float)len)/8.0f );
61 }
62
63 VectorCopy (cl.frame.playerstate.pmove.origin, cl.predicted_origins[frame]);
64
65 // save for error itnerpolation
66 for (i=0 ; i<3 ; i++)
67 cl.prediction_error[i] = delta[i]*0.125;
68 }
69 }
70
71
72 /*
73 ====================
74 CL_ClipMoveToEntities
75
76 ====================
77 */
CL_ClipMoveToEntities(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end,trace_t * tr)78 void CL_ClipMoveToEntities ( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, trace_t *tr )
79 {
80 int i, x, zd, zu;
81 trace_t trace;
82 int headnode;
83 float *angles;
84 entity_state_t *ent;
85 int num;
86 cmodel_t *cmodel;
87 vec3_t bmins, bmaxs;
88
89 for (i=0 ; i<cl.frame.num_entities ; i++)
90 {
91 num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);
92 ent = &cl_parse_entities[num];
93
94 if (!ent->solid)
95 continue;
96
97 if (ent->number == cl.playernum+1)
98 continue;
99
100 if (ent->solid == 31)
101 { // special value for bmodel
102 cmodel = cl.model_clip[ent->modelindex];
103 if (!cmodel)
104 continue;
105 headnode = cmodel->headnode;
106 angles = ent->angles;
107 }
108 else
109 { // encoded bbox
110 x = 8*(ent->solid & 31);
111 zd = 8*((ent->solid>>5) & 31);
112 zu = 8*((ent->solid>>10) & 63) - 32;
113
114 bmins[0] = bmins[1] = -x;
115 bmaxs[0] = bmaxs[1] = x;
116 bmins[2] = -zd;
117 bmaxs[2] = zu;
118
119 headnode = CM_HeadnodeForBox (bmins, bmaxs);
120 angles = vec3_origin; // boxes don't rotate
121 }
122
123 if (tr->allsolid)
124 return;
125
126 trace = CM_TransformedBoxTrace (start, end,
127 mins, maxs, headnode, MASK_PLAYERSOLID,
128 ent->origin, angles);
129
130 if (trace.allsolid || trace.startsolid ||
131 trace.fraction < tr->fraction)
132 {
133 trace.ent = (struct edict_s *)ent;
134 if (tr->startsolid)
135 {
136 *tr = trace;
137 tr->startsolid = true;
138 }
139 else
140 *tr = trace;
141 }
142 else if (trace.startsolid)
143 tr->startsolid = true;
144 }
145 }
146
147
148 /*
149 ================
150 CL_PMTrace
151 ================
152 */
CL_PMTrace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end)153 trace_t CL_PMTrace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
154 {
155 trace_t t;
156
157 // check against world
158 t = CM_BoxTrace (start, end, mins, maxs, 0, MASK_PLAYERSOLID);
159 if (t.fraction < 1.0)
160 t.ent = (struct edict_s *)1;
161
162 // check all other solid models
163 CL_ClipMoveToEntities (start, mins, maxs, end, &t);
164
165 return t;
166 }
167
CL_PMpointcontents(vec3_t point)168 int CL_PMpointcontents (vec3_t point)
169 {
170 int i;
171 entity_state_t *ent;
172 int num;
173 cmodel_t *cmodel;
174 int contents;
175
176 contents = CM_PointContents (point, 0);
177
178 for (i=0 ; i<cl.frame.num_entities ; i++)
179 {
180 num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);
181 ent = &cl_parse_entities[num];
182
183 if (ent->solid != 31) // special value for bmodel
184 continue;
185
186 cmodel = cl.model_clip[ent->modelindex];
187 if (!cmodel)
188 continue;
189
190 contents |= CM_TransformedPointContents (point, cmodel->headnode, ent->origin, ent->angles);
191 }
192
193 return contents;
194 }
195
196 //Knightmare added- this can check using masks, good for checking surface flags
197 // also checks for bmodels
198 /*
199 ================
200 CL_PMSurfaceTrace
201 ================
202 */
CL_PMSurfaceTrace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end,int contentmask)203 trace_t CL_PMSurfaceTrace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int contentmask)
204 {
205 trace_t t;
206
207 if (!mins)
208 mins = vec3_origin;
209 if (!maxs)
210 maxs = vec3_origin;
211
212 // check against world
213 t = CM_BoxTrace (start, end, mins, maxs, 0, contentmask);
214 if (t.fraction < 1.0)
215 t.ent = (struct edict_s *)1;
216
217 // check all other solid models
218 CL_ClipMoveToEntities (start, mins, maxs, end, &t);
219
220 return t;
221 }
222
223
224 /*
225 =================
226 CL_PredictMovement
227
228 Sets cl.predicted_origin and cl.predicted_angles
229 =================
230 */
CL_PredictMovement(void)231 void CL_PredictMovement (void)
232 {
233 int ack, current;
234 int frame;
235 int oldframe;
236 usercmd_t *cmd;
237 pmove_t pm;
238 int i;
239 int step;
240 int oldz;
241
242 if (cls.state != ca_active)
243 return;
244
245 if (cl_paused->value)
246 return;
247
248 if (!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
249 { // just set angles
250 for (i=0 ; i<3 ; i++)
251 {
252 cl.last_predicted_angles[i] = cl.predicted_angles[i] = cl.viewangles[i] + SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[i]);
253 }
254 return;
255 }
256
257 ack = cls.netchan.incoming_acknowledged;
258 current = cls.netchan.outgoing_sequence;
259
260 // if we are too far out of date, just freeze
261 if (current - ack >= CMD_BACKUP)
262 {
263 if (cl_showmiss->value)
264 Com_Printf ("exceeded CMD_BACKUP\n");
265 return;
266 }
267
268 // copy current state to pmove
269 memset (&pm, 0, sizeof(pm));
270 pm.trace = CL_PMTrace;
271 pm.pointcontents = CL_PMpointcontents;
272
273 pm_airaccelerate = atof(cl.configstrings[CS_AIRACCEL]);
274
275 pm.s = cl.frame.playerstate.pmove;
276
277 // SCR_DebugGraph (current - ack - 1, 0);
278
279 frame = 0;
280
281 // run frames
282 while (++ack < current)
283 {
284 frame = ack & (CMD_BACKUP-1);
285 cmd = &cl.cmds[frame];
286
287 pm.cmd = *cmd;
288 Pmove (&pm);
289
290 // save for debug checking
291 VectorCopy (pm.s.origin, cl.predicted_origins[frame]);
292 }
293
294 oldframe = (ack-2) & (CMD_BACKUP-1);
295 oldz = cl.predicted_origins[oldframe][2];
296 step = pm.s.origin[2] - oldz;
297 if (step > 63 && step < 160 && (pm.s.pm_flags & PMF_ON_GROUND) )
298 {
299 cl.predicted_step = step * 0.125;
300 cl.predicted_step_time = cls.realtime - cls.frametime * 500;
301 }
302
303
304 // copy results out for rendering
305 cl.predicted_origin[0] = pm.s.origin[0]*0.125;
306 cl.predicted_origin[1] = pm.s.origin[1]*0.125;
307 cl.predicted_origin[2] = pm.s.origin[2]*0.125;
308
309 cl.predicted_velocity[0] = pm.s.velocity[0]*0.125;
310 cl.predicted_velocity[1] = pm.s.velocity[1]*0.125;
311 cl.predicted_velocity[2] = pm.s.velocity[2]*0.125;
312
313 VectorCopy (pm.viewangles, cl.predicted_angles);
314 VectorCopy (pm.viewangles, cl.last_predicted_angles);
315 }
316
317 /*
318 =================
319 CL_Trace
320 =================
321 */
CL_Trace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end,int skipNumber,int brushMask,qboolean brushOnly,int * entNumber)322 trace_t CL_Trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int skipNumber, int brushMask, qboolean brushOnly, int *entNumber){
323
324 trace_t trace, tmp;
325 entity_state_t *ent;
326 cmodel_t *cmodel;
327 vec3_t bmins, bmaxs;
328 int i, xy, zd, zu, headNode, num;
329
330 // Check against world
331 trace = CM_BoxTrace(start, end, mins, maxs, 0, brushMask);
332 if (trace.fraction < 1.0){
333 if (entNumber)
334 *entNumber = 0;
335
336 trace.ent = (struct edict_s *)1;
337 }
338
339 if (trace.allsolid || trace.fraction == 0.0)
340 return trace;
341
342 // Check all other solid models
343 for (i=0 ; i<cl.frame.num_entities ; i++)
344 {
345 num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);
346 ent = &cl_parse_entities[num];
347
348 if (ent->number == skipNumber)
349 continue;
350
351 if (ent->solid == 31){
352 // Special value for brush model
353 cmodel = cl.model_clip[ent->modelindex];
354 if (!cmodel)
355 continue;
356
357 tmp = CM_TransformedBoxTrace(start, end, mins, maxs, cmodel->headnode, brushMask, ent->origin, ent->angles);
358 }
359 else {
360 if (brushOnly)
361 continue;
362
363 // Encoded bounding box
364 xy = 8 * (ent->solid & 31);
365 zd = 8 * ((ent->solid >> 5) & 31);
366 zu = 8 * ((ent->solid >> 10) & 63) - 32;
367
368 bmins[0] = bmins[1] = -xy;
369 bmaxs[0] = bmaxs[1] = xy;
370 bmins[2] = -zd;
371 bmaxs[2] = zu;
372
373 headNode = CM_HeadnodeForBox (bmins, bmaxs);
374 tmp = CM_TransformedBoxTrace(start, end, mins, maxs, headNode, brushMask, ent->origin, vec3_origin);
375 }
376
377 if (tmp.allsolid || tmp.startsolid || tmp.fraction < trace.fraction){
378 if (entNumber)
379 *entNumber = ent->number;
380
381 tmp.ent = (struct edict_s *)ent;
382 if (trace.startsolid){
383 trace = tmp;
384 trace.startsolid = true;
385 }
386 else
387 trace = tmp;
388 }
389 else if (tmp.startsolid)
390 trace.startsolid = true;
391
392 if (trace.allsolid)
393 break;
394 }
395
396 return trace;
397 }
398
399
400