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