1 /*
2 Copyright (C) 1996-1997 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 // sv_phys.c
21
22 #include "quakedef.h"
23 #include "prvm_cmds.h"
24
25 /*
26
27
28 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
29
30 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
31
32 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
33 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
34 corpses are SOLID_NOT and MOVETYPE_TOSS
35 crates are SOLID_BBOX and MOVETYPE_TOSS
36 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
37 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38
39 solid_edge items only clip against bsp models.
40
41 */
42
43 #define MOVE_EPSILON 0.01
44
45 void SV_Physics_Toss (prvm_edict_t *ent);
46
SV_GetPitchSign(prvm_prog_t * prog,prvm_edict_t * ent)47 int SV_GetPitchSign(prvm_prog_t *prog, prvm_edict_t *ent)
48 {
49 dp_model_t *model;
50 if (
51 (model = SV_GetModelFromEdict(ent))
52 ?
53 model->type == mod_alias
54 :
55 (
56 (((unsigned char)PRVM_serveredictfloat(ent, pflags)) & PFLAGS_FULLDYNAMIC)
57 ||
58 ((gamemode == GAME_TENEBRAE) && ((unsigned int)PRVM_serveredictfloat(ent, effects) & (16 | 32)))
59 )
60 )
61 return -1;
62 return 1;
63 }
64
65 /*
66 ===============================================================================
67
68 LINE TESTING IN HULLS
69
70 ===============================================================================
71 */
72
SV_GenericHitSuperContentsMask(const prvm_edict_t * passedict)73 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
74 {
75 prvm_prog_t *prog = SVVM_prog;
76 if (passedict)
77 {
78 int dphitcontentsmask = (int)PRVM_serveredictfloat(passedict, dphitcontentsmask);
79 if (dphitcontentsmask)
80 return dphitcontentsmask;
81 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_SLIDEBOX)
82 {
83 if ((int)PRVM_serveredictfloat(passedict, flags) & FL_MONSTER)
84 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
85 else
86 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
87 }
88 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_CORPSE)
89 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
90 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_TRIGGER)
91 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
92 else
93 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
94 }
95 else
96 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
97 }
98
99 /*
100 ==================
101 SV_TracePoint
102 ==================
103 */
SV_TracePoint(const vec3_t start,int type,prvm_edict_t * passedict,int hitsupercontentsmask,int skipsupercontentsmask)104 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask)
105 {
106 prvm_prog_t *prog = SVVM_prog;
107 int i, bodysupercontents;
108 int passedictprog;
109 float pitchsign = 1;
110 prvm_edict_t *traceowner, *touch;
111 trace_t trace;
112 // temporary storage because prvm_vec_t may differ from vec_t
113 vec3_t touchmins, touchmaxs;
114 // bounding box of entire move area
115 vec3_t clipboxmins, clipboxmaxs;
116 // size when clipping against monsters
117 vec3_t clipmins2, clipmaxs2;
118 // start and end origin of move
119 vec3_t clipstart;
120 // trace results
121 trace_t cliptrace;
122 // matrices to transform into/out of other entity's space
123 matrix4x4_t matrix, imatrix;
124 // model of other entity
125 dp_model_t *model;
126 // list of entities to test for collisions
127 int numtouchedicts;
128 static prvm_edict_t *touchedicts[MAX_EDICTS];
129
130 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask);
131
132 VectorCopy(start, clipstart);
133 VectorClear(clipmins2);
134 VectorClear(clipmaxs2);
135 #if COLLISIONPARANOID >= 3
136 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
137 #endif
138
139 // clip to world
140 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask, skipsupercontentsmask);
141 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
142 if (cliptrace.startsolid || cliptrace.fraction < 1)
143 cliptrace.ent = prog->edicts;
144 if (type == MOVE_WORLDONLY)
145 goto finished;
146
147 if (type == MOVE_MISSILE)
148 {
149 // LordHavoc: modified this, was = -15, now -= 15
150 for (i = 0;i < 3;i++)
151 {
152 clipmins2[i] -= 15;
153 clipmaxs2[i] += 15;
154 }
155 }
156
157 // create the bounding box of the entire move
158 for (i = 0;i < 3;i++)
159 {
160 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
161 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
162 }
163
164 // debug override to test against everything
165 if (sv_debugmove.integer)
166 {
167 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
168 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
169 }
170
171 // if the passedict is world, make it NULL (to avoid two checks each time)
172 if (passedict == prog->edicts)
173 passedict = NULL;
174 // precalculate prog value for passedict for comparisons
175 passedictprog = PRVM_EDICT_TO_PROG(passedict);
176 // precalculate passedict's owner edict pointer for comparisons
177 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
178
179 // clip to entities
180 // because this uses World_EntitiestoBox, we know all entity boxes overlap
181 // the clip region, so we can skip culling checks in the loop below
182 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
183 if (numtouchedicts > MAX_EDICTS)
184 {
185 // this never happens
186 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
187 numtouchedicts = MAX_EDICTS;
188 }
189 for (i = 0;i < numtouchedicts;i++)
190 {
191 touch = touchedicts[i];
192
193 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
194 continue;
195 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
196 continue;
197
198 if (passedict)
199 {
200 // don't clip against self
201 if (passedict == touch)
202 continue;
203 // don't clip owned entities against owner
204 if (traceowner == touch)
205 continue;
206 // don't clip owner against owned entities
207 if (passedictprog == PRVM_serveredictedict(touch, owner))
208 continue;
209 // don't clip points against points (they can't collide)
210 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
211 continue;
212 }
213
214 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
215
216 // might interact, so do an exact clip
217 model = NULL;
218 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
219 {
220 model = SV_GetModelFromEdict(touch);
221 pitchsign = SV_GetPitchSign(prog, touch);
222 }
223 if (model)
224 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
225 else
226 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
227 Matrix4x4_Invert_Simple(&imatrix, &matrix);
228 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
229 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
230 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
231 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
232 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
233 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
234 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask, skipsupercontentsmask, 0.0f);
235 else
236 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask, skipsupercontentsmask);
237
238 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
239 }
240
241 finished:
242 return cliptrace;
243 }
244
245 /*
246 ==================
247 SV_TraceLine
248 ==================
249 */
SV_TraceLine(const vec3_t start,const vec3_t end,int type,prvm_edict_t * passedict,int hitsupercontentsmask,int skipsupercontentsmask,float extend)250 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, float extend)
251 {
252 prvm_prog_t *prog = SVVM_prog;
253 int i, bodysupercontents;
254 int passedictprog;
255 float pitchsign = 1;
256 prvm_edict_t *traceowner, *touch;
257 trace_t trace;
258 // temporary storage because prvm_vec_t may differ from vec_t
259 vec3_t touchmins, touchmaxs;
260 // bounding box of entire move area
261 vec3_t clipboxmins, clipboxmaxs;
262 // size when clipping against monsters
263 vec3_t clipmins2, clipmaxs2;
264 // start and end origin of move
265 vec3_t clipstart, clipend;
266 // trace results
267 trace_t cliptrace;
268 // matrices to transform into/out of other entity's space
269 matrix4x4_t matrix, imatrix;
270 // model of other entity
271 dp_model_t *model;
272 // list of entities to test for collisions
273 int numtouchedicts;
274 static prvm_edict_t *touchedicts[MAX_EDICTS];
275 if (VectorCompare(start, end))
276 return SV_TracePoint(start, type, passedict, hitsupercontentsmask, skipsupercontentsmask);
277
278 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
279
280 VectorCopy(start, clipstart);
281 VectorCopy(end, clipend);
282 VectorClear(clipmins2);
283 VectorClear(clipmaxs2);
284 #if COLLISIONPARANOID >= 3
285 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
286 #endif
287
288 // clip to world
289 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, extend, false);
290 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
291 if (cliptrace.startsolid || cliptrace.fraction < 1)
292 cliptrace.ent = prog->edicts;
293 if (type == MOVE_WORLDONLY)
294 goto finished;
295
296 if (type == MOVE_MISSILE)
297 {
298 // LordHavoc: modified this, was = -15, now -= 15
299 for (i = 0;i < 3;i++)
300 {
301 clipmins2[i] -= 15;
302 clipmaxs2[i] += 15;
303 }
304 }
305
306 // create the bounding box of the entire move
307 for (i = 0;i < 3;i++)
308 {
309 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
310 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
311 }
312
313 // debug override to test against everything
314 if (sv_debugmove.integer)
315 {
316 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
317 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
318 }
319
320 // if the passedict is world, make it NULL (to avoid two checks each time)
321 if (passedict == prog->edicts)
322 passedict = NULL;
323 // precalculate prog value for passedict for comparisons
324 passedictprog = PRVM_EDICT_TO_PROG(passedict);
325 // precalculate passedict's owner edict pointer for comparisons
326 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
327
328 // clip to entities
329 // because this uses World_EntitiestoBox, we know all entity boxes overlap
330 // the clip region, so we can skip culling checks in the loop below
331 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
332 if (numtouchedicts > MAX_EDICTS)
333 {
334 // this never happens
335 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
336 numtouchedicts = MAX_EDICTS;
337 }
338 for (i = 0;i < numtouchedicts;i++)
339 {
340 touch = touchedicts[i];
341
342 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
343 continue;
344 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
345 continue;
346
347 if (passedict)
348 {
349 // don't clip against self
350 if (passedict == touch)
351 continue;
352 // don't clip owned entities against owner
353 if (traceowner == touch)
354 continue;
355 // don't clip owner against owned entities
356 if (passedictprog == PRVM_serveredictedict(touch, owner))
357 continue;
358 // don't clip points against points (they can't collide)
359 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
360 continue;
361 }
362
363 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
364
365 // might interact, so do an exact clip
366 model = NULL;
367 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
368 {
369 model = SV_GetModelFromEdict(touch);
370 pitchsign = SV_GetPitchSign(prog, touch);
371 }
372 if (model)
373 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
374 else
375 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
376 Matrix4x4_Invert_Simple(&imatrix, &matrix);
377 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
378 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
379 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
380 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
381 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
382 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
383 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
384 else
385 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, extend, false);
386
387 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
388 }
389
390 finished:
391 return cliptrace;
392 }
393
394 /*
395 ==================
396 SV_Move
397 ==================
398 */
399 #if COLLISIONPARANOID >= 1
SV_TraceBox_(const vec3_t start,const vec3_t mins,const vec3_t maxs,const vec3_t end,int type,prvm_edict_t * passedict,int hitsupercontentsmask,int skipsupercontentsmask,float extend)400 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, float extend)
401 #else
402 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, float extend)
403 #endif
404 {
405 prvm_prog_t *prog = SVVM_prog;
406 vec3_t hullmins, hullmaxs;
407 int i, bodysupercontents;
408 int passedictprog;
409 float pitchsign = 1;
410 qboolean pointtrace;
411 prvm_edict_t *traceowner, *touch;
412 trace_t trace;
413 // temporary storage because prvm_vec_t may differ from vec_t
414 vec3_t touchmins, touchmaxs;
415 // bounding box of entire move area
416 vec3_t clipboxmins, clipboxmaxs;
417 // size of the moving object
418 vec3_t clipmins, clipmaxs;
419 // size when clipping against monsters
420 vec3_t clipmins2, clipmaxs2;
421 // start and end origin of move
422 vec3_t clipstart, clipend;
423 // trace results
424 trace_t cliptrace;
425 // matrices to transform into/out of other entity's space
426 matrix4x4_t matrix, imatrix;
427 // model of other entity
428 dp_model_t *model;
429 // list of entities to test for collisions
430 int numtouchedicts;
431 static prvm_edict_t *touchedicts[MAX_EDICTS];
432 if (VectorCompare(mins, maxs))
433 {
434 vec3_t shiftstart, shiftend;
435 VectorAdd(start, mins, shiftstart);
436 VectorAdd(end, mins, shiftend);
437 if (VectorCompare(start, end))
438 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, skipsupercontentsmask);
439 else
440 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, skipsupercontentsmask, extend);
441 VectorSubtract(trace.endpos, mins, trace.endpos);
442 return trace;
443 }
444
445 VectorCopy(start, clipstart);
446 VectorCopy(end, clipend);
447 VectorCopy(mins, clipmins);
448 VectorCopy(maxs, clipmaxs);
449 VectorCopy(mins, clipmins2);
450 VectorCopy(maxs, clipmaxs2);
451 #if COLLISIONPARANOID >= 3
452 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
453 #endif
454
455 // clip to world
456 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
457 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
458 if (cliptrace.startsolid || cliptrace.fraction < 1)
459 cliptrace.ent = prog->edicts;
460 if (type == MOVE_WORLDONLY)
461 goto finished;
462
463 if (type == MOVE_MISSILE)
464 {
465 // LordHavoc: modified this, was = -15, now -= 15
466 for (i = 0;i < 3;i++)
467 {
468 clipmins2[i] -= 15;
469 clipmaxs2[i] += 15;
470 }
471 }
472
473 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
474 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
475 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
476 else
477 {
478 VectorCopy(clipmins, hullmins);
479 VectorCopy(clipmaxs, hullmaxs);
480 }
481
482 // create the bounding box of the entire move
483 for (i = 0;i < 3;i++)
484 {
485 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
486 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
487 }
488
489 // debug override to test against everything
490 if (sv_debugmove.integer)
491 {
492 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
493 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
494 }
495
496 // if the passedict is world, make it NULL (to avoid two checks each time)
497 if (passedict == prog->edicts)
498 passedict = NULL;
499 // precalculate prog value for passedict for comparisons
500 passedictprog = PRVM_EDICT_TO_PROG(passedict);
501 // figure out whether this is a point trace for comparisons
502 pointtrace = VectorCompare(clipmins, clipmaxs);
503 // precalculate passedict's owner edict pointer for comparisons
504 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
505
506 // clip to entities
507 // because this uses World_EntitiestoBox, we know all entity boxes overlap
508 // the clip region, so we can skip culling checks in the loop below
509 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
510 if (numtouchedicts > MAX_EDICTS)
511 {
512 // this never happens
513 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
514 numtouchedicts = MAX_EDICTS;
515 }
516 for (i = 0;i < numtouchedicts;i++)
517 {
518 touch = touchedicts[i];
519
520 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
521 continue;
522 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
523 continue;
524
525 if (passedict)
526 {
527 // don't clip against self
528 if (passedict == touch)
529 continue;
530 // don't clip owned entities against owner
531 if (traceowner == touch)
532 continue;
533 // don't clip owner against owned entities
534 if (passedictprog == PRVM_serveredictedict(touch, owner))
535 continue;
536 // don't clip points against points (they can't collide)
537 if (pointtrace && VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
538 continue;
539 }
540
541 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
542
543 // might interact, so do an exact clip
544 model = NULL;
545 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
546 {
547 model = SV_GetModelFromEdict(touch);
548 pitchsign = SV_GetPitchSign(prog, touch);
549 }
550 if (model)
551 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
552 else
553 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
554 Matrix4x4_Invert_Simple(&imatrix, &matrix);
555 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
556 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
557 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
558 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
559 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
560 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
561 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
562 else
563 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, extend);
564
565 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
566 }
567
568 finished:
569 return cliptrace;
570 }
571
572 #if COLLISIONPARANOID >= 1
SV_TraceBox(const vec3_t start,const vec3_t mins,const vec3_t maxs,const vec3_t end,int type,prvm_edict_t * passedict,int hitsupercontentsmask,int skipsupercontentsmask)573 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask)
574 {
575 prvm_prog_t *prog = SVVM_prog;
576 int endstuck;
577 trace_t trace;
578 vec3_t temp;
579 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask);
580 if (passedict)
581 {
582 VectorCopy(trace.endpos, temp);
583 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask).startsolid;
584 #if COLLISIONPARANOID < 3
585 if (trace.startsolid || endstuck)
586 #endif
587 Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "^3" : "", passedict ? (int)(passedict - prog->edicts) : -1, PRVM_serveredictvector(passedict, origin)[0], PRVM_serveredictvector(passedict, origin)[1], PRVM_serveredictvector(passedict, origin)[2], end[0] - PRVM_serveredictvector(passedict, origin)[0], end[1] - PRVM_serveredictvector(passedict, origin)[1], end[2] - PRVM_serveredictvector(passedict, origin)[2], trace.fraction, trace.endpos[0] - PRVM_serveredictvector(passedict, origin)[0], trace.endpos[1] - PRVM_serveredictvector(passedict, origin)[1], trace.endpos[2] - PRVM_serveredictvector(passedict, origin)[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
588 }
589 return trace;
590 }
591 #endif
592
SV_PointSuperContents(const vec3_t point)593 int SV_PointSuperContents(const vec3_t point)
594 {
595 prvm_prog_t *prog = SVVM_prog;
596 int supercontents = 0;
597 int i;
598 prvm_edict_t *touch;
599 vec3_t transformed;
600 // matrices to transform into/out of other entity's space
601 matrix4x4_t matrix, imatrix;
602 // model of other entity
603 dp_model_t *model;
604 int frame;
605 // list of entities to test for collisions
606 int numtouchedicts;
607 static prvm_edict_t *touchedicts[MAX_EDICTS];
608
609 // get world supercontents at this point
610 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
611 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
612
613 // if sv_gameplayfix_swiminbmodels is off we're done
614 if (!sv_gameplayfix_swiminbmodels.integer)
615 return supercontents;
616
617 // get list of entities at this point
618 numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
619 if (numtouchedicts > MAX_EDICTS)
620 {
621 // this never happens
622 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
623 numtouchedicts = MAX_EDICTS;
624 }
625 for (i = 0;i < numtouchedicts;i++)
626 {
627 touch = touchedicts[i];
628
629 // we only care about SOLID_BSP for pointcontents
630 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
631 continue;
632
633 // might interact, so do an exact clip
634 model = SV_GetModelFromEdict(touch);
635 if (!model || !model->PointSuperContents)
636 continue;
637 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
638 Matrix4x4_Invert_Simple(&imatrix, &matrix);
639 Matrix4x4_Transform(&imatrix, point, transformed);
640 frame = (int)PRVM_serveredictfloat(touch, frame);
641 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
642 }
643
644 return supercontents;
645 }
646
647 /*
648 ===============================================================================
649
650 Linking entities into the world culling system
651
652 ===============================================================================
653 */
654
SV_EntitiesInBox(const vec3_t mins,const vec3_t maxs,int maxedicts,prvm_edict_t ** resultedicts)655 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
656 {
657 prvm_prog_t *prog = SVVM_prog;
658 vec3_t paddedmins, paddedmaxs;
659 if (maxedicts < 1 || resultedicts == NULL)
660 return 0;
661 // LordHavoc: discovered this actually causes its own bugs (dm6 teleporters being too close to info_teleport_destination)
662 //VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
663 //VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
664 VectorCopy(mins, paddedmins);
665 VectorCopy(maxs, paddedmaxs);
666 if (sv_areadebug.integer)
667 {
668 int numresultedicts = 0;
669 int edictindex;
670 prvm_edict_t *ed;
671 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
672 {
673 ed = PRVM_EDICT_NUM(edictindex);
674 if (!ed->priv.required->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
675 {
676 resultedicts[numresultedicts++] = ed;
677 if (numresultedicts == maxedicts)
678 break;
679 }
680 }
681 return numresultedicts;
682 }
683 else
684 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
685 }
686
SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t * touch,prvm_edict_t * ent)687 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
688 {
689 prvm_prog_t *prog = SVVM_prog;
690 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
691 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
692 PRVM_serverglobalfloat(time) = sv.time;
693 PRVM_serverglobalfloat(trace_allsolid) = false;
694 PRVM_serverglobalfloat(trace_startsolid) = false;
695 PRVM_serverglobalfloat(trace_fraction) = 1;
696 PRVM_serverglobalfloat(trace_inwater) = false;
697 PRVM_serverglobalfloat(trace_inopen) = true;
698 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
699 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
700 PRVM_serverglobalfloat(trace_plane_dist) = 0;
701 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
702 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
703 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
704 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
705 PRVM_serverglobalstring(trace_dphittexturename) = 0;
706 prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
707 }
708
SV_LinkEdict_TouchAreaGrid(prvm_edict_t * ent)709 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
710 {
711 prvm_prog_t *prog = SVVM_prog;
712 int i, numtouchedicts, old_self, old_other;
713 prvm_edict_t *touch;
714 static prvm_edict_t *touchedicts[MAX_EDICTS];
715
716 if (ent == prog->edicts)
717 return; // don't add the world
718
719 if (ent->priv.server->free)
720 return;
721
722 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
723 return;
724
725 // build a list of edicts to touch, because the link loop can be corrupted
726 // by IncreaseEdicts called during touch functions
727 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
728 if (numtouchedicts > MAX_EDICTS)
729 {
730 // this never happens
731 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
732 numtouchedicts = MAX_EDICTS;
733 }
734
735 old_self = PRVM_serverglobaledict(self);
736 old_other = PRVM_serverglobaledict(other);
737 for (i = 0;i < numtouchedicts;i++)
738 {
739 touch = touchedicts[i];
740 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
741 {
742 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
743 }
744 }
745 PRVM_serverglobaledict(self) = old_self;
746 PRVM_serverglobaledict(other) = old_other;
747 }
748
RotateBBox(const vec3_t mins,const vec3_t maxs,const vec3_t angles,vec3_t rotatedmins,vec3_t rotatedmaxs)749 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
750 {
751 vec3_t v, u;
752 matrix4x4_t m;
753 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
754
755 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
756 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
757 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
758 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
759 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
760 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
761 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
762 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
763 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
764 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
765 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
766 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
767 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
768 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
769 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
770 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
771 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
772 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
773 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
774 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
775 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
776 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
777 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
778 }
779
780 /*
781 ===============
782 SV_LinkEdict
783
784 ===============
785 */
SV_LinkEdict(prvm_edict_t * ent)786 void SV_LinkEdict (prvm_edict_t *ent)
787 {
788 prvm_prog_t *prog = SVVM_prog;
789 dp_model_t *model;
790 vec3_t mins, maxs, entmins, entmaxs, entangles;
791 int modelindex;
792
793 if (ent == prog->edicts)
794 return; // don't add the world
795
796 if (ent->priv.server->free)
797 return;
798
799 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
800 if (modelindex < 0 || modelindex >= MAX_MODELS)
801 {
802 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
803 modelindex = 0;
804 }
805 model = SV_GetModelByIndex(modelindex);
806
807 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
808 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
809 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
810
811 // set the abs box
812
813 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
814 {
815 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
816 // TODO special handling for spheres?
817 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
818 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
819 VectorCopy(PRVM_serveredictvector(ent, angles), entangles);
820 RotateBBox(entmins, entmaxs, entangles, mins, maxs);
821 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
822 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
823 }
824 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
825 {
826 if (model != NULL)
827 {
828 if (!model->TraceBox)
829 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
830
831 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
832 {
833 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
834 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
835 }
836 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
837 {
838 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
839 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
840 }
841 else
842 {
843 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
844 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
845 }
846 }
847 else
848 {
849 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
850 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
851 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
852 }
853 }
854 else
855 {
856 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
857 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
858 }
859
860 //
861 // to make items easier to pick up and allow them to be grabbed off
862 // of shelves, the abs sizes are expanded
863 //
864 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
865 {
866 mins[0] -= 15;
867 mins[1] -= 15;
868 mins[2] -= 1;
869 maxs[0] += 15;
870 maxs[1] += 15;
871 maxs[2] += 1;
872 }
873 else
874 {
875 // because movement is clipped an epsilon away from an actual edge,
876 // we must fully check even when bounding boxes don't quite touch
877 mins[0] -= 1;
878 mins[1] -= 1;
879 mins[2] -= 1;
880 maxs[0] += 1;
881 maxs[1] += 1;
882 maxs[2] += 1;
883 }
884
885 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
886 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
887
888 World_LinkEdict(&sv.world, ent, mins, maxs);
889 }
890
891 /*
892 ===============================================================================
893
894 Utility functions
895
896 ===============================================================================
897 */
898
899 /*
900 ============
901 SV_TestEntityPosition
902
903 returns true if the entity is in solid currently
904 ============
905 */
SV_TestEntityPosition(prvm_edict_t * ent,vec3_t offset)906 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
907 {
908 prvm_prog_t *prog = SVVM_prog;
909 int contents;
910 vec3_t org, entorigin, entmins, entmaxs;
911 trace_t trace;
912 contents = SV_GenericHitSuperContentsMask(ent);
913 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
914 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
915 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
916 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
917 trace = SV_TraceBox(org, entmins, entmaxs, entorigin, ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, contents, 0, collision_extendmovelength.value);
918 if (trace.startsupercontents & contents)
919 return true;
920 else
921 {
922 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
923 {
924 // q1bsp/hlbsp use hulls and if the entity does not exactly match
925 // a hull size it is incorrectly tested, so this code tries to
926 // 'fix' it slightly...
927 // FIXME: this breaks entities larger than the hull size
928 int i;
929 vec3_t v, m1, m2, s;
930 VectorAdd(org, entmins, m1);
931 VectorAdd(org, entmaxs, m2);
932 VectorSubtract(m2, m1, s);
933 #define EPSILON (1.0f / 32.0f)
934 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
935 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
936 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
937 for (i = 0;i < 8;i++)
938 {
939 v[0] = (i & 1) ? m2[0] : m1[0];
940 v[1] = (i & 2) ? m2[1] : m1[1];
941 v[2] = (i & 4) ? m2[2] : m1[2];
942 if (SV_PointSuperContents(v) & contents)
943 return true;
944 }
945 }
946 }
947 // if the trace found a better position for the entity, move it there
948 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
949 {
950 #if 0
951 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
952 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
953 #else
954 // verify if the endpos is REALLY outside solid
955 VectorCopy(trace.endpos, org);
956 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, contents, 0, collision_extendmovelength.value);
957 if(trace.startsolid)
958 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
959 else
960 VectorCopy(org, PRVM_serveredictvector(ent, origin));
961 #endif
962 }
963 return false;
964 }
965
966 // DRESK - Support for Entity Contents Transition Event
967 /*
968 ================
969 SV_CheckContentsTransition
970
971 returns true if entity had a valid contentstransition function call
972 ================
973 */
SV_CheckContentsTransition(prvm_edict_t * ent,const int nContents)974 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
975 {
976 prvm_prog_t *prog = SVVM_prog;
977 int bValidFunctionCall;
978
979 // Default Valid Function Call to False
980 bValidFunctionCall = false;
981
982 if(PRVM_serveredictfloat(ent, watertype) != nContents)
983 { // Changed Contents
984 // Acquire Contents Transition Function from QC
985 if(PRVM_serveredictfunction(ent, contentstransition))
986 { // Valid Function; Execute
987 // Assign Valid Function
988 bValidFunctionCall = true;
989 // Prepare Parameters (Original Contents, New Contents)
990 // Original Contents
991 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
992 // New Contents
993 PRVM_G_FLOAT(OFS_PARM1) = nContents;
994 // Assign Self
995 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
996 // Set Time
997 PRVM_serverglobalfloat(time) = sv.time;
998 // Execute VM Function
999 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1000 }
1001 }
1002
1003 // Return if Function Call was Valid
1004 return bValidFunctionCall;
1005 }
1006
1007
1008 /*
1009 ================
1010 SV_CheckVelocity
1011 ================
1012 */
SV_CheckVelocity(prvm_edict_t * ent)1013 void SV_CheckVelocity (prvm_edict_t *ent)
1014 {
1015 prvm_prog_t *prog = SVVM_prog;
1016 int i;
1017 float wishspeed;
1018
1019 //
1020 // bound velocity
1021 //
1022 for (i=0 ; i<3 ; i++)
1023 {
1024 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1025 {
1026 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1027 PRVM_serveredictvector(ent, velocity)[i] = 0;
1028 }
1029 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1030 {
1031 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1032 PRVM_serveredictvector(ent, origin)[i] = 0;
1033 }
1034 }
1035
1036 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1037 // player_run/player_stand1 does not horribly malfunction if the
1038 // velocity becomes a denormalized float
1039 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0001)
1040 VectorClear(PRVM_serveredictvector(ent, velocity));
1041
1042 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1043 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1044 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1045 {
1046 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1047 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1048 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1049 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1050 }
1051 }
1052
1053 /*
1054 =============
1055 SV_RunThink
1056
1057 Runs thinking code if time. There is some play in the exact time the think
1058 function will be called, because it is called before any movement is done
1059 in a frame. Not used for pushmove objects, because they must be exact.
1060 Returns false if the entity removed itself.
1061 =============
1062 */
SV_RunThink(prvm_edict_t * ent)1063 static qboolean SV_RunThink (prvm_edict_t *ent)
1064 {
1065 prvm_prog_t *prog = SVVM_prog;
1066 int iterations;
1067
1068 // don't let things stay in the past.
1069 // it is possible to start that way by a trigger with a local time.
1070 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1071 return true;
1072
1073 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1074 {
1075 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1076 PRVM_serveredictfloat(ent, nextthink) = 0;
1077 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1078 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1079 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1080 // mods often set nextthink to time to cause a think every frame,
1081 // we don't want to loop in that case, so exit if the new nextthink is
1082 // <= the time the qc was told, also exit if it is past the end of the
1083 // frame
1084 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1085 break;
1086 }
1087 return !ent->priv.server->free;
1088 }
1089
1090 /*
1091 ==================
1092 SV_Impact
1093
1094 Two entities have touched, so run their touch functions
1095 ==================
1096 */
SV_Impact(prvm_edict_t * e1,trace_t * trace)1097 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1098 {
1099 prvm_prog_t *prog = SVVM_prog;
1100 int restorevm_tempstringsbuf_cursize;
1101 int old_self, old_other;
1102 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1103
1104 old_self = PRVM_serverglobaledict(self);
1105 old_other = PRVM_serverglobaledict(other);
1106 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1107
1108 VM_SetTraceGlobals(prog, trace);
1109
1110 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1111 {
1112 PRVM_serverglobalfloat(time) = sv.time;
1113 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1114 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1115 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1116 }
1117
1118 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1119 {
1120 PRVM_serverglobalfloat(time) = sv.time;
1121 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1122 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1123 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1124 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1125 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1126 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1127 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1128 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1129 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1130 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1131 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1132 }
1133
1134 PRVM_serverglobaledict(self) = old_self;
1135 PRVM_serverglobaledict(other) = old_other;
1136 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1137 }
1138
1139
1140 /*
1141 ==================
1142 ClipVelocity
1143
1144 Slide off of the impacting object
1145 returns the blocked flags (1 = floor, 2 = step / wall)
1146 ==================
1147 */
1148 #define STOP_EPSILON 0.1
ClipVelocity(prvm_vec3_t in,vec3_t normal,prvm_vec3_t out,prvm_vec_t overbounce)1149 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1150 {
1151 int i;
1152 float backoff;
1153
1154 backoff = -DotProduct (in, normal) * overbounce;
1155 VectorMA(in, backoff, normal, out);
1156
1157 for (i = 0;i < 3;i++)
1158 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1159 out[i] = 0;
1160 }
1161
1162
1163 /*
1164 ============
1165 SV_FlyMove
1166
1167 The basic solid body movement clip that slides along multiple planes
1168 Returns the clipflags if the velocity was modified (hit something solid)
1169 1 = floor
1170 2 = wall / step
1171 4 = dead stop
1172 8 = teleported by touch method
1173 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1174 ============
1175 */
1176 static float SV_Gravity (prvm_edict_t *ent);
1177 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink);
1178 #define MAX_CLIP_PLANES 5
SV_FlyMove(prvm_edict_t * ent,float time,qboolean applygravity,float * stepnormal,int hitsupercontentsmask,int skipsupercontentsmask,float stepheight)1179 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, float stepheight)
1180 {
1181 prvm_prog_t *prog = SVVM_prog;
1182 int blocked, bumpcount;
1183 int i, j, numplanes;
1184 float d, time_left, gravity;
1185 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1186 prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
1187 #if 0
1188 vec3_t end;
1189 #endif
1190 trace_t trace;
1191 if (time <= 0)
1192 return 0;
1193 gravity = 0;
1194
1195 VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1196
1197 if(applygravity)
1198 {
1199 gravity = SV_Gravity(ent);
1200
1201 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1202 {
1203 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1204 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1205 else
1206 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1207 }
1208 }
1209
1210 blocked = 0;
1211 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1212 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1213 numplanes = 0;
1214 time_left = time;
1215 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1216 {
1217 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1218 break;
1219
1220 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1221 if(!SV_PushEntity(&trace, ent, push, false))
1222 {
1223 // we got teleported by a touch function
1224 // let's abort the move
1225 blocked |= 8;
1226 break;
1227 }
1228
1229 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
1230 // abort move if we're stuck in the world (and didn't make it out)
1231 if (trace.worldstartsolid && trace.allsolid)
1232 {
1233 VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
1234 return 3;
1235 }
1236
1237 if (trace.fraction == 1)
1238 break;
1239 if (trace.plane.normal[2])
1240 {
1241 if (trace.plane.normal[2] > 0.7)
1242 {
1243 // floor
1244 blocked |= 1;
1245
1246 if (!trace.ent)
1247 {
1248 Con_Printf ("SV_FlyMove: !trace.ent");
1249 trace.ent = prog->edicts;
1250 }
1251
1252 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1253 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1254 }
1255 }
1256 else if (stepheight)
1257 {
1258 // step - handle it immediately
1259 vec3_t org;
1260 vec3_t steppush;
1261 trace_t steptrace;
1262 trace_t steptrace2;
1263 trace_t steptrace3;
1264 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1265 VectorSet(steppush, 0, 0, stepheight);
1266 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1267 if(!SV_PushEntity(&steptrace, ent, steppush, false))
1268 {
1269 blocked |= 8;
1270 break;
1271 }
1272 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1273 if(!SV_PushEntity(&steptrace2, ent, push, false))
1274 {
1275 blocked |= 8;
1276 break;
1277 }
1278 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1279 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1280 if(!SV_PushEntity(&steptrace3, ent, steppush, false))
1281 {
1282 blocked |= 8;
1283 break;
1284 }
1285 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1286 // accept the new position if it made some progress...
1287 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1288 {
1289 //Con_Printf("accepted (delta %f %f %f)\n", PRVM_serveredictvector(ent, origin)[0] - org[0], PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
1290 trace = steptrace2;
1291 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1292 time_left *= 1 - trace.fraction;
1293 numplanes = 0;
1294 continue;
1295 }
1296 else
1297 {
1298 //Con_Printf("REJECTED (delta %f %f %f)\n", PRVM_serveredictvector(ent, origin)[0] - org[0], PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
1299 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1300 }
1301 }
1302 else
1303 {
1304 // step - return it to caller
1305 blocked |= 2;
1306 // save the trace for player extrafriction
1307 if (stepnormal)
1308 VectorCopy(trace.plane.normal, stepnormal);
1309 }
1310 if (trace.fraction >= 0.001)
1311 {
1312 // actually covered some distance
1313 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1314 numplanes = 0;
1315 }
1316
1317 time_left *= 1 - trace.fraction;
1318
1319 // clipped to another plane
1320 if (numplanes >= MAX_CLIP_PLANES)
1321 {
1322 // this shouldn't really happen
1323 VectorClear(PRVM_serveredictvector(ent, velocity));
1324 blocked = 3;
1325 break;
1326 }
1327
1328 /*
1329 for (i = 0;i < numplanes;i++)
1330 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1331 break;
1332 if (i < numplanes)
1333 {
1334 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1335 continue;
1336 }
1337 */
1338
1339 VectorCopy(trace.plane.normal, planes[numplanes]);
1340 numplanes++;
1341
1342 // modify original_velocity so it parallels all of the clip planes
1343 for (i = 0;i < numplanes;i++)
1344 {
1345 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1346 for (j = 0;j < numplanes;j++)
1347 {
1348 if (j != i)
1349 {
1350 // not ok
1351 if (DotProduct(new_velocity, planes[j]) < 0)
1352 break;
1353 }
1354 }
1355 if (j == numplanes)
1356 break;
1357 }
1358
1359 if (i != numplanes)
1360 {
1361 // go along this plane
1362 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1363 }
1364 else
1365 {
1366 // go along the crease
1367 if (numplanes != 2)
1368 {
1369 VectorClear(PRVM_serveredictvector(ent, velocity));
1370 blocked = 7;
1371 break;
1372 }
1373 CrossProduct(planes[0], planes[1], dir);
1374 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1375 VectorNormalize(dir);
1376 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1377 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1378 }
1379
1380 // if current velocity is against the original velocity,
1381 // stop dead to avoid tiny occilations in sloping corners
1382 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1383 {
1384 VectorClear(PRVM_serveredictvector(ent, velocity));
1385 break;
1386 }
1387 }
1388
1389 //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, PRVM_serveredictvector(ent, velocity)[0], PRVM_serveredictvector(ent, velocity)[1], PRVM_serveredictvector(ent, velocity)[2]);
1390
1391 /*
1392 if ((blocked & 1) == 0 && bumpcount > 1)
1393 {
1394 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1395 // flag ONGROUND if there's ground under it
1396 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask, skipsupercontentsmask);
1397 }
1398 */
1399
1400 // LordHavoc: this came from QW and allows you to get out of water more easily
1401 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1402 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1403
1404 if(applygravity)
1405 {
1406 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1407 {
1408 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1409 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1410 }
1411 }
1412
1413 return blocked;
1414 }
1415
1416 /*
1417 ============
1418 SV_Gravity
1419
1420 ============
1421 */
SV_Gravity(prvm_edict_t * ent)1422 static float SV_Gravity (prvm_edict_t *ent)
1423 {
1424 prvm_prog_t *prog = SVVM_prog;
1425 float ent_gravity;
1426
1427 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1428 if (!ent_gravity)
1429 ent_gravity = 1.0f;
1430 return ent_gravity * sv_gravity.value * sv.frametime;
1431 }
1432
1433
1434 /*
1435 ===============================================================================
1436
1437 PUSHMOVE
1438
1439 ===============================================================================
1440 */
1441
SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t * ent,vec3_t pivot)1442 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1443 {
1444 prvm_prog_t *prog = SVVM_prog;
1445 int bump;
1446 trace_t stucktrace;
1447 vec3_t stuckorigin;
1448 vec3_t stuckmins, stuckmaxs;
1449 vec3_t goodmins, goodmaxs;
1450 vec3_t testorigin;
1451 vec_t nudge;
1452 vec3_t move;
1453 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1454 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1455 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1456 VectorCopy(pivot, goodmins);
1457 VectorCopy(pivot, goodmaxs);
1458 for (bump = 0;bump < 6;bump++)
1459 {
1460 int coord = 2-(bump >> 1);
1461 //int coord = (bump >> 1);
1462 int dir = (bump & 1);
1463 int subbump;
1464
1465 for(subbump = 0; ; ++subbump)
1466 {
1467 VectorCopy(stuckorigin, testorigin);
1468 if(dir)
1469 {
1470 // pushing maxs
1471 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1472 }
1473 else
1474 {
1475 // pushing mins
1476 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1477 }
1478
1479 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, collision_extendmovelength.value);
1480 if (stucktrace.bmodelstartsolid)
1481 {
1482 // BAD BAD, can't fix that
1483 return false;
1484 }
1485
1486 if (stucktrace.fraction >= 1)
1487 break; // it WORKS!
1488
1489 if(subbump >= 10)
1490 {
1491 // BAD BAD, can't fix that
1492 return false;
1493 }
1494
1495 // we hit something... let's move out of it
1496 VectorSubtract(stucktrace.endpos, testorigin, move);
1497 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1498 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1499 }
1500 /*
1501 if(subbump > 0)
1502 Con_Printf("subbump: %d\n", subbump);
1503 */
1504
1505 if(dir)
1506 {
1507 // pushing maxs
1508 goodmaxs[coord] = stuckmaxs[coord];
1509 }
1510 else
1511 {
1512 // pushing mins
1513 goodmins[coord] = stuckmins[coord];
1514 }
1515 }
1516
1517 // WE WIN
1518 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1519
1520 return true;
1521 }
1522
SV_NudgeOutOfSolid(prvm_edict_t * ent)1523 qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1524 {
1525 prvm_prog_t *prog = SVVM_prog;
1526 int bump, pass;
1527 trace_t stucktrace;
1528 vec3_t stuckorigin;
1529 vec3_t stuckmins, stuckmaxs;
1530 vec_t nudge;
1531 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1532 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1533 separation = 0.0f; // when using hulls, it can not be enlarged
1534 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1535 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1536 stuckmins[0] -= separation;
1537 stuckmins[1] -= separation;
1538 stuckmins[2] -= separation;
1539 stuckmaxs[0] += separation;
1540 stuckmaxs[1] += separation;
1541 stuckmaxs[2] += separation;
1542 // first pass we try to get it out of brush entities
1543 // second pass we try to get it out of world only (can't win them all)
1544 for (pass = 0;pass < 2;pass++)
1545 {
1546 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1547 for (bump = 0;bump < 10;bump++)
1548 {
1549 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, collision_extendmovelength.value);
1550 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1551 {
1552 // found a good location, use it
1553 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1554 return true;
1555 }
1556 nudge = -stucktrace.startdepth;
1557 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1558 }
1559 }
1560 return false;
1561 }
1562
1563 /*
1564 ============
1565 SV_PushEntity
1566
1567 Does not change the entities velocity at all
1568 The trace struct is filled with the trace that has been done.
1569 Returns true if the push did not result in the entity being teleported by QC code.
1570 ============
1571 */
SV_PushEntity(trace_t * trace,prvm_edict_t * ent,vec3_t push,qboolean dolink)1572 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink)
1573 {
1574 prvm_prog_t *prog = SVVM_prog;
1575 int solid;
1576 int movetype;
1577 int type;
1578 vec3_t mins, maxs;
1579 vec3_t start;
1580 vec3_t end;
1581
1582 solid = (int)PRVM_serveredictfloat(ent, solid);
1583 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1584 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1585 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1586
1587 // move start position out of solids
1588 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1589 {
1590 SV_NudgeOutOfSolid(ent);
1591 }
1592
1593 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1594 VectorAdd(start, push, end);
1595
1596 if (movetype == MOVETYPE_FLYMISSILE)
1597 type = MOVE_MISSILE;
1598 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1599 type = MOVE_WORLDONLY;
1600 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1601 type = MOVE_NOMONSTERS; // only clip against bmodels
1602 else
1603 type = MOVE_NORMAL;
1604
1605 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, collision_extendmovelength.value);
1606 // fail the move if stuck in world
1607 if (trace->worldstartsolid)
1608 return true;
1609
1610 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1611
1612 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1613
1614 SV_LinkEdict(ent);
1615
1616 #if 0
1617 if(!trace->startsolid)
1618 if(SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), PRVM_serveredictvector(ent, origin), type, ent, SV_GenericHitSuperContentsMask(ent), 0).startsolid)
1619 {
1620 Con_Printf("something eeeeevil happened\n");
1621 }
1622 #endif
1623
1624 if (dolink)
1625 SV_LinkEdict_TouchAreaGrid(ent);
1626
1627 if((PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace->ent && (!((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) || PRVM_serveredictedict(ent, groundentity) != PRVM_EDICT_TO_PROG(trace->ent))))
1628 SV_Impact (ent, trace);
1629
1630 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1631 {
1632 ent->priv.required->mark = 0;
1633 return false;
1634 }
1635 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1636 {
1637 ent->priv.required->mark = 0;
1638 return true;
1639 }
1640 else
1641 {
1642 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1643 return true;
1644 }
1645 }
1646
1647
1648 /*
1649 ============
1650 SV_PushMove
1651
1652 ============
1653 */
SV_PushMove(prvm_edict_t * pusher,float movetime)1654 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1655 {
1656 prvm_prog_t *prog = SVVM_prog;
1657 int i, e, index;
1658 int pusherowner, pusherprog;
1659 int checkcontents;
1660 qboolean rotated;
1661 float savesolid, movetime2, pushltime;
1662 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1663 int num_moved;
1664 int numcheckentities;
1665 static prvm_edict_t *checkentities[MAX_EDICTS];
1666 dp_model_t *pushermodel;
1667 trace_t trace, trace2;
1668 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1669 static unsigned short moved_edicts[MAX_EDICTS];
1670 vec3_t pivot;
1671
1672 if (!PRVM_serveredictvector(pusher, velocity)[0] && !PRVM_serveredictvector(pusher, velocity)[1] && !PRVM_serveredictvector(pusher, velocity)[2] && !PRVM_serveredictvector(pusher, avelocity)[0] && !PRVM_serveredictvector(pusher, avelocity)[1] && !PRVM_serveredictvector(pusher, avelocity)[2])
1673 {
1674 PRVM_serveredictfloat(pusher, ltime) += movetime;
1675 return;
1676 }
1677
1678 switch ((int) PRVM_serveredictfloat(pusher, solid))
1679 {
1680 // LordHavoc: valid pusher types
1681 case SOLID_BSP:
1682 case SOLID_BBOX:
1683 case SOLID_SLIDEBOX:
1684 case SOLID_CORPSE: // LordHavoc: this would be weird...
1685 break;
1686 // LordHavoc: no collisions
1687 case SOLID_NOT:
1688 case SOLID_TRIGGER:
1689 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1690 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1691 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1692 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1693 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1694 PRVM_serveredictfloat(pusher, ltime) += movetime;
1695 SV_LinkEdict(pusher);
1696 return;
1697 default:
1698 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1699 return;
1700 }
1701 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1702 if (index < 1 || index >= MAX_MODELS)
1703 {
1704 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1705 return;
1706 }
1707 pushermodel = SV_GetModelByIndex(index);
1708 pusherowner = PRVM_serveredictedict(pusher, owner);
1709 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1710
1711 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1712
1713 movetime2 = movetime;
1714 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1715 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1716 if (moveangle[0] || moveangle[2])
1717 {
1718 for (i = 0;i < 3;i++)
1719 {
1720 if (move1[i] > 0)
1721 {
1722 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1723 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1724 }
1725 else
1726 {
1727 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1728 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1729 }
1730 }
1731 }
1732 else if (moveangle[1])
1733 {
1734 for (i = 0;i < 3;i++)
1735 {
1736 if (move1[i] > 0)
1737 {
1738 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1739 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1740 }
1741 else
1742 {
1743 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1744 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1745 }
1746 }
1747 }
1748 else
1749 {
1750 for (i = 0;i < 3;i++)
1751 {
1752 if (move1[i] > 0)
1753 {
1754 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1755 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1756 }
1757 else
1758 {
1759 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1760 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1761 }
1762 }
1763 }
1764
1765 VectorNegate (moveangle, a);
1766 AngleVectorsFLU (a, forward, left, up);
1767
1768 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1769 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1770 pushltime = PRVM_serveredictfloat(pusher, ltime);
1771
1772 // move the pusher to its final position
1773
1774 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1775 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1776 PRVM_serveredictfloat(pusher, ltime) += movetime;
1777 SV_LinkEdict(pusher);
1778
1779 pushermodel = SV_GetModelFromEdict(pusher);
1780 Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, PRVM_serveredictvector(pusher, origin)[0], PRVM_serveredictvector(pusher, origin)[1], PRVM_serveredictvector(pusher, origin)[2], PRVM_serveredictvector(pusher, angles)[0], PRVM_serveredictvector(pusher, angles)[1], PRVM_serveredictvector(pusher, angles)[2], 1);
1781 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1782
1783 savesolid = PRVM_serveredictfloat(pusher, solid);
1784
1785 // see if any solid entities are inside the final position
1786 num_moved = 0;
1787
1788 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1789 numcheckentities = 0;
1790 else // MOVETYPE_PUSH
1791 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1792 for (e = 0;e < numcheckentities;e++)
1793 {
1794 prvm_edict_t *check = checkentities[e];
1795 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1796 switch(movetype)
1797 {
1798 case MOVETYPE_NONE:
1799 case MOVETYPE_PUSH:
1800 case MOVETYPE_FOLLOW:
1801 case MOVETYPE_NOCLIP:
1802 case MOVETYPE_FLY_WORLDONLY:
1803 continue;
1804 default:
1805 break;
1806 }
1807
1808 if (PRVM_serveredictedict(check, owner) == pusherprog)
1809 continue;
1810
1811 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1812 continue;
1813
1814 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1815
1816 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1817 check->priv.server->waterposition_forceupdate = true;
1818
1819 checkcontents = SV_GenericHitSuperContentsMask(check);
1820
1821 // if the entity is standing on the pusher, it will definitely be moved
1822 // if the entity is not standing on the pusher, but is in the pusher's
1823 // final position, move it
1824 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1825 {
1826 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1827 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1828 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1829 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1830 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1831 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, 0, collision_extendmovelength.value);
1832 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1833 if (!trace.startsolid)
1834 {
1835 //Con_Printf("- not in solid\n");
1836 continue;
1837 }
1838 }
1839
1840 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1841 //VectorClear(pivot);
1842
1843 if (rotated)
1844 {
1845 vec3_t org2;
1846 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1847 VectorAdd (org, pivot, org);
1848 org2[0] = DotProduct (org, forward);
1849 org2[1] = DotProduct (org, left);
1850 org2[2] = DotProduct (org, up);
1851 VectorSubtract (org2, org, move);
1852 VectorAdd (move, move1, move);
1853 }
1854 else
1855 VectorCopy (move1, move);
1856
1857 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1858
1859 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1860 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1861 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1862
1863 // physics objects need better collisions than this code can do
1864 if (movetype == MOVETYPE_PHYSICS)
1865 {
1866 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1867 SV_LinkEdict(check);
1868 SV_LinkEdict_TouchAreaGrid(check);
1869 continue;
1870 }
1871
1872 // try moving the contacted entity
1873 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1874 if(!SV_PushEntity (&trace, check, move, true))
1875 {
1876 // entity "check" got teleported
1877 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1878 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1879 continue; // pushed enough
1880 }
1881 // FIXME: turn players specially
1882 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1883 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1884 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1885
1886 // this trace.fraction < 1 check causes items to fall off of pushers
1887 // if they pass under or through a wall
1888 // the groundentity check causes items to fall off of ledges
1889 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1890 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1891
1892 // if it is still inside the pusher, block
1893 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1894 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1895 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1896 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1897 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1898 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, 0, collision_extendmovelength.value);
1899 if (trace.startsolid)
1900 {
1901 vec3_t move2;
1902 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1903 {
1904 // hack to invoke all necessary movement triggers
1905 VectorClear(move2);
1906 if(!SV_PushEntity(&trace2, check, move2, true))
1907 {
1908 // entity "check" got teleported
1909 continue;
1910 }
1911 // we could fix it
1912 continue;
1913 }
1914
1915 // still inside pusher, so it's really blocked
1916
1917 // fail the move
1918 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1919 continue;
1920 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1921 {
1922 // corpse
1923 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1924 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1925 continue;
1926 }
1927
1928 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1929 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1930 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1931 SV_LinkEdict(pusher);
1932
1933 // move back any entities we already moved
1934 for (i = 0;i < num_moved;i++)
1935 {
1936 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1937 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1938 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1939 SV_LinkEdict(ed);
1940 }
1941
1942 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1943 if (PRVM_serveredictfunction(pusher, blocked))
1944 {
1945 PRVM_serverglobalfloat(time) = sv.time;
1946 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1947 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1948 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1949 }
1950 break;
1951 }
1952 }
1953 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1954 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1955 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1956 }
1957
1958 /*
1959 ================
1960 SV_Physics_Pusher
1961
1962 ================
1963 */
SV_Physics_Pusher(prvm_edict_t * ent)1964 static void SV_Physics_Pusher (prvm_edict_t *ent)
1965 {
1966 prvm_prog_t *prog = SVVM_prog;
1967 float thinktime, oldltime, movetime;
1968
1969 oldltime = PRVM_serveredictfloat(ent, ltime);
1970
1971 thinktime = PRVM_serveredictfloat(ent, nextthink);
1972 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1973 {
1974 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1975 if (movetime < 0)
1976 movetime = 0;
1977 }
1978 else
1979 movetime = sv.frametime;
1980
1981 if (movetime)
1982 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
1983 SV_PushMove (ent, movetime);
1984
1985 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
1986 {
1987 PRVM_serveredictfloat(ent, nextthink) = 0;
1988 PRVM_serverglobalfloat(time) = sv.time;
1989 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1990 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1991 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1992 }
1993 }
1994
1995
1996 /*
1997 ===============================================================================
1998
1999 CLIENT MOVEMENT
2000
2001 ===============================================================================
2002 */
2003
2004 static float unstickoffsets[] =
2005 {
2006 // poutting -/+z changes first as they are least weird
2007 0, 0, -1,
2008 0, 0, 1,
2009 // x or y changes
2010 -1, 0, 0,
2011 1, 0, 0,
2012 0, -1, 0,
2013 0, 1, 0,
2014 // x and y changes
2015 -1, -1, 0,
2016 1, -1, 0,
2017 -1, 1, 0,
2018 1, 1, 0,
2019 };
2020
2021 typedef enum unstickresult_e
2022 {
2023 UNSTICK_STUCK = 0,
2024 UNSTICK_GOOD = 1,
2025 UNSTICK_UNSTUCK = 2
2026 }
2027 unstickresult_t;
2028
SV_UnstickEntityReturnOffset(prvm_edict_t * ent,vec3_t offset)2029 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2030 {
2031 prvm_prog_t *prog = SVVM_prog;
2032 int i, maxunstick;
2033
2034 // if not stuck in a bmodel, just return
2035 if (!SV_TestEntityPosition(ent, vec3_origin))
2036 return UNSTICK_GOOD;
2037
2038 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2039 {
2040 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2041 {
2042 VectorCopy(unstickoffsets + i, offset);
2043 SV_LinkEdict(ent);
2044 //SV_LinkEdict_TouchAreaGrid(ent);
2045 return UNSTICK_UNSTUCK;
2046 }
2047 }
2048
2049 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2050 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2051
2052 for(i = 2; i <= maxunstick; ++i)
2053 {
2054 VectorClear(offset);
2055 offset[2] = -i;
2056 if (!SV_TestEntityPosition(ent, offset))
2057 {
2058 SV_LinkEdict(ent);
2059 //SV_LinkEdict_TouchAreaGrid(ent);
2060 return UNSTICK_UNSTUCK;
2061 }
2062 offset[2] = i;
2063 if (!SV_TestEntityPosition(ent, offset))
2064 {
2065 SV_LinkEdict(ent);
2066 //SV_LinkEdict_TouchAreaGrid(ent);
2067 return UNSTICK_UNSTUCK;
2068 }
2069 }
2070
2071 return UNSTICK_STUCK;
2072 }
2073
SV_UnstickEntity(prvm_edict_t * ent)2074 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2075 {
2076 prvm_prog_t *prog = SVVM_prog;
2077 vec3_t offset;
2078 switch(SV_UnstickEntityReturnOffset(ent, offset))
2079 {
2080 case UNSTICK_GOOD:
2081 return true;
2082 case UNSTICK_UNSTUCK:
2083 Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2084 return true;
2085 case UNSTICK_STUCK:
2086 if (developer_extra.integer)
2087 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2088 return false;
2089 default:
2090 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2091 return false;
2092 }
2093 }
2094
2095 /*
2096 =============
2097 SV_CheckStuck
2098
2099 This is a big hack to try and fix the rare case of getting stuck in the world
2100 clipping hull.
2101 =============
2102 */
SV_CheckStuck(prvm_edict_t * ent)2103 static void SV_CheckStuck (prvm_edict_t *ent)
2104 {
2105 prvm_prog_t *prog = SVVM_prog;
2106 vec3_t offset;
2107
2108 switch(SV_UnstickEntityReturnOffset(ent, offset))
2109 {
2110 case UNSTICK_GOOD:
2111 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2112 break;
2113 case UNSTICK_UNSTUCK:
2114 Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2115 break;
2116 case UNSTICK_STUCK:
2117 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2118 if (!SV_TestEntityPosition(ent, offset))
2119 {
2120 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2121 SV_LinkEdict(ent);
2122 //SV_LinkEdict_TouchAreaGrid(ent);
2123 }
2124 else
2125 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2126 break;
2127 default:
2128 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2129 }
2130 }
2131
2132
2133 /*
2134 =============
2135 SV_CheckWater
2136 =============
2137 */
SV_CheckWater(prvm_edict_t * ent)2138 static qboolean SV_CheckWater (prvm_edict_t *ent)
2139 {
2140 prvm_prog_t *prog = SVVM_prog;
2141 int cont;
2142 int nNativeContents;
2143 vec3_t point;
2144
2145 point[0] = PRVM_serveredictvector(ent, origin)[0];
2146 point[1] = PRVM_serveredictvector(ent, origin)[1];
2147 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2148
2149 // DRESK - Support for Entity Contents Transition Event
2150 // NOTE: Some logic needed to be slightly re-ordered
2151 // to not affect performance and allow for the feature.
2152
2153 // Acquire Super Contents Prior to Resets
2154 cont = SV_PointSuperContents(point);
2155 // Acquire Native Contents Here
2156 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2157
2158 // DRESK - Support for Entity Contents Transition Event
2159 if(PRVM_serveredictfloat(ent, watertype))
2160 // Entity did NOT Spawn; Check
2161 SV_CheckContentsTransition(ent, nNativeContents);
2162
2163
2164 PRVM_serveredictfloat(ent, waterlevel) = 0;
2165 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2166 cont = SV_PointSuperContents(point);
2167 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2168 {
2169 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2170 PRVM_serveredictfloat(ent, waterlevel) = 1;
2171 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2172 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2173 {
2174 PRVM_serveredictfloat(ent, waterlevel) = 2;
2175 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2176 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2177 PRVM_serveredictfloat(ent, waterlevel) = 3;
2178 }
2179 }
2180
2181 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2182 }
2183
2184 /*
2185 ============
2186 SV_WallFriction
2187
2188 ============
2189 */
SV_WallFriction(prvm_edict_t * ent,float * stepnormal)2190 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2191 {
2192 prvm_prog_t *prog = SVVM_prog;
2193 float d, i;
2194 vec3_t forward, into, side, v_angle;
2195
2196 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2197 AngleVectors (v_angle, forward, NULL, NULL);
2198 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2199 {
2200 // cut the tangential velocity
2201 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2202 VectorScale (stepnormal, i, into);
2203 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2204 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2205 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2206 }
2207 }
2208
2209 #if 0
2210 /*
2211 =====================
2212 SV_TryUnstick
2213
2214 Player has come to a dead stop, possibly due to the problem with limited
2215 float precision at some angle joins in the BSP hull.
2216
2217 Try fixing by pushing one pixel in each direction.
2218
2219 This is a hack, but in the interest of good gameplay...
2220 ======================
2221 */
2222 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2223 {
2224 int i, clip;
2225 vec3_t oldorg, dir;
2226
2227 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2228 VectorClear (dir);
2229
2230 for (i=0 ; i<8 ; i++)
2231 {
2232 // try pushing a little in an axial direction
2233 switch (i)
2234 {
2235 case 0: dir[0] = 2; dir[1] = 0; break;
2236 case 1: dir[0] = 0; dir[1] = 2; break;
2237 case 2: dir[0] = -2; dir[1] = 0; break;
2238 case 3: dir[0] = 0; dir[1] = -2; break;
2239 case 4: dir[0] = 2; dir[1] = 2; break;
2240 case 5: dir[0] = -2; dir[1] = 2; break;
2241 case 6: dir[0] = 2; dir[1] = -2; break;
2242 case 7: dir[0] = -2; dir[1] = -2; break;
2243 }
2244
2245 SV_PushEntity (&trace, ent, dir, false, true);
2246
2247 // retry the original move
2248 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2249 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2250 PRVM_serveredictvector(ent, velocity)[2] = 0;
2251 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2252
2253 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2254 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2255 {
2256 Con_DPrint("TryUnstick - success.\n");
2257 return clip;
2258 }
2259
2260 // go back to the original pos and try again
2261 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2262 }
2263
2264 // still not moving
2265 VectorClear (PRVM_serveredictvector(ent, velocity));
2266 Con_DPrint("TryUnstick - failure.\n");
2267 return 7;
2268 }
2269 #endif
2270
2271 /*
2272 =====================
2273 SV_WalkMove
2274
2275 Only used by players
2276 ======================
2277 */
SV_WalkMove(prvm_edict_t * ent)2278 static void SV_WalkMove (prvm_edict_t *ent)
2279 {
2280 prvm_prog_t *prog = SVVM_prog;
2281 int clip;
2282 int oldonground;
2283 //int originalmove_clip;
2284 int originalmove_flags;
2285 int originalmove_groundentity;
2286 int hitsupercontentsmask;
2287 int skipsupercontentsmask;
2288 int type;
2289 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2290 trace_t downtrace, trace;
2291 qboolean applygravity;
2292
2293 // if frametime is 0 (due to client sending the same timestamp twice),
2294 // don't move
2295 if (sv.frametime <= 0)
2296 return;
2297
2298 if (sv_gameplayfix_unstickplayers.integer)
2299 SV_CheckStuck (ent);
2300
2301 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2302
2303 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2304 skipsupercontentsmask = 0;
2305
2306 SV_CheckVelocity(ent);
2307
2308 // do a regular slide move unless it looks like you ran into a step
2309 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2310
2311 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2312 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2313
2314 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2315
2316 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2317 if(!(clip & 1))
2318 {
2319 // only try this if there was no floor in the way in the trace (no,
2320 // this check seems to be not REALLY necessary, because if clip & 1,
2321 // our trace will hit that thing too)
2322 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2323 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2324 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2325 type = MOVE_MISSILE;
2326 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2327 type = MOVE_WORLDONLY;
2328 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2329 type = MOVE_NOMONSTERS; // only clip against bmodels
2330 else
2331 type = MOVE_NORMAL;
2332 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2333 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2334 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), skipsupercontentsmask, collision_extendmovelength.value);
2335 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2336 clip |= 1; // but we HAVE found a floor
2337 }
2338
2339 // if the move did not hit the ground at any point, we're not on ground
2340 if(!(clip & 1))
2341 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2342
2343 SV_CheckVelocity(ent);
2344 SV_LinkEdict(ent);
2345 SV_LinkEdict_TouchAreaGrid(ent);
2346
2347 if(clip & 8) // teleport
2348 return;
2349
2350 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2351 return;
2352
2353 if (sv_nostep.integer)
2354 return;
2355
2356 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2357 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2358 //originalmove_clip = clip;
2359 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2360 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2361
2362 // if move didn't block on a step, return
2363 if (clip & 2)
2364 {
2365 // if move was not trying to move into the step, return
2366 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2367 return;
2368
2369 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2370 {
2371 // return if gibbed by a trigger
2372 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2373 return;
2374
2375 // return if attempting to jump while airborn (unless sv_jumpstep)
2376 if (!sv_jumpstep.integer)
2377 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2378 return;
2379 }
2380
2381 // try moving up and forward to go up a step
2382 // back to start pos
2383 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2384 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2385
2386 // move up
2387 VectorClear (upmove);
2388 upmove[2] = sv_stepheight.value;
2389 if(!SV_PushEntity(&trace, ent, upmove, true))
2390 {
2391 // we got teleported when upstepping... must abort the move
2392 return;
2393 }
2394
2395 // move forward
2396 PRVM_serveredictvector(ent, velocity)[2] = 0;
2397 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, skipsupercontentsmask, 0);
2398 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2399 if(clip & 8)
2400 {
2401 // we got teleported when upstepping... must abort the move
2402 // note that z velocity handling may not be what QC expects here, but we cannot help it
2403 return;
2404 }
2405
2406 SV_CheckVelocity(ent);
2407 SV_LinkEdict(ent);
2408 SV_LinkEdict_TouchAreaGrid(ent);
2409
2410 // check for stuckness, possibly due to the limited precision of floats
2411 // in the clipping hulls
2412 if (clip
2413 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2414 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2415 {
2416 //Con_Printf("wall\n");
2417 // stepping up didn't make any progress, revert to original move
2418 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2419 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2420 //clip = originalmove_clip;
2421 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2422 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2423 // now try to unstick if needed
2424 //clip = SV_TryUnstick (ent, oldvel);
2425 return;
2426 }
2427
2428 //Con_Printf("step - ");
2429
2430 // extra friction based on view angle
2431 if (clip & 2 && sv_wallfriction.integer)
2432 SV_WallFriction (ent, stepnormal);
2433 }
2434 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2435 else if (!sv_gameplayfix_stepdown.integer || PRVM_serveredictfloat(ent, waterlevel) >= 3 || start_velocity[2] >= (1.0 / 32.0) || !oldonground || ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
2436 return;
2437
2438 // move down
2439 VectorClear (downmove);
2440 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2441 if(!SV_PushEntity (&downtrace, ent, downmove, true))
2442 {
2443 // we got teleported when downstepping... must abort the move
2444 return;
2445 }
2446
2447 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2448 {
2449 // this has been disabled so that you can't jump when you are stepping
2450 // up while already jumping (also known as the Quake2 double jump bug)
2451 #if 0
2452 // LordHavoc: disabled this check so you can walk on monsters/players
2453 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2454 {
2455 //Con_Printf("onground\n");
2456 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2457 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2458 }
2459 #endif
2460 }
2461 else
2462 {
2463 //Con_Printf("slope\n");
2464 // if the push down didn't end up on good ground, use the move without
2465 // the step up. This happens near wall / slope combinations, and can
2466 // cause the player to hop up higher on a slope too steep to climb
2467 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2468 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2469 //clip = originalmove_clip;
2470 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2471 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2472 }
2473
2474 SV_CheckVelocity(ent);
2475 SV_LinkEdict(ent);
2476 SV_LinkEdict_TouchAreaGrid(ent);
2477 }
2478
2479 //============================================================================
2480
2481 /*
2482 =============
2483 SV_Physics_Follow
2484
2485 Entities that are "stuck" to another entity
2486 =============
2487 */
SV_Physics_Follow(prvm_edict_t * ent)2488 static void SV_Physics_Follow (prvm_edict_t *ent)
2489 {
2490 prvm_prog_t *prog = SVVM_prog;
2491 vec3_t vf, vr, vu, angles, v;
2492 prvm_edict_t *e;
2493
2494 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2495 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2496 if (PRVM_serveredictvector(e, angles)[0] == PRVM_serveredictvector(ent, punchangle)[0] && PRVM_serveredictvector(e, angles)[1] == PRVM_serveredictvector(ent, punchangle)[1] && PRVM_serveredictvector(e, angles)[2] == PRVM_serveredictvector(ent, punchangle)[2])
2497 {
2498 // quick case for no rotation
2499 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2500 }
2501 else
2502 {
2503 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2504 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2505 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2506 AngleVectors (angles, vf, vr, vu);
2507 v[0] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[0] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[0] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[0];
2508 v[1] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[1] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[1] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[1];
2509 v[2] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[2] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[2] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[2];
2510 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2511 angles[1] = PRVM_serveredictvector(e, angles)[1];
2512 angles[2] = PRVM_serveredictvector(e, angles)[2];
2513 AngleVectors (angles, vf, vr, vu);
2514 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2515 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2516 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2517 }
2518 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2519 SV_LinkEdict(ent);
2520 //SV_LinkEdict_TouchAreaGrid(ent);
2521 }
2522
2523 /*
2524 ==============================================================================
2525
2526 TOSS / BOUNCE
2527
2528 ==============================================================================
2529 */
2530
2531 /*
2532 =============
2533 SV_CheckWaterTransition
2534
2535 =============
2536 */
SV_CheckWaterTransition(prvm_edict_t * ent)2537 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2538 {
2539 vec3_t entorigin;
2540 prvm_prog_t *prog = SVVM_prog;
2541 // LordHavoc: bugfixes in this function are keyed to the sv_gameplayfix_bugfixedcheckwatertransition cvar - if this cvar is 0 then all the original bugs should be reenabled for compatibility
2542 int cont;
2543 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2544 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(entorigin));
2545 if (!PRVM_serveredictfloat(ent, watertype))
2546 {
2547 // just spawned here
2548 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2549 {
2550 PRVM_serveredictfloat(ent, watertype) = cont;
2551 PRVM_serveredictfloat(ent, waterlevel) = 1;
2552 return;
2553 }
2554 }
2555 // DRESK - Support for Entity Contents Transition Event
2556 // NOTE: Call here BEFORE updating the watertype below,
2557 // and suppress watersplash sound if a valid function
2558 // call was made to allow for custom "splash" sounds.
2559 else if( !SV_CheckContentsTransition(ent, cont) )
2560 { // Contents Transition Function Invalid; Potentially Play Water Sound
2561 // check if the entity crossed into or out of water
2562 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2563 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2564 }
2565
2566 if (cont <= CONTENTS_WATER)
2567 {
2568 PRVM_serveredictfloat(ent, watertype) = cont;
2569 PRVM_serveredictfloat(ent, waterlevel) = 1;
2570 }
2571 else
2572 {
2573 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2574 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2575 }
2576 }
2577
2578 /*
2579 =============
2580 SV_Physics_Toss
2581
2582 Toss, bounce, and fly movement. When onground, do nothing.
2583 =============
2584 */
2585
SV_Physics_Toss(prvm_edict_t * ent)2586 void SV_Physics_Toss (prvm_edict_t *ent)
2587 {
2588 prvm_prog_t *prog = SVVM_prog;
2589 trace_t trace;
2590 vec3_t move;
2591 vec_t movetime;
2592 int bump;
2593 prvm_edict_t *groundentity;
2594 float d, ent_gravity;
2595 float bouncefactor;
2596 float bouncestop;
2597
2598 // if onground, return without moving
2599 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2600 {
2601 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2602 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2603 {
2604 // don't stick to ground if onground and moving upward
2605 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2606 }
2607 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2608 {
2609 // we can trust FL_ONGROUND if groundentity is world because it never moves
2610 return;
2611 }
2612 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2613 {
2614 // if ent was supported by a brush model on previous frame,
2615 // and groundentity is now freed, set groundentity to 0 (world)
2616 // which leaves it suspended in the air
2617 PRVM_serveredictedict(ent, groundentity) = 0;
2618 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2619 return;
2620 }
2621 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2622 {
2623 // don't slide if still touching the groundentity
2624 return;
2625 }
2626 }
2627 ent->priv.server->suspendedinairflag = false;
2628
2629 SV_CheckVelocity (ent);
2630
2631 // add gravity
2632 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2633 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2634
2635 // move angles
2636 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2637
2638 movetime = sv.frametime;
2639 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2640 {
2641 // move origin
2642 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2643 if(!SV_PushEntity(&trace, ent, move, true))
2644 return; // teleported
2645 if (ent->priv.server->free)
2646 return;
2647 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2648 {
2649 // try to unstick the entity
2650 SV_UnstickEntity(ent);
2651 if(!SV_PushEntity(&trace, ent, move, true))
2652 return; // teleported
2653 if (ent->priv.server->free)
2654 return;
2655 }
2656 if (trace.fraction == 1)
2657 break;
2658 movetime *= 1 - min(1, trace.fraction);
2659 switch((int)PRVM_serveredictfloat(ent, movetype))
2660 {
2661 case MOVETYPE_BOUNCEMISSILE:
2662 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2663 if (!bouncefactor)
2664 bouncefactor = 1.0f;
2665
2666 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2667 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2668 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2669 movetime = 0;
2670 break;
2671 case MOVETYPE_BOUNCE:
2672 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2673 if (!bouncefactor)
2674 bouncefactor = 0.5f;
2675
2676 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2677 if (!bouncestop)
2678 bouncestop = 60.0f / 800.0f;
2679
2680 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2681 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2682 if (!ent_gravity)
2683 ent_gravity = 1.0f;
2684 // LordHavoc: fixed grenades not bouncing when fired down a slope
2685 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2686 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2687 else
2688 d = PRVM_serveredictvector(ent, velocity)[2];
2689 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2690 {
2691 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2692 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2693 VectorClear(PRVM_serveredictvector(ent, velocity));
2694 VectorClear(PRVM_serveredictvector(ent, avelocity));
2695 movetime = 0;
2696 }
2697 else
2698 {
2699 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2700 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2701 movetime = 0;
2702 }
2703 break;
2704 default:
2705 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2706 if (trace.plane.normal[2] > 0.7)
2707 {
2708 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2709 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2710 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2711 ent->priv.server->suspendedinairflag = true;
2712 VectorClear (PRVM_serveredictvector(ent, velocity));
2713 VectorClear (PRVM_serveredictvector(ent, avelocity));
2714 movetime = 0;
2715 }
2716 else
2717 {
2718 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2719 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2720 movetime = 0;
2721 }
2722 break;
2723 }
2724 }
2725
2726 // check for in water
2727 SV_CheckWaterTransition (ent);
2728 }
2729
2730 /*
2731 ===============================================================================
2732
2733 STEPPING MOVEMENT
2734
2735 ===============================================================================
2736 */
2737
2738 /*
2739 =============
2740 SV_Physics_Step
2741
2742 Monsters freefall when they don't have a ground entity, otherwise
2743 all movement is done with discrete steps.
2744
2745 This is also used for objects that have become still on the ground, but
2746 will fall if the floor is pulled out from under them.
2747 =============
2748 */
SV_Physics_Step(prvm_edict_t * ent)2749 static void SV_Physics_Step (prvm_edict_t *ent)
2750 {
2751 prvm_prog_t *prog = SVVM_prog;
2752 int flags = (int)PRVM_serveredictfloat(ent, flags);
2753
2754 // DRESK
2755 // Backup Velocity in the event that movetypesteplandevent is called,
2756 // to provide a parameter with the entity's velocity at impact.
2757 vec3_t backupVelocity;
2758 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2759 // don't fall at all if fly/swim
2760 if (!(flags & (FL_FLY | FL_SWIM)))
2761 {
2762 if (flags & FL_ONGROUND)
2763 {
2764 // freefall if onground and moving upward
2765 // freefall if not standing on a world surface (it may be a lift or trap door)
2766 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2767 {
2768 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2769 SV_CheckVelocity(ent);
2770 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0);
2771 SV_LinkEdict(ent);
2772 SV_LinkEdict_TouchAreaGrid(ent);
2773 ent->priv.server->waterposition_forceupdate = true;
2774 }
2775 }
2776 else
2777 {
2778 // freefall if not onground
2779 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2780
2781 SV_CheckVelocity(ent);
2782 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0);
2783 SV_LinkEdict(ent);
2784 SV_LinkEdict_TouchAreaGrid(ent);
2785
2786 // just hit ground
2787 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2788 {
2789 // DRESK - Check for Entity Land Event Function
2790 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2791 { // Valid Function; Execute
2792 // Prepare Parameters
2793 // Assign Velocity at Impact
2794 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2795 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2796 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2797 // Assign Self
2798 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2799 // Set Time
2800 PRVM_serverglobalfloat(time) = sv.time;
2801 // Execute VM Function
2802 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2803 }
2804 else
2805 // Check for Engine Landing Sound
2806 if(sv_sound_land.string)
2807 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2808 }
2809 ent->priv.server->waterposition_forceupdate = true;
2810 }
2811 }
2812 }
2813
2814 //============================================================================
2815
SV_Physics_Entity(prvm_edict_t * ent)2816 static void SV_Physics_Entity (prvm_edict_t *ent)
2817 {
2818 prvm_prog_t *prog = SVVM_prog;
2819 // don't run think/move on newly spawned projectiles as it messes up
2820 // movement interpolation and rocket trails, and is inconsistent with
2821 // respect to entities spawned in the same frame
2822 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2823 // but if it spawns a lower numbered ent, it doesn't - this never moves
2824 // ents in the first frame regardless)
2825 qboolean runmove = ent->priv.server->move;
2826 ent->priv.server->move = true;
2827 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2828 return;
2829 switch ((int) PRVM_serveredictfloat(ent, movetype))
2830 {
2831 case MOVETYPE_PUSH:
2832 case MOVETYPE_FAKEPUSH:
2833 SV_Physics_Pusher (ent);
2834 break;
2835 case MOVETYPE_NONE:
2836 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2837 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2838 SV_RunThink (ent);
2839 break;
2840 case MOVETYPE_FOLLOW:
2841 if(SV_RunThink(ent))
2842 SV_Physics_Follow (ent);
2843 break;
2844 case MOVETYPE_NOCLIP:
2845 if (SV_RunThink(ent))
2846 {
2847 SV_CheckWater(ent);
2848 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2849 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2850 }
2851 SV_LinkEdict(ent);
2852 break;
2853 case MOVETYPE_STEP:
2854 SV_Physics_Step (ent);
2855 // regular thinking
2856 if (SV_RunThink(ent))
2857 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2858 {
2859 ent->priv.server->waterposition_forceupdate = false;
2860 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2861 SV_CheckWaterTransition(ent);
2862 }
2863 break;
2864 case MOVETYPE_WALK:
2865 if (SV_RunThink (ent))
2866 SV_WalkMove (ent);
2867 break;
2868 case MOVETYPE_TOSS:
2869 case MOVETYPE_BOUNCE:
2870 case MOVETYPE_BOUNCEMISSILE:
2871 case MOVETYPE_FLYMISSILE:
2872 case MOVETYPE_FLY:
2873 case MOVETYPE_FLY_WORLDONLY:
2874 // regular thinking
2875 if (SV_RunThink (ent))
2876 SV_Physics_Toss (ent);
2877 break;
2878 case MOVETYPE_PHYSICS:
2879 if (SV_RunThink(ent))
2880 {
2881 SV_LinkEdict(ent);
2882 SV_LinkEdict_TouchAreaGrid(ent);
2883 }
2884 break;
2885 default:
2886 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2887 break;
2888 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2889 break;
2890 }
2891 }
2892
SV_Physics_ClientEntity_NoThink(prvm_edict_t * ent)2893 static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
2894 {
2895 prvm_prog_t *prog = SVVM_prog;
2896
2897 // don't run think at all, that is done during server frames
2898 // instead, call the movetypes directly so they match client input
2899
2900 // This probably only makes sense for CSQC-networked (SendEntity field set) player entities
2901 switch ((int) PRVM_serveredictfloat(ent, movetype))
2902 {
2903 case MOVETYPE_PUSH:
2904 case MOVETYPE_FAKEPUSH:
2905 // push physics relies heavily on think times and calls, and so cannot be predicted currently
2906 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2907 break;
2908 case MOVETYPE_NONE:
2909 break;
2910 case MOVETYPE_FOLLOW:
2911 SV_Physics_Follow (ent);
2912 break;
2913 case MOVETYPE_NOCLIP:
2914 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2915 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2916 break;
2917 case MOVETYPE_STEP:
2918 SV_Physics_Step (ent);
2919 break;
2920 case MOVETYPE_WALK:
2921 SV_WalkMove (ent);
2922 break;
2923 case MOVETYPE_TOSS:
2924 case MOVETYPE_BOUNCE:
2925 case MOVETYPE_BOUNCEMISSILE:
2926 case MOVETYPE_FLYMISSILE:
2927 SV_Physics_Toss (ent);
2928 break;
2929 case MOVETYPE_FLY:
2930 case MOVETYPE_FLY_WORLDONLY:
2931 SV_WalkMove (ent);
2932 break;
2933 case MOVETYPE_PHYSICS:
2934 break;
2935 default:
2936 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2937 break;
2938 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2939 break;
2940 }
2941 }
2942
SV_Physics_ClientMove(void)2943 void SV_Physics_ClientMove(void)
2944 {
2945 prvm_prog_t *prog = SVVM_prog;
2946 prvm_edict_t *ent;
2947 ent = host_client->edict;
2948
2949 // call player physics, this needs the proper frametime
2950 PRVM_serverglobalfloat(frametime) = sv.frametime;
2951 SV_ClientThink();
2952
2953 // call standard client pre-think, with frametime = 0
2954 PRVM_serverglobalfloat(time) = sv.time;
2955 PRVM_serverglobalfloat(frametime) = 0;
2956 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2957 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2958 PRVM_serverglobalfloat(frametime) = sv.frametime;
2959
2960 // make sure the velocity is sane (not a NaN)
2961 SV_CheckVelocity(ent);
2962
2963 // perform movetype behaviour
2964 // note: will always be MOVETYPE_WALK if disableclientprediction = 0
2965 SV_Physics_ClientEntity_NoThink (ent);
2966
2967 // call standard player post-think, with frametime = 0
2968 PRVM_serverglobalfloat(time) = sv.time;
2969 PRVM_serverglobalfloat(frametime) = 0;
2970 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2971 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2972 PRVM_serverglobalfloat(frametime) = sv.frametime;
2973
2974 if(PRVM_serveredictfloat(ent, fixangle))
2975 {
2976 // angle fixing was requested by physics code...
2977 // so store the current angles for later use
2978 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2979 host_client->fixangle_angles_set = TRUE;
2980
2981 // and clear fixangle for the next frame
2982 PRVM_serveredictfloat(ent, fixangle) = 0;
2983 }
2984 }
2985
SV_Physics_ClientEntity_PreThink(prvm_edict_t * ent)2986 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2987 {
2988 prvm_prog_t *prog = SVVM_prog;
2989 // don't do physics on disconnected clients, FrikBot relies on this
2990 if (!host_client->begun)
2991 return;
2992
2993 // make sure the velocity is sane (not a NaN)
2994 SV_CheckVelocity(ent);
2995
2996 // don't run physics here if running asynchronously
2997 if (host_client->clmovement_inputtimeout <= 0)
2998 {
2999 SV_ClientThink();
3000 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
3001 }
3002
3003 // make sure the velocity is still sane (not a NaN)
3004 SV_CheckVelocity(ent);
3005
3006 // call standard client pre-think
3007 PRVM_serverglobalfloat(time) = sv.time;
3008 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3009 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3010
3011 // make sure the velocity is still sane (not a NaN)
3012 SV_CheckVelocity(ent);
3013 }
3014
SV_Physics_ClientEntity_PostThink(prvm_edict_t * ent)3015 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3016 {
3017 prvm_prog_t *prog = SVVM_prog;
3018 // don't do physics on disconnected clients, FrikBot relies on this
3019 if (!host_client->begun)
3020 return;
3021
3022 // make sure the velocity is sane (not a NaN)
3023 SV_CheckVelocity(ent);
3024
3025 // call standard player post-think
3026 PRVM_serverglobalfloat(time) = sv.time;
3027 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3028 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3029
3030 // make sure the velocity is still sane (not a NaN)
3031 SV_CheckVelocity(ent);
3032
3033 if(PRVM_serveredictfloat(ent, fixangle))
3034 {
3035 // angle fixing was requested by physics code...
3036 // so store the current angles for later use
3037 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3038 host_client->fixangle_angles_set = TRUE;
3039
3040 // and clear fixangle for the next frame
3041 PRVM_serveredictfloat(ent, fixangle) = 0;
3042 }
3043
3044 // decrement the countdown variable used to decide when to go back to
3045 // synchronous physics
3046 if (host_client->clmovement_inputtimeout > sv.frametime)
3047 host_client->clmovement_inputtimeout -= sv.frametime;
3048 else
3049 host_client->clmovement_inputtimeout = 0;
3050 }
3051
SV_Physics_ClientEntity(prvm_edict_t * ent)3052 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3053 {
3054 prvm_prog_t *prog = SVVM_prog;
3055 // don't do physics on disconnected clients, FrikBot relies on this
3056 if (!host_client->begun)
3057 {
3058 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3059 return;
3060 }
3061
3062 // make sure the velocity is sane (not a NaN)
3063 SV_CheckVelocity(ent);
3064
3065 switch ((int) PRVM_serveredictfloat(ent, movetype))
3066 {
3067 case MOVETYPE_PUSH:
3068 case MOVETYPE_FAKEPUSH:
3069 SV_Physics_Pusher (ent);
3070 break;
3071 case MOVETYPE_NONE:
3072 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3073 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3074 SV_RunThink (ent);
3075 break;
3076 case MOVETYPE_FOLLOW:
3077 SV_RunThink (ent);
3078 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3079 SV_Physics_Follow (ent);
3080 break;
3081 case MOVETYPE_NOCLIP:
3082 SV_RunThink(ent);
3083 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3084 {
3085 SV_CheckWater(ent);
3086 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3087 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3088 }
3089 break;
3090 case MOVETYPE_STEP:
3091 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3092 SV_Physics_Step (ent);
3093 if (SV_RunThink(ent))
3094 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
3095 {
3096 ent->priv.server->waterposition_forceupdate = false;
3097 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
3098 SV_CheckWaterTransition(ent);
3099 }
3100 break;
3101 case MOVETYPE_WALK:
3102 SV_RunThink (ent);
3103 // don't run physics here if running asynchronously
3104 if (host_client->clmovement_inputtimeout <= 0)
3105 SV_WalkMove (ent);
3106 break;
3107 case MOVETYPE_TOSS:
3108 case MOVETYPE_BOUNCE:
3109 case MOVETYPE_BOUNCEMISSILE:
3110 case MOVETYPE_FLYMISSILE:
3111 // regular thinking
3112 SV_RunThink (ent);
3113 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3114 SV_Physics_Toss (ent);
3115 break;
3116 case MOVETYPE_FLY:
3117 case MOVETYPE_FLY_WORLDONLY:
3118 SV_RunThink (ent);
3119 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3120 SV_WalkMove (ent);
3121 break;
3122 case MOVETYPE_PHYSICS:
3123 SV_RunThink (ent);
3124 break;
3125 default:
3126 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
3127 break;
3128 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3129 break;
3130 }
3131
3132 SV_CheckVelocity (ent);
3133
3134 SV_LinkEdict(ent);
3135 SV_LinkEdict_TouchAreaGrid(ent);
3136
3137 SV_CheckVelocity (ent);
3138 }
3139
3140 /*
3141 ================
3142 SV_Physics
3143
3144 ================
3145 */
SV_Physics(void)3146 void SV_Physics (void)
3147 {
3148 prvm_prog_t *prog = SVVM_prog;
3149 int i;
3150 prvm_edict_t *ent;
3151
3152 // let the progs know that a new frame has started
3153 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3154 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3155 PRVM_serverglobalfloat(time) = sv.time;
3156 PRVM_serverglobalfloat(frametime) = sv.frametime;
3157 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3158
3159 // run physics engine
3160 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3161
3162 //
3163 // treat each object in turn
3164 //
3165
3166 // if force_retouch, relink all the entities
3167 if (PRVM_serverglobalfloat(force_retouch) > 0)
3168 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3169 if (!ent->priv.server->free)
3170 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3171
3172 if (sv_gameplayfix_consistentplayerprethink.integer)
3173 {
3174 // run physics on the client entities in 3 stages
3175 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3176 if (!ent->priv.server->free)
3177 SV_Physics_ClientEntity_PreThink(ent);
3178
3179 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3180 if (!ent->priv.server->free)
3181 SV_Physics_ClientEntity(ent);
3182
3183 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3184 if (!ent->priv.server->free)
3185 SV_Physics_ClientEntity_PostThink(ent);
3186 }
3187 else
3188 {
3189 // run physics on the client entities
3190 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3191 {
3192 if (!ent->priv.server->free)
3193 {
3194 SV_Physics_ClientEntity_PreThink(ent);
3195 SV_Physics_ClientEntity(ent);
3196 SV_Physics_ClientEntity_PostThink(ent);
3197 }
3198 }
3199 }
3200
3201 // run physics on all the non-client entities
3202 if (!sv_freezenonclients.integer)
3203 {
3204 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3205 if (!ent->priv.server->free)
3206 SV_Physics_Entity(ent);
3207 // make a second pass to see if any ents spawned this frame and make
3208 // sure they run their move/think
3209 if (sv_gameplayfix_delayprojectiles.integer < 0)
3210 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3211 if (!ent->priv.server->move && !ent->priv.server->free)
3212 SV_Physics_Entity(ent);
3213 }
3214
3215 if (PRVM_serverglobalfloat(force_retouch) > 0)
3216 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3217
3218 // LordHavoc: endframe support
3219 if (PRVM_serverfunction(EndFrame))
3220 {
3221 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3222 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3223 PRVM_serverglobalfloat(time) = sv.time;
3224 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3225 }
3226
3227 // decrement prog->num_edicts if the highest number entities died
3228 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3229
3230 if (!sv_freezenonclients.integer)
3231 sv.time += sv.frametime;
3232 }
3233