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