]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
Corrected two comments regarding IBSP version of the Q2 bsp format.
[xonotic/darkplaces.git] / sv_phys.c
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
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
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 */
104 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
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);
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);
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, 0.0f);
235                 else
236                         Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
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 */
250 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, 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);
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, 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, extend);
384                 else
385                         Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, 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
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, 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, 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);
439                 else
440                         trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, 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, 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, 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, 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
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)
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);
580         if (passedict)
581         {
582                 VectorCopy(trace.endpos, temp);
583                 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).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
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
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
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
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
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 */
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 */
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, 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, 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 */
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 */
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 */
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 */
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
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
1179 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, 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);
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 */
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
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), 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
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), 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 */
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), 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)).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 */
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, 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, 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 */
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
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
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 */
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 */
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 */
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));
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 */
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 type;
2288         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2289         trace_t downtrace, trace;
2290         qboolean applygravity;
2291
2292         // if frametime is 0 (due to client sending the same timestamp twice),
2293         // don't move
2294         if (sv.frametime <= 0)
2295                 return;
2296
2297         if (sv_gameplayfix_unstickplayers.integer)
2298                 SV_CheckStuck (ent);
2299
2300         applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2301
2302         hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2303
2304         SV_CheckVelocity(ent);
2305
2306         // do a regular slide move unless it looks like you ran into a step
2307         oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2308
2309         VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2310         VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2311
2312         clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2313
2314         if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2315         if(!(clip & 1))
2316         {
2317                 // only try this if there was no floor in the way in the trace (no,
2318                 // this check seems to be not REALLY necessary, because if clip & 1,
2319                 // our trace will hit that thing too)
2320                 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2321                 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2322                 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2323                         type = MOVE_MISSILE;
2324                 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2325                         type = MOVE_WORLDONLY;
2326                 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2327                         type = MOVE_NOMONSTERS; // only clip against bmodels
2328                 else
2329                         type = MOVE_NORMAL;
2330                 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2331                 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2332                 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), collision_extendmovelength.value);
2333                 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2334                         clip |= 1; // but we HAVE found a floor
2335         }
2336
2337         // if the move did not hit the ground at any point, we're not on ground
2338         if(!(clip & 1))
2339                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2340
2341         SV_CheckVelocity(ent);
2342         SV_LinkEdict(ent);
2343         SV_LinkEdict_TouchAreaGrid(ent);
2344
2345         if(clip & 8) // teleport
2346                 return;
2347
2348         if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2349                 return;
2350
2351         if (sv_nostep.integer)
2352                 return;
2353
2354         VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2355         VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2356         //originalmove_clip = clip;
2357         originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2358         originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2359
2360         // if move didn't block on a step, return
2361         if (clip & 2)
2362         {
2363                 // if move was not trying to move into the step, return
2364                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2365                         return;
2366
2367                 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2368                 {
2369                         // return if gibbed by a trigger
2370                         if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2371                                 return;
2372
2373                         // return if attempting to jump while airborn (unless sv_jumpstep)
2374                         if (!sv_jumpstep.integer)
2375                                 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2376                                         return;
2377                 }
2378
2379                 // try moving up and forward to go up a step
2380                 // back to start pos
2381                 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2382                 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2383
2384                 // move up
2385                 VectorClear (upmove);
2386                 upmove[2] = sv_stepheight.value;
2387                 if(!SV_PushEntity(&trace, ent, upmove, true))
2388                 {
2389                         // we got teleported when upstepping... must abort the move
2390                         return;
2391                 }
2392
2393                 // move forward
2394                 PRVM_serveredictvector(ent, velocity)[2] = 0;
2395                 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2396                 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2397                 if(clip & 8)
2398                 {
2399                         // we got teleported when upstepping... must abort the move
2400                         // note that z velocity handling may not be what QC expects here, but we cannot help it
2401                         return;
2402                 }
2403
2404                 SV_CheckVelocity(ent);
2405                 SV_LinkEdict(ent);
2406                 SV_LinkEdict_TouchAreaGrid(ent);
2407
2408                 // check for stuckness, possibly due to the limited precision of floats
2409                 // in the clipping hulls
2410                 if (clip
2411                  && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2412                  && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2413                 {
2414                         //Con_Printf("wall\n");
2415                         // stepping up didn't make any progress, revert to original move
2416                         VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2417                         VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2418                         //clip = originalmove_clip;
2419                         PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2420                         PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2421                         // now try to unstick if needed
2422                         //clip = SV_TryUnstick (ent, oldvel);
2423                         return;
2424                 }
2425
2426                 //Con_Printf("step - ");
2427
2428                 // extra friction based on view angle
2429                 if (clip & 2 && sv_wallfriction.integer)
2430                         SV_WallFriction (ent, stepnormal);
2431         }
2432         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2433         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))
2434                 return;
2435
2436         // move down
2437         VectorClear (downmove);
2438         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2439         if(!SV_PushEntity (&downtrace, ent, downmove, true))
2440         {
2441                 // we got teleported when downstepping... must abort the move
2442                 return;
2443         }
2444
2445         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2446         {
2447                 // this has been disabled so that you can't jump when you are stepping
2448                 // up while already jumping (also known as the Quake2 double jump bug)
2449 #if 0
2450                 // LordHavoc: disabled this check so you can walk on monsters/players
2451                 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2452                 {
2453                         //Con_Printf("onground\n");
2454                         PRVM_serveredictfloat(ent, flags) =     (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2455                         PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2456                 }
2457 #endif
2458         }
2459         else
2460         {
2461                 //Con_Printf("slope\n");
2462                 // if the push down didn't end up on good ground, use the move without
2463                 // the step up.  This happens near wall / slope combinations, and can
2464                 // cause the player to hop up higher on a slope too steep to climb
2465                 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2466                 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2467                 //clip = originalmove_clip;
2468                 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2469                 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2470         }
2471
2472         SV_CheckVelocity(ent);
2473         SV_LinkEdict(ent);
2474         SV_LinkEdict_TouchAreaGrid(ent);
2475 }
2476
2477 //============================================================================
2478
2479 /*
2480 =============
2481 SV_Physics_Follow
2482
2483 Entities that are "stuck" to another entity
2484 =============
2485 */
2486 static void SV_Physics_Follow (prvm_edict_t *ent)
2487 {
2488         prvm_prog_t *prog = SVVM_prog;
2489         vec3_t vf, vr, vu, angles, v;
2490         prvm_edict_t *e;
2491
2492         // regular thinking
2493         if (!SV_RunThink (ent))
2494                 return;
2495
2496         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2497         e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2498         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])
2499         {
2500                 // quick case for no rotation
2501                 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2502         }
2503         else
2504         {
2505                 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2506                 angles[1] =  PRVM_serveredictvector(ent, punchangle)[1];
2507                 angles[2] =  PRVM_serveredictvector(ent, punchangle)[2];
2508                 AngleVectors (angles, vf, vr, vu);
2509                 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];
2510                 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];
2511                 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];
2512                 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2513                 angles[1] =  PRVM_serveredictvector(e, angles)[1];
2514                 angles[2] =  PRVM_serveredictvector(e, angles)[2];
2515                 AngleVectors (angles, vf, vr, vu);
2516                 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2517                 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2518                 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2519         }
2520         VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2521         SV_LinkEdict(ent);
2522         //SV_LinkEdict_TouchAreaGrid(ent);
2523 }
2524
2525 /*
2526 ==============================================================================
2527
2528 TOSS / BOUNCE
2529
2530 ==============================================================================
2531 */
2532
2533 /*
2534 =============
2535 SV_CheckWaterTransition
2536
2537 =============
2538 */
2539 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2540 {
2541         vec3_t entorigin;
2542         prvm_prog_t *prog = SVVM_prog;
2543         // 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
2544         int cont;
2545         VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2546         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(entorigin));
2547         if (!PRVM_serveredictfloat(ent, watertype))
2548         {
2549                 // just spawned here
2550                 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2551                 {
2552                         PRVM_serveredictfloat(ent, watertype) = cont;
2553                         PRVM_serveredictfloat(ent, waterlevel) = 1;
2554                         return;
2555                 }
2556         }
2557         // DRESK - Support for Entity Contents Transition Event
2558         // NOTE: Call here BEFORE updating the watertype below,
2559         // and suppress watersplash sound if a valid function
2560         // call was made to allow for custom "splash" sounds.
2561         else if( !SV_CheckContentsTransition(ent, cont) )
2562         { // Contents Transition Function Invalid; Potentially Play Water Sound
2563                 // check if the entity crossed into or out of water
2564                 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2565                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2566         }
2567
2568         if (cont <= CONTENTS_WATER)
2569         {
2570                 PRVM_serveredictfloat(ent, watertype) = cont;
2571                 PRVM_serveredictfloat(ent, waterlevel) = 1;
2572         }
2573         else
2574         {
2575                 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2576                 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2577         }
2578 }
2579
2580 /*
2581 =============
2582 SV_Physics_Toss
2583
2584 Toss, bounce, and fly movement.  When onground, do nothing.
2585 =============
2586 */
2587
2588 void SV_Physics_Toss (prvm_edict_t *ent)
2589 {
2590         prvm_prog_t *prog = SVVM_prog;
2591         trace_t trace;
2592         vec3_t move;
2593         vec_t movetime;
2594         int bump;
2595         prvm_edict_t *groundentity;
2596         float d, ent_gravity;
2597         float bouncefactor;
2598         float bouncestop;
2599
2600 // if onground, return without moving
2601         if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2602         {
2603                 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2604                 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2605                 {
2606                         // don't stick to ground if onground and moving upward
2607                         PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2608                 }
2609                 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2610                 {
2611                         // we can trust FL_ONGROUND if groundentity is world because it never moves
2612                         return;
2613                 }
2614                 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2615                 {
2616                         // if ent was supported by a brush model on previous frame,
2617                         // and groundentity is now freed, set groundentity to 0 (world)
2618                         // which leaves it suspended in the air
2619                         PRVM_serveredictedict(ent, groundentity) = 0;
2620                         if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2621                                 return;
2622                 }
2623                 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2624                 {
2625                         // don't slide if still touching the groundentity
2626                         return;
2627                 }
2628         }
2629         ent->priv.server->suspendedinairflag = false;
2630
2631         SV_CheckVelocity (ent);
2632
2633 // add gravity
2634         if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2635                 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2636
2637 // move angles
2638         VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2639
2640         movetime = sv.frametime;
2641         for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2642         {
2643         // move origin
2644                 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2645                 if(!SV_PushEntity(&trace, ent, move, true))
2646                         return; // teleported
2647                 if (ent->priv.server->free)
2648                         return;
2649                 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2650                 {
2651                         // try to unstick the entity
2652                         SV_UnstickEntity(ent);
2653                         if(!SV_PushEntity(&trace, ent, move, true))
2654                                 return; // teleported
2655                         if (ent->priv.server->free)
2656                                 return;
2657                 }
2658                 if (trace.fraction == 1)
2659                         break;
2660                 movetime *= 1 - min(1, trace.fraction);
2661                 switch((int)PRVM_serveredictfloat(ent, movetype))
2662                 {
2663                 case MOVETYPE_BOUNCEMISSILE:
2664                         bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2665                         if (!bouncefactor)
2666                                 bouncefactor = 1.0f;
2667
2668                         ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2669                         PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2670                         if (!sv_gameplayfix_slidemoveprojectiles.integer)
2671                                 movetime = 0;
2672                         break;
2673                 case MOVETYPE_BOUNCE:
2674                         bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2675                         if (!bouncefactor)
2676                                 bouncefactor = 0.5f;
2677
2678                         bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2679                         if (!bouncestop)
2680                                 bouncestop = 60.0f / 800.0f;
2681
2682                         ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2683                         ent_gravity = PRVM_serveredictfloat(ent, gravity);
2684                         if (!ent_gravity)
2685                                 ent_gravity = 1.0f;
2686                         // LordHavoc: fixed grenades not bouncing when fired down a slope
2687                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
2688                                 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2689                         else
2690                                 d = PRVM_serveredictvector(ent, velocity)[2];
2691                         if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2692                         {
2693                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2694                                 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2695                                 VectorClear(PRVM_serveredictvector(ent, velocity));
2696                                 VectorClear(PRVM_serveredictvector(ent, avelocity));
2697                                 movetime = 0;
2698                         }
2699                         else
2700                         {
2701                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2702                                 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2703                                         movetime = 0;
2704                         }
2705                         break;
2706                 default:
2707                         ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2708                         if (trace.plane.normal[2] > 0.7)
2709                         {
2710                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2711                                 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2712                                 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2713                                         ent->priv.server->suspendedinairflag = true;
2714                                 VectorClear (PRVM_serveredictvector(ent, velocity));
2715                                 VectorClear (PRVM_serveredictvector(ent, avelocity));
2716                         }
2717                         else
2718                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2719                         movetime = 0;
2720                         break;
2721                 }
2722         }
2723
2724 // check for in water
2725         SV_CheckWaterTransition (ent);
2726 }
2727
2728 /*
2729 ===============================================================================
2730
2731 STEPPING MOVEMENT
2732
2733 ===============================================================================
2734 */
2735
2736 /*
2737 =============
2738 SV_Physics_Step
2739
2740 Monsters freefall when they don't have a ground entity, otherwise
2741 all movement is done with discrete steps.
2742
2743 This is also used for objects that have become still on the ground, but
2744 will fall if the floor is pulled out from under them.
2745 =============
2746 */
2747 static void SV_Physics_Step (prvm_edict_t *ent)
2748 {
2749         prvm_prog_t *prog = SVVM_prog;
2750         int flags = (int)PRVM_serveredictfloat(ent, flags);
2751
2752         // DRESK
2753         // Backup Velocity in the event that movetypesteplandevent is called,
2754         // to provide a parameter with the entity's velocity at impact.
2755         vec3_t backupVelocity;
2756         VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2757         // don't fall at all if fly/swim
2758         if (!(flags & (FL_FLY | FL_SWIM)))
2759         {
2760                 if (flags & FL_ONGROUND)
2761                 {
2762                         // freefall if onground and moving upward
2763                         // freefall if not standing on a world surface (it may be a lift or trap door)
2764                         if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2765                         {
2766                                 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2767                                 SV_CheckVelocity(ent);
2768                                 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2769                                 SV_LinkEdict(ent);
2770                                 SV_LinkEdict_TouchAreaGrid(ent);
2771                                 ent->priv.server->waterposition_forceupdate = true;
2772                         }
2773                 }
2774                 else
2775                 {
2776                         // freefall if not onground
2777                         int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2778
2779                         SV_CheckVelocity(ent);
2780                         SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2781                         SV_LinkEdict(ent);
2782                         SV_LinkEdict_TouchAreaGrid(ent);
2783
2784                         // just hit ground
2785                         if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2786                         {
2787                                 // DRESK - Check for Entity Land Event Function
2788                                 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2789                                 { // Valid Function; Execute
2790                                         // Prepare Parameters
2791                                         // Assign Velocity at Impact
2792                                         PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2793                                         PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2794                                         PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2795                                         // Assign Self
2796                                         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2797                                         // Set Time
2798                                         PRVM_serverglobalfloat(time) = sv.time;
2799                                         // Execute VM Function
2800                                         prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2801                                 }
2802                                 else
2803                                 // Check for Engine Landing Sound
2804                                 if(sv_sound_land.string)
2805                                         SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2806                         }
2807                         ent->priv.server->waterposition_forceupdate = true;
2808                 }
2809         }
2810
2811 // regular thinking
2812         if (!SV_RunThink(ent))
2813                 return;
2814
2815         if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2816         {
2817                 ent->priv.server->waterposition_forceupdate = false;
2818                 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2819                 SV_CheckWaterTransition(ent);
2820         }
2821 }
2822
2823 //============================================================================
2824
2825 static void SV_Physics_Entity (prvm_edict_t *ent)
2826 {
2827         prvm_prog_t *prog = SVVM_prog;
2828         // don't run think/move on newly spawned projectiles as it messes up
2829         // movement interpolation and rocket trails, and is inconsistent with
2830         // respect to entities spawned in the same frame
2831         // (if an ent spawns a higher numbered ent, it moves in the same frame,
2832         //  but if it spawns a lower numbered ent, it doesn't - this never moves
2833         //  ents in the first frame regardless)
2834         qboolean runmove = ent->priv.server->move;
2835         ent->priv.server->move = true;
2836         if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2837                 return;
2838         switch ((int) PRVM_serveredictfloat(ent, movetype))
2839         {
2840         case MOVETYPE_PUSH:
2841         case MOVETYPE_FAKEPUSH:
2842                 SV_Physics_Pusher (ent);
2843                 break;
2844         case MOVETYPE_NONE:
2845                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2846                 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2847                         SV_RunThink (ent);
2848                 break;
2849         case MOVETYPE_FOLLOW:
2850                 SV_Physics_Follow (ent);
2851                 break;
2852         case MOVETYPE_NOCLIP:
2853                 if (SV_RunThink(ent))
2854                 {
2855                         SV_CheckWater(ent);
2856                         VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2857                         VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2858                 }
2859                 SV_LinkEdict(ent);
2860                 break;
2861         case MOVETYPE_STEP:
2862                 SV_Physics_Step (ent);
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                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2887                 break;
2888         }
2889 }
2890
2891 void SV_Physics_ClientMove(void)
2892 {
2893         prvm_prog_t *prog = SVVM_prog;
2894         prvm_edict_t *ent;
2895         ent = host_client->edict;
2896
2897         // call player physics, this needs the proper frametime
2898         PRVM_serverglobalfloat(frametime) = sv.frametime;
2899         SV_ClientThink();
2900
2901         // call standard client pre-think, with frametime = 0
2902         PRVM_serverglobalfloat(time) = sv.time;
2903         PRVM_serverglobalfloat(frametime) = 0;
2904         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2905         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2906         PRVM_serverglobalfloat(frametime) = sv.frametime;
2907
2908         // make sure the velocity is sane (not a NaN)
2909         SV_CheckVelocity(ent);
2910
2911         // perform MOVETYPE_WALK behavior
2912         SV_WalkMove (ent);
2913
2914         // call standard player post-think, with frametime = 0
2915         PRVM_serverglobalfloat(time) = sv.time;
2916         PRVM_serverglobalfloat(frametime) = 0;
2917         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2918         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2919         PRVM_serverglobalfloat(frametime) = sv.frametime;
2920
2921         if(PRVM_serveredictfloat(ent, fixangle))
2922         {
2923                 // angle fixing was requested by physics code...
2924                 // so store the current angles for later use
2925                 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2926                 host_client->fixangle_angles_set = TRUE;
2927
2928                 // and clear fixangle for the next frame
2929                 PRVM_serveredictfloat(ent, fixangle) = 0;
2930         }
2931 }
2932
2933 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2934 {
2935         prvm_prog_t *prog = SVVM_prog;
2936         // don't do physics on disconnected clients, FrikBot relies on this
2937         if (!host_client->begun)
2938                 return;
2939
2940         // make sure the velocity is sane (not a NaN)
2941         SV_CheckVelocity(ent);
2942
2943         // don't run physics here if running asynchronously
2944         if (host_client->clmovement_inputtimeout <= 0)
2945         {
2946                 SV_ClientThink();
2947                 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2948         }
2949
2950         // make sure the velocity is still sane (not a NaN)
2951         SV_CheckVelocity(ent);
2952
2953         // call standard client pre-think
2954         PRVM_serverglobalfloat(time) = sv.time;
2955         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2956         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2957
2958         // make sure the velocity is still sane (not a NaN)
2959         SV_CheckVelocity(ent);
2960 }
2961
2962 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2963 {
2964         prvm_prog_t *prog = SVVM_prog;
2965         // don't do physics on disconnected clients, FrikBot relies on this
2966         if (!host_client->begun)
2967                 return;
2968
2969         // make sure the velocity is sane (not a NaN)
2970         SV_CheckVelocity(ent);
2971
2972         // call standard player post-think
2973         PRVM_serverglobalfloat(time) = sv.time;
2974         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2975         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2976
2977         // make sure the velocity is still sane (not a NaN)
2978         SV_CheckVelocity(ent);
2979
2980         if(PRVM_serveredictfloat(ent, fixangle))
2981         {
2982                 // angle fixing was requested by physics code...
2983                 // so store the current angles for later use
2984                 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2985                 host_client->fixangle_angles_set = TRUE;
2986
2987                 // and clear fixangle for the next frame
2988                 PRVM_serveredictfloat(ent, fixangle) = 0;
2989         }
2990
2991         // decrement the countdown variable used to decide when to go back to
2992         // synchronous physics
2993         if (host_client->clmovement_inputtimeout > sv.frametime)
2994                 host_client->clmovement_inputtimeout -= sv.frametime;
2995         else
2996                 host_client->clmovement_inputtimeout = 0;
2997 }
2998
2999 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3000 {
3001         prvm_prog_t *prog = SVVM_prog;
3002         // don't do physics on disconnected clients, FrikBot relies on this
3003         if (!host_client->begun)
3004         {
3005                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3006                 return;
3007         }
3008
3009         // make sure the velocity is sane (not a NaN)
3010         SV_CheckVelocity(ent);
3011
3012         switch ((int) PRVM_serveredictfloat(ent, movetype))
3013         {
3014         case MOVETYPE_PUSH:
3015         case MOVETYPE_FAKEPUSH:
3016                 SV_Physics_Pusher (ent);
3017                 break;
3018         case MOVETYPE_NONE:
3019                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3020                 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3021                         SV_RunThink (ent);
3022                 break;
3023         case MOVETYPE_FOLLOW:
3024                 SV_Physics_Follow (ent);
3025                 break;
3026         case MOVETYPE_NOCLIP:
3027                 SV_RunThink(ent);
3028                 SV_CheckWater(ent);
3029                 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3030                 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3031                 break;
3032         case MOVETYPE_STEP:
3033                 SV_Physics_Step (ent);
3034                 break;
3035         case MOVETYPE_WALK:
3036                 SV_RunThink (ent);
3037                 // don't run physics here if running asynchronously
3038                 if (host_client->clmovement_inputtimeout <= 0)
3039                         SV_WalkMove (ent);
3040                 break;
3041         case MOVETYPE_TOSS:
3042         case MOVETYPE_BOUNCE:
3043         case MOVETYPE_BOUNCEMISSILE:
3044         case MOVETYPE_FLYMISSILE:
3045                 // regular thinking
3046                 SV_RunThink (ent);
3047                 SV_Physics_Toss (ent);
3048                 break;
3049         case MOVETYPE_FLY:
3050         case MOVETYPE_FLY_WORLDONLY:
3051                 SV_RunThink (ent);
3052                 SV_WalkMove (ent);
3053                 break;
3054         case MOVETYPE_PHYSICS:
3055                 SV_RunThink (ent);
3056                 break;
3057         default:
3058                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3059                 break;
3060         }
3061
3062         SV_CheckVelocity (ent);
3063
3064         SV_LinkEdict(ent);
3065         SV_LinkEdict_TouchAreaGrid(ent);
3066
3067         SV_CheckVelocity (ent);
3068 }
3069
3070 /*
3071 ================
3072 SV_Physics
3073
3074 ================
3075 */
3076 void SV_Physics (void)
3077 {
3078         prvm_prog_t *prog = SVVM_prog;
3079         int i;
3080         prvm_edict_t *ent;
3081
3082 // let the progs know that a new frame has started
3083         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3084         PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3085         PRVM_serverglobalfloat(time) = sv.time;
3086         PRVM_serverglobalfloat(frametime) = sv.frametime;
3087         prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3088
3089         // run physics engine
3090         World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3091
3092 //
3093 // treat each object in turn
3094 //
3095
3096         // if force_retouch, relink all the entities
3097         if (PRVM_serverglobalfloat(force_retouch) > 0)
3098                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3099                         if (!ent->priv.server->free)
3100                                 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3101
3102         if (sv_gameplayfix_consistentplayerprethink.integer)
3103         {
3104                 // run physics on the client entities in 3 stages
3105                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3106                         if (!ent->priv.server->free)
3107                                 SV_Physics_ClientEntity_PreThink(ent);
3108
3109                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3110                         if (!ent->priv.server->free)
3111                                 SV_Physics_ClientEntity(ent);
3112
3113                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3114                         if (!ent->priv.server->free)
3115                                 SV_Physics_ClientEntity_PostThink(ent);
3116         }
3117         else
3118         {
3119                 // run physics on the client entities
3120                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3121                 {
3122                         if (!ent->priv.server->free)
3123                         {
3124                                 SV_Physics_ClientEntity_PreThink(ent);
3125                                 SV_Physics_ClientEntity(ent);
3126                                 SV_Physics_ClientEntity_PostThink(ent);
3127                         }
3128                 }
3129         }
3130
3131         // run physics on all the non-client entities
3132         if (!sv_freezenonclients.integer)
3133         {
3134                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3135                         if (!ent->priv.server->free)
3136                                 SV_Physics_Entity(ent);
3137                 // make a second pass to see if any ents spawned this frame and make
3138                 // sure they run their move/think
3139                 if (sv_gameplayfix_delayprojectiles.integer < 0)
3140                         for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3141                                 if (!ent->priv.server->move && !ent->priv.server->free)
3142                                         SV_Physics_Entity(ent);
3143         }
3144
3145         if (PRVM_serverglobalfloat(force_retouch) > 0)
3146                 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3147
3148         // LordHavoc: endframe support
3149         if (PRVM_serverfunction(EndFrame))
3150         {
3151                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3152                 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3153                 PRVM_serverglobalfloat(time) = sv.time;
3154                 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3155         }
3156
3157         // decrement prog->num_edicts if the highest number entities died
3158         for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3159
3160         if (!sv_freezenonclients.integer)
3161                 sv.time += sv.frametime;
3162 }