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