]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
Merge PR 'sv_gameplayfix_stepmultipletimes 1: Make stepping up while jumping reliable'
[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                 if (trace.plane.normal[2])
1259                 {
1260                         if (trace.plane.normal[2] > 0.7)
1261                         {
1262                                 // floor
1263                                 blocked |= 1;
1264
1265                                 if (!trace.ent)
1266                                 {
1267                                         Con_Printf ("SV_FlyMove: !trace.ent");
1268                                         trace.ent = prog->edicts;
1269                                 }
1270
1271                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1272                                 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1273                         }
1274                 }
1275                 else if (stepheight)
1276                 {
1277                         // step - handle it immediately
1278                         vec3_t org;
1279                         vec3_t steppush;
1280                         trace_t steptrace;
1281                         trace_t steptrace2;
1282                         trace_t steptrace3;
1283                         //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1284                         VectorSet(steppush, 0, 0, stepheight);
1285                         VectorCopy(PRVM_serveredictvector(ent, origin), org);
1286                         if(!SV_PushEntity(&steptrace, ent, steppush, false))
1287                         {
1288                                 blocked |= 8;
1289                                 break;
1290                         }
1291                         //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1292                         if(!SV_PushEntity(&steptrace2, ent, push, false))
1293                         {
1294                                 blocked |= 8;
1295                                 break;
1296                         }
1297                         //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1298                         VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1299                         if(!SV_PushEntity(&steptrace3, ent, steppush, false))
1300                         {
1301                                 blocked |= 8;
1302                                 break;
1303                         }
1304                         //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1305                         // accept the new position if it made some progress...
1306                         // previously this checked if absolute distance >= 0.03125 which made stepping up unreliable
1307                         if (PRVM_serveredictvector(ent, origin)[0] - org[0] || PRVM_serveredictvector(ent, origin)[1] - org[1])
1308                         {
1309                                 //Con_Printf("accepted (delta %f %f %f)\n", PRVM_serveredictvector(ent, origin)[0] - org[0], PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
1310                                 trace = steptrace2;
1311                                 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1312                                 time_left *= 1 - trace.fraction;
1313                                 numplanes = 0;
1314                                 continue;
1315                         }
1316                         else
1317                         {
1318                                 //Con_Printf("REJECTED (delta %f %f %f)\n", PRVM_serveredictvector(ent, origin)[0] - org[0], PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
1319                                 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1320                         }
1321                 }
1322                 else
1323                 {
1324                         // step - return it to caller
1325                         blocked |= 2;
1326                         // save the trace for player extrafriction
1327                         if (stepnormal)
1328                                 VectorCopy(trace.plane.normal, stepnormal);
1329                 }
1330                 if (trace.fraction >= 0.001)
1331                 {
1332                         // actually covered some distance
1333                         VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1334                         numplanes = 0;
1335                 }
1336
1337                 time_left *= 1 - trace.fraction;
1338
1339                 // clipped to another plane
1340                 if (numplanes >= MAX_CLIP_PLANES)
1341                 {
1342                         // this shouldn't really happen
1343                         VectorClear(PRVM_serveredictvector(ent, velocity));
1344                         blocked = 3;
1345                         break;
1346                 }
1347
1348                 /*
1349                 for (i = 0;i < numplanes;i++)
1350                         if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1351                                 break;
1352                 if (i < numplanes)
1353                 {
1354                         VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1355                         continue;
1356                 }
1357                 */
1358
1359                 VectorCopy(trace.plane.normal, planes[numplanes]);
1360                 numplanes++;
1361
1362                 // modify original_velocity so it parallels all of the clip planes
1363                 for (i = 0;i < numplanes;i++)
1364                 {
1365                         ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1366                         for (j = 0;j < numplanes;j++)
1367                         {
1368                                 if (j != i)
1369                                 {
1370                                         // not ok
1371                                         if (DotProduct(new_velocity, planes[j]) < 0)
1372                                                 break;
1373                                 }
1374                         }
1375                         if (j == numplanes)
1376                                 break;
1377                 }
1378
1379                 if (i != numplanes)
1380                 {
1381                         // go along this plane
1382                         VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1383                 }
1384                 else
1385                 {
1386                         // go along the crease
1387                         if (numplanes != 2)
1388                         {
1389                                 VectorClear(PRVM_serveredictvector(ent, velocity));
1390                                 blocked = 7;
1391                                 break;
1392                         }
1393                         CrossProduct(planes[0], planes[1], dir);
1394                         // LadyHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1395                         VectorNormalize(dir);
1396                         d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1397                         VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1398                 }
1399
1400                 // if current velocity is against the original velocity,
1401                 // stop dead to avoid tiny occilations in sloping corners
1402                 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1403                 {
1404                         VectorClear(PRVM_serveredictvector(ent, velocity));
1405                         break;
1406                 }
1407         }
1408
1409         //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, PRVM_serveredictvector(ent, velocity)[0], PRVM_serveredictvector(ent, velocity)[1], PRVM_serveredictvector(ent, velocity)[2]);
1410
1411         /*
1412         if ((blocked & 1) == 0 && bumpcount > 1)
1413         {
1414                 // LadyHavoc: fix the 'fall to your death in a wedge corner' glitch
1415                 // flag ONGROUND if there's ground under it
1416                 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
1417         }
1418         */
1419
1420         // LadyHavoc: this came from QW and allows you to get out of water more easily
1421         if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1422                 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1423
1424         if(applygravity)
1425         {
1426                 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1427                 {
1428                         if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1429                                 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1430                 }
1431         }
1432
1433         return blocked;
1434 }
1435
1436 /*
1437 ============
1438 SV_Gravity
1439
1440 ============
1441 */
1442 static float SV_Gravity (prvm_edict_t *ent)
1443 {
1444         prvm_prog_t *prog = SVVM_prog;
1445         float ent_gravity;
1446
1447         ent_gravity = PRVM_serveredictfloat(ent, gravity);
1448         if (!ent_gravity)
1449                 ent_gravity = 1.0f;
1450         return ent_gravity * sv_gravity.value * sv.frametime;
1451 }
1452
1453
1454 /*
1455 ===============================================================================
1456
1457 PUSHMOVE
1458
1459 ===============================================================================
1460 */
1461
1462 static qbool SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1463 {
1464         prvm_prog_t *prog = SVVM_prog;
1465         int bump;
1466         trace_t stucktrace;
1467         vec3_t stuckorigin;
1468         vec3_t stuckmins, stuckmaxs;
1469         vec3_t goodmins, goodmaxs;
1470         vec3_t testorigin;
1471         vec_t nudge;
1472         vec3_t move;
1473         VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1474         VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1475         VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1476         VectorCopy(pivot, goodmins);
1477         VectorCopy(pivot, goodmaxs);
1478         for (bump = 0;bump < 6;bump++)
1479         {
1480                 int coord = 2-(bump >> 1);
1481                 //int coord = (bump >> 1);
1482                 int dir = (bump & 1);
1483                 int subbump;
1484
1485                 for(subbump = 0; ; ++subbump)
1486                 {
1487                         VectorCopy(stuckorigin, testorigin);
1488                         if(dir)
1489                         {
1490                                 // pushing maxs
1491                                 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1492                         }
1493                         else
1494                         {
1495                                 // pushing mins
1496                                 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1497                         }
1498
1499                         stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1500                         if (stucktrace.bmodelstartsolid)
1501                         {
1502                                 // BAD BAD, can't fix that
1503                                 return false;
1504                         }
1505
1506                         if (stucktrace.fraction >= 1)
1507                                 break; // it WORKS!
1508
1509                         if(subbump >= 10)
1510                         {
1511                                 // BAD BAD, can't fix that
1512                                 return false;
1513                         }
1514
1515                         // we hit something... let's move out of it
1516                         VectorSubtract(stucktrace.endpos, testorigin, move);
1517                         nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1518                         VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1519                 }
1520                 /*
1521                 if(subbump > 0)
1522                         Con_Printf("subbump: %d\n", subbump);
1523                 */
1524
1525                 if(dir)
1526                 {
1527                         // pushing maxs
1528                         goodmaxs[coord] = stuckmaxs[coord];
1529                 }
1530                 else
1531                 {
1532                         // pushing mins
1533                         goodmins[coord] = stuckmins[coord];
1534                 }
1535         }
1536
1537         // WE WIN
1538         VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1539
1540         return true;
1541 }
1542
1543 qbool SV_NudgeOutOfSolid(prvm_edict_t *ent)
1544 {
1545         prvm_prog_t *prog = SVVM_prog;
1546         int bump, pass;
1547         trace_t stucktrace;
1548         vec3_t stuckorigin;
1549         vec3_t stuckmins, stuckmaxs;
1550         vec_t nudge;
1551         vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1552         if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1553                 separation = 0.0f; // when using hulls, it can not be enlarged
1554         VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1555         VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1556         stuckmins[0] -= separation;
1557         stuckmins[1] -= separation;
1558         stuckmins[2] -= separation;
1559         stuckmaxs[0] += separation;
1560         stuckmaxs[1] += separation;
1561         stuckmaxs[2] += separation;
1562         // first pass we try to get it out of brush entities
1563         // second pass we try to get it out of world only (can't win them all)
1564         for (pass = 0;pass < 2;pass++)
1565         {
1566                 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1567                 for (bump = 0;bump < 10;bump++)
1568                 {
1569                         stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1570                         if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1571                         {
1572                                 // found a good location, use it
1573                                 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1574                                 return true;
1575                         }
1576                         nudge = -stucktrace.startdepth;
1577                         VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1578                 }
1579         }
1580         return false;
1581 }
1582
1583 /*
1584 ============
1585 SV_PushEntity
1586
1587 Does not change the entities velocity at all
1588 The trace struct is filled with the trace that has been done.
1589 Returns true if the push did not result in the entity being teleported by QC code.
1590 ============
1591 */
1592 static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink)
1593 {
1594         prvm_prog_t *prog = SVVM_prog;
1595         int solid;
1596         int movetype;
1597         int type;
1598         vec3_t mins, maxs;
1599         vec3_t start;
1600         vec3_t end;
1601
1602         solid = (int)PRVM_serveredictfloat(ent, solid);
1603         movetype = (int)PRVM_serveredictfloat(ent, movetype);
1604         VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1605         VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1606
1607         // move start position out of solids
1608         if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1609         {
1610                 SV_NudgeOutOfSolid(ent);
1611         }
1612
1613         VectorCopy(PRVM_serveredictvector(ent, origin), start);
1614         VectorAdd(start, push, end);
1615
1616         if (movetype == MOVETYPE_FLYMISSILE)
1617                 type = MOVE_MISSILE;
1618         else if (movetype == MOVETYPE_FLY_WORLDONLY)
1619                 type = MOVE_WORLDONLY;
1620         else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1621                 type = MOVE_NOMONSTERS; // only clip against bmodels
1622         else
1623                 type = MOVE_NORMAL;
1624
1625         *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1626         // fail the move if stuck in world
1627         if (trace->worldstartsolid)
1628                 return true;
1629
1630         VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1631
1632         ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1633
1634         SV_LinkEdict(ent);
1635
1636 #if 0
1637         if(!trace->startsolid)
1638         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)
1639         {
1640                 Con_Printf("something eeeeevil happened\n");
1641         }
1642 #endif
1643
1644         if (dolink)
1645                 SV_LinkEdict_TouchAreaGrid(ent);
1646
1647         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))))
1648                 SV_Impact (ent, trace);
1649
1650         if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1651         {
1652                 ent->priv.required->mark = 0;
1653                 return false;
1654         }
1655         else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1656         {
1657                 ent->priv.required->mark = 0;
1658                 return true;
1659         }
1660         else
1661         {
1662                 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1663                 return true;
1664         }
1665 }
1666
1667
1668 /*
1669 ============
1670 SV_PushMove
1671
1672 ============
1673 */
1674 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1675 {
1676         prvm_prog_t *prog = SVVM_prog;
1677         int i, e, index;
1678         int pusherowner, pusherprog;
1679         int checkcontents;
1680         qbool rotated;
1681         float savesolid, movetime2, pushltime;
1682         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1683         int num_moved;
1684         int numcheckentities;
1685         static prvm_edict_t *checkentities[MAX_EDICTS];
1686         model_t *pushermodel;
1687         trace_t trace, trace2;
1688         matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1689         static unsigned short moved_edicts[MAX_EDICTS];
1690         vec3_t pivot;
1691
1692         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])
1693         {
1694                 PRVM_serveredictfloat(pusher, ltime) += movetime;
1695                 return;
1696         }
1697
1698         switch ((int) PRVM_serveredictfloat(pusher, solid))
1699         {
1700         // LadyHavoc: valid pusher types
1701         case SOLID_BSP:
1702         case SOLID_BBOX:
1703         case SOLID_SLIDEBOX:
1704         case SOLID_CORPSE: // LadyHavoc: this would be weird...
1705                 break;
1706         // LadyHavoc: no collisions
1707         case SOLID_NOT:
1708         case SOLID_TRIGGER:
1709                 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1710                 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1711                 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1712                 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1713                 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1714                 PRVM_serveredictfloat(pusher, ltime) += movetime;
1715                 SV_LinkEdict(pusher);
1716                 return;
1717         default:
1718                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1719                 return;
1720         }
1721         index = (int) PRVM_serveredictfloat(pusher, modelindex);
1722         if (index < 1 || index >= MAX_MODELS)
1723         {
1724                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1725                 return;
1726         }
1727         pushermodel = SV_GetModelByIndex(index);
1728         pusherowner = PRVM_serveredictedict(pusher, owner);
1729         pusherprog = PRVM_EDICT_TO_PROG(pusher);
1730
1731         rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1732
1733         movetime2 = movetime;
1734         VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1735         VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1736         if (moveangle[0] || moveangle[2])
1737         {
1738                 for (i = 0;i < 3;i++)
1739                 {
1740                         if (move1[i] > 0)
1741                         {
1742                                 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1743                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1744                         }
1745                         else
1746                         {
1747                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1748                                 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1749                         }
1750                 }
1751         }
1752         else if (moveangle[1])
1753         {
1754                 for (i = 0;i < 3;i++)
1755                 {
1756                         if (move1[i] > 0)
1757                         {
1758                                 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1759                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1760                         }
1761                         else
1762                         {
1763                                 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1764                                 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1765                         }
1766                 }
1767         }
1768         else
1769         {
1770                 for (i = 0;i < 3;i++)
1771                 {
1772                         if (move1[i] > 0)
1773                         {
1774                                 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1775                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1776                         }
1777                         else
1778                         {
1779                                 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1780                                 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1781                         }
1782                 }
1783         }
1784
1785         VectorNegate (moveangle, a);
1786         AngleVectorsFLU (a, forward, left, up);
1787
1788         VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1789         VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1790         pushltime = PRVM_serveredictfloat(pusher, ltime);
1791
1792 // move the pusher to its final position
1793
1794         VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1795         VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1796         PRVM_serveredictfloat(pusher, ltime) += movetime;
1797         SV_LinkEdict(pusher);
1798
1799         pushermodel = SV_GetModelFromEdict(pusher);
1800         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);
1801         Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1802
1803         savesolid = PRVM_serveredictfloat(pusher, solid);
1804
1805 // see if any solid entities are inside the final position
1806         num_moved = 0;
1807
1808         if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1809                 numcheckentities = 0;
1810         else // MOVETYPE_PUSH
1811                 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1812         for (e = 0;e < numcheckentities;e++)
1813         {
1814                 prvm_edict_t *check = checkentities[e];
1815                 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1816                 switch(movetype)
1817                 {
1818                 case MOVETYPE_NONE:
1819                 case MOVETYPE_PUSH:
1820                 case MOVETYPE_FOLLOW:
1821                 case MOVETYPE_NOCLIP:
1822                 case MOVETYPE_FLY_WORLDONLY:
1823                         continue;
1824                 default:
1825                         break;
1826                 }
1827
1828                 if (PRVM_serveredictedict(check, owner) == pusherprog)
1829                         continue;
1830
1831                 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1832                         continue;
1833
1834                 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1835
1836                 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1837                 check->priv.server->waterposition_forceupdate = true;
1838
1839                 checkcontents = SV_GenericHitSuperContentsMask(check);
1840
1841                 // if the entity is standing on the pusher, it will definitely be moved
1842                 // if the entity is not standing on the pusher, but is in the pusher's
1843                 // final position, move it
1844                 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1845                 {
1846                         VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1847                         VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1848                         VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1849                         VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1850                         VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1851                         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);
1852                         //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1853                         if (!trace.startsolid)
1854                         {
1855                                 //Con_Printf("- not in solid\n");
1856                                 continue;
1857                         }
1858                 }
1859
1860                 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1861                 //VectorClear(pivot);
1862
1863                 if (rotated)
1864                 {
1865                         vec3_t org2;
1866                         VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1867                         VectorAdd (org, pivot, org);
1868                         org2[0] = DotProduct (org, forward);
1869                         org2[1] = DotProduct (org, left);
1870                         org2[2] = DotProduct (org, up);
1871                         VectorSubtract (org2, org, move);
1872                         VectorAdd (move, move1, move);
1873                 }
1874                 else
1875                         VectorCopy (move1, move);
1876
1877                 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1878
1879                 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1880                 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1881                 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1882
1883                 // physics objects need better collisions than this code can do
1884                 if (movetype == MOVETYPE_PHYSICS)
1885                 {
1886                         VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1887                         SV_LinkEdict(check);
1888                         SV_LinkEdict_TouchAreaGrid(check);
1889                         continue;
1890                 }
1891
1892                 // try moving the contacted entity
1893                 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1894                 if(!SV_PushEntity (&trace, check, move, true))
1895                 {
1896                         // entity "check" got teleported
1897                         PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1898                         PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1899                         continue; // pushed enough
1900                 }
1901                 // FIXME: turn players specially
1902                 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1903                 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1904                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1905
1906                 // this trace.fraction < 1 check causes items to fall off of pushers
1907                 // if they pass under or through a wall
1908                 // the groundentity check causes items to fall off of ledges
1909                 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1910                         PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1911
1912                 // if it is still inside the pusher, block
1913                 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1914                 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1915                 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1916                 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1917                 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1918                 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);
1919                 if (trace.startsolid)
1920                 {
1921                         vec3_t move2;
1922                         if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1923                         {
1924                                 // hack to invoke all necessary movement triggers
1925                                 VectorClear(move2);
1926                                 if(!SV_PushEntity(&trace2, check, move2, true))
1927                                 {
1928                                         // entity "check" got teleported
1929                                         continue;
1930                                 }
1931                                 // we could fix it
1932                                 continue;
1933                         }
1934
1935                         // still inside pusher, so it's really blocked
1936
1937                         // fail the move
1938                         if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1939                                 continue;
1940                         if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1941                         {
1942                                 // corpse
1943                                 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1944                                 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1945                                 continue;
1946                         }
1947
1948                         VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1949                         VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1950                         PRVM_serveredictfloat(pusher, ltime) = pushltime;
1951                         SV_LinkEdict(pusher);
1952
1953                         // move back any entities we already moved
1954                         for (i = 0;i < num_moved;i++)
1955                         {
1956                                 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1957                                 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1958                                 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1959                                 SV_LinkEdict(ed);
1960                         }
1961
1962                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1963                         if (PRVM_serveredictfunction(pusher, blocked))
1964                         {
1965                                 PRVM_serverglobalfloat(time) = sv.time;
1966                                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1967                                 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1968                                 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1969                         }
1970                         break;
1971                 }
1972         }
1973         PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1974         PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1975         PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1976 }
1977
1978 /*
1979 ================
1980 SV_Physics_Pusher
1981
1982 ================
1983 */
1984 static void SV_Physics_Pusher (prvm_edict_t *ent)
1985 {
1986         prvm_prog_t *prog = SVVM_prog;
1987         prvm_vec_t thinktime, oldltime, movetime;
1988
1989         oldltime = PRVM_serveredictfloat(ent, ltime);
1990
1991         thinktime = PRVM_serveredictfloat(ent, nextthink);
1992         if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1993         {
1994                 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1995                 if (movetime < 0)
1996                         movetime = 0;
1997         }
1998         else
1999                 movetime = sv.frametime;
2000
2001         if (movetime)
2002                 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
2003                 SV_PushMove (ent, movetime);
2004
2005         if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
2006         {
2007                 PRVM_serveredictfloat(ent, nextthink) = 0;
2008                 PRVM_serverglobalfloat(time) = sv.time;
2009                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2010                 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
2011                 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2012         }
2013 }
2014
2015
2016 /*
2017 ===============================================================================
2018
2019 CLIENT MOVEMENT
2020
2021 ===============================================================================
2022 */
2023
2024 static float unstickoffsets[] =
2025 {
2026         // poutting -/+z changes first as they are least weird
2027          0,  0,  -1,
2028          0,  0,  1,
2029          // x or y changes
2030         -1,  0,  0,
2031          1,  0,  0,
2032          0, -1,  0,
2033          0,  1,  0,
2034          // x and y changes
2035         -1, -1,  0,
2036          1, -1,  0,
2037         -1,  1,  0,
2038          1,  1,  0,
2039 };
2040
2041 typedef enum unstickresult_e
2042 {
2043         UNSTICK_STUCK = 0,
2044         UNSTICK_GOOD = 1,
2045         UNSTICK_UNSTUCK = 2
2046 }
2047 unstickresult_t;
2048
2049 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2050 {
2051         prvm_prog_t *prog = SVVM_prog;
2052         int i, maxunstick;
2053
2054         // if not stuck in a bmodel, just return
2055         if (!SV_TestEntityPosition(ent, vec3_origin))
2056                 return UNSTICK_GOOD;
2057
2058         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2059         {
2060                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2061                 {
2062                         VectorCopy(unstickoffsets + i, offset);
2063                         SV_LinkEdict(ent);
2064                         //SV_LinkEdict_TouchAreaGrid(ent);
2065                         return UNSTICK_UNSTUCK;
2066                 }
2067         }
2068
2069         maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2070         // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2071
2072         for(i = 2; i <= maxunstick; ++i)
2073         {
2074                 VectorClear(offset);
2075                 offset[2] = -i;
2076                 if (!SV_TestEntityPosition(ent, offset))
2077                 {
2078                         SV_LinkEdict(ent);
2079                         //SV_LinkEdict_TouchAreaGrid(ent);
2080                         return UNSTICK_UNSTUCK;
2081                 }
2082                 offset[2] = i;
2083                 if (!SV_TestEntityPosition(ent, offset))
2084                 {
2085                         SV_LinkEdict(ent);
2086                         //SV_LinkEdict_TouchAreaGrid(ent);
2087                         return UNSTICK_UNSTUCK;
2088                 }
2089         }
2090
2091         return UNSTICK_STUCK;
2092 }
2093
2094 qbool SV_UnstickEntity (prvm_edict_t *ent)
2095 {
2096         prvm_prog_t *prog = SVVM_prog;
2097         vec3_t offset;
2098         switch(SV_UnstickEntityReturnOffset(ent, offset))
2099         {
2100                 case UNSTICK_GOOD:
2101                         return true;
2102                 case UNSTICK_UNSTUCK:
2103                         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]);
2104                         return true;
2105                 case UNSTICK_STUCK:
2106                         if (developer_extra.integer)
2107                                 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2108                         return false;
2109                 default:
2110                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2111                         return false;
2112         }
2113 }
2114
2115 /*
2116 =============
2117 SV_CheckStuck
2118
2119 This is a big hack to try and fix the rare case of getting stuck in the world
2120 clipping hull.
2121 =============
2122 */
2123 static void SV_CheckStuck (prvm_edict_t *ent)
2124 {
2125         prvm_prog_t *prog = SVVM_prog;
2126         vec3_t offset;
2127
2128         switch(SV_UnstickEntityReturnOffset(ent, offset))
2129         {
2130                 case UNSTICK_GOOD:
2131                         VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2132                         break;
2133                 case UNSTICK_UNSTUCK:
2134                         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]);
2135                         break;
2136                 case UNSTICK_STUCK:
2137                         VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2138                         if (!SV_TestEntityPosition(ent, offset))
2139                         {
2140                                 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)));
2141                                 SV_LinkEdict(ent);
2142                                 //SV_LinkEdict_TouchAreaGrid(ent);
2143                         }
2144                         else
2145                                 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2146                         break;
2147                 default:
2148                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2149         }
2150 }
2151
2152
2153 /*
2154 =============
2155 SV_CheckWater
2156 =============
2157 */
2158 static qbool SV_CheckWater (prvm_edict_t *ent)
2159 {
2160         prvm_prog_t *prog = SVVM_prog;
2161         int cont;
2162         int nNativeContents;
2163         vec3_t point;
2164
2165         point[0] = PRVM_serveredictvector(ent, origin)[0];
2166         point[1] = PRVM_serveredictvector(ent, origin)[1];
2167         point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2168
2169         // DRESK - Support for Entity Contents Transition Event
2170         // NOTE: Some logic needed to be slightly re-ordered
2171         // to not affect performance and allow for the feature.
2172
2173         // Acquire Super Contents Prior to Resets
2174         cont = SV_PointSuperContents(point);
2175         // Acquire Native Contents Here
2176         nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(cont);
2177
2178         // DRESK - Support for Entity Contents Transition Event
2179         if(PRVM_serveredictfloat(ent, watertype))
2180                 // Entity did NOT Spawn; Check
2181                 SV_CheckContentsTransition(ent, nNativeContents);
2182
2183
2184         PRVM_serveredictfloat(ent, waterlevel) = 0;
2185         PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2186         cont = SV_PointSuperContents(point);
2187         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2188         {
2189                 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2190                 PRVM_serveredictfloat(ent, waterlevel) = 1;
2191                 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2192                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2193                 {
2194                         PRVM_serveredictfloat(ent, waterlevel) = 2;
2195                         point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2196                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2197                                 PRVM_serveredictfloat(ent, waterlevel) = 3;
2198                 }
2199         }
2200
2201         return PRVM_serveredictfloat(ent, waterlevel) > 1;
2202 }
2203
2204 /*
2205 ============
2206 SV_WallFriction
2207
2208 ============
2209 */
2210 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2211 {
2212         prvm_prog_t *prog = SVVM_prog;
2213         float d, i;
2214         vec3_t forward, into, side, v_angle;
2215
2216         VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2217         AngleVectors (v_angle, forward, NULL, NULL);
2218         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2219         {
2220                 // cut the tangential velocity
2221                 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2222                 VectorScale (stepnormal, i, into);
2223                 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2224                 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2225                 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2226         }
2227 }
2228
2229 #if 0
2230 /*
2231 =====================
2232 SV_TryUnstick
2233
2234 Player has come to a dead stop, possibly due to the problem with limited
2235 float precision at some angle joins in the BSP hull.
2236
2237 Try fixing by pushing one pixel in each direction.
2238
2239 This is a hack, but in the interest of good gameplay...
2240 ======================
2241 */
2242 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2243 {
2244         int i, clip;
2245         vec3_t oldorg, dir;
2246
2247         VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2248         VectorClear (dir);
2249
2250         for (i=0 ; i<8 ; i++)
2251         {
2252                 // try pushing a little in an axial direction
2253                 switch (i)
2254                 {
2255                         case 0: dir[0] = 2; dir[1] = 0; break;
2256                         case 1: dir[0] = 0; dir[1] = 2; break;
2257                         case 2: dir[0] = -2; dir[1] = 0; break;
2258                         case 3: dir[0] = 0; dir[1] = -2; break;
2259                         case 4: dir[0] = 2; dir[1] = 2; break;
2260                         case 5: dir[0] = -2; dir[1] = 2; break;
2261                         case 6: dir[0] = 2; dir[1] = -2; break;
2262                         case 7: dir[0] = -2; dir[1] = -2; break;
2263                 }
2264
2265                 SV_PushEntity (&trace, ent, dir, false, true);
2266
2267                 // retry the original move
2268                 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2269                 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2270                 PRVM_serveredictvector(ent, velocity)[2] = 0;
2271                 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2272
2273                 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2274                  || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2275                 {
2276                         Con_DPrint("TryUnstick - success.\n");
2277                         return clip;
2278                 }
2279
2280                 // go back to the original pos and try again
2281                 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2282         }
2283
2284         // still not moving
2285         VectorClear (PRVM_serveredictvector(ent, velocity));
2286         Con_DPrint("TryUnstick - failure.\n");
2287         return 7;
2288 }
2289 #endif
2290
2291 /*
2292 =====================
2293 SV_WalkMove
2294
2295 Only used by players
2296 ======================
2297 */
2298 static void SV_WalkMove (prvm_edict_t *ent)
2299 {
2300         prvm_prog_t *prog = SVVM_prog;
2301         int clip;
2302         int oldonground;
2303         //int originalmove_clip;
2304         int originalmove_flags;
2305         int originalmove_groundentity;
2306         int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2307         int skipsupercontentsmask = 0;
2308         int skipmaterialflagsmask = 0;
2309         int type;
2310         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2311         trace_t downtrace, trace;
2312         qbool applygravity;
2313
2314         // if frametime is 0 (due to client sending the same timestamp twice),
2315         // don't move
2316         if (sv.frametime <= 0)
2317                 return;
2318
2319         if (sv_gameplayfix_unstickplayers.integer)
2320                 SV_CheckStuck (ent);
2321
2322         applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2323
2324         SV_CheckVelocity(ent);
2325
2326         // do a regular slide move unless it looks like you ran into a step
2327         oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2328
2329         VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2330         VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2331
2332         clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2333
2334         if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2335         if(!(clip & 1))
2336         {
2337                 // only try this if there was no floor in the way in the trace (no,
2338                 // this check seems to be not REALLY necessary, because if clip & 1,
2339                 // our trace will hit that thing too)
2340                 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2341                 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2342                 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2343                         type = MOVE_MISSILE;
2344                 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2345                         type = MOVE_WORLDONLY;
2346                 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2347                         type = MOVE_NOMONSTERS; // only clip against bmodels
2348                 else
2349                         type = MOVE_NORMAL;
2350                 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2351                 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2352                 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
2353                 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2354                 {
2355                         clip |= 1; // but we HAVE found a floor
2356                         // set groundentity so we get carried when walking onto a mover with sv_gameplayfix_nogravityonground
2357                         PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2358                 }
2359         }
2360
2361         // if the move did not hit the ground at any point, we're not on ground
2362         if(!(clip & 1))
2363                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2364
2365         SV_CheckVelocity(ent);
2366         SV_LinkEdict(ent);
2367         SV_LinkEdict_TouchAreaGrid(ent);
2368
2369         if(clip & 8) // teleport
2370                 return;
2371
2372         if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2373                 return;
2374
2375         if (sv_nostep.integer)
2376                 return;
2377
2378         VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2379         VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2380         //originalmove_clip = clip;
2381         originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2382         originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2383
2384         // if move didn't block on a step, return
2385         if (clip & 2)
2386         {
2387                 // if move was not trying to move into the step, return
2388                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2389                         return;
2390
2391                 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2392                 {
2393                         // return if gibbed by a trigger
2394                         if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2395                                 return;
2396
2397                         // return if attempting to jump while airborn (unless sv_jumpstep)
2398                         if (!sv_jumpstep.integer)
2399                                 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2400                                         return;
2401                 }
2402
2403                 // try moving up and forward to go up a step
2404                 // back to start pos
2405                 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2406                 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2407
2408                 // move up
2409                 VectorClear (upmove);
2410                 upmove[2] = sv_stepheight.value;
2411                 if(!SV_PushEntity(&trace, ent, upmove, true))
2412                 {
2413                         // we got teleported when upstepping... must abort the move
2414                         return;
2415                 }
2416
2417                 // move forward
2418                 PRVM_serveredictvector(ent, velocity)[2] = 0;
2419                 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 0);
2420                 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2421                 if(clip & 8)
2422                 {
2423                         // we got teleported when upstepping... must abort the move
2424                         // note that z velocity handling may not be what QC expects here, but we cannot help it
2425                         return;
2426                 }
2427
2428                 SV_CheckVelocity(ent);
2429                 SV_LinkEdict(ent);
2430                 SV_LinkEdict_TouchAreaGrid(ent);
2431
2432                 // check for stuckness, possibly due to the limited precision of floats
2433                 // in the clipping hulls
2434                 if (clip
2435                  && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2436                  && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2437                 {
2438                         //Con_Printf("wall\n");
2439                         // stepping up didn't make any progress, revert to original move
2440                         VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2441                         VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2442                         //clip = originalmove_clip;
2443                         PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2444                         PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2445                         // now try to unstick if needed
2446                         //clip = SV_TryUnstick (ent, oldvel);
2447                         return;
2448                 }
2449
2450                 //Con_Printf("step - ");
2451
2452                 // extra friction based on view angle
2453                 if (clip & 2 && sv_wallfriction.integer)
2454                         SV_WallFriction (ent, stepnormal);
2455         }
2456         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2457         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))
2458                 return;
2459
2460         // move down
2461         VectorClear (downmove);
2462         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2463         if(!SV_PushEntity (&downtrace, ent, downmove, true))
2464         {
2465                 // we got teleported when downstepping... must abort the move
2466                 return;
2467         }
2468
2469         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2470         {
2471                 // this has been disabled so that you can't jump when you are stepping
2472                 // up while already jumping (also known as the Quake2 double jump bug)
2473 #if 0
2474                 // LadyHavoc: disabled this check so you can walk on monsters/players
2475                 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2476                 {
2477                         //Con_Printf("onground\n");
2478                         PRVM_serveredictfloat(ent, flags) =     (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2479                         PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2480                 }
2481 #endif
2482         }
2483         else
2484         {
2485                 //Con_Printf("slope\n");
2486                 // if the push down didn't end up on good ground, use the move without
2487                 // the step up.  This happens near wall / slope combinations, and can
2488                 // cause the player to hop up higher on a slope too steep to climb
2489                 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2490                 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2491                 //clip = originalmove_clip;
2492                 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2493                 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2494         }
2495
2496         SV_CheckVelocity(ent);
2497         SV_LinkEdict(ent);
2498         SV_LinkEdict_TouchAreaGrid(ent);
2499 }
2500
2501 //============================================================================
2502
2503 /*
2504 =============
2505 SV_Physics_Follow
2506
2507 Entities that are "stuck" to another entity
2508 =============
2509 */
2510 static void SV_Physics_Follow (prvm_edict_t *ent)
2511 {
2512         prvm_prog_t *prog = SVVM_prog;
2513         vec3_t vf, vr, vu, angles, v;
2514         prvm_edict_t *e;
2515
2516         // LadyHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2517         e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2518         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])
2519         {
2520                 // quick case for no rotation
2521                 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2522         }
2523         else
2524         {
2525                 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2526                 angles[1] =  PRVM_serveredictvector(ent, punchangle)[1];
2527                 angles[2] =  PRVM_serveredictvector(ent, punchangle)[2];
2528                 AngleVectors (angles, vf, vr, vu);
2529                 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];
2530                 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];
2531                 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];
2532                 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2533                 angles[1] =  PRVM_serveredictvector(e, angles)[1];
2534                 angles[2] =  PRVM_serveredictvector(e, angles)[2];
2535                 AngleVectors (angles, vf, vr, vu);
2536                 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2537                 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2538                 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2539         }
2540         VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2541         SV_LinkEdict(ent);
2542         //SV_LinkEdict_TouchAreaGrid(ent);
2543 }
2544
2545 /*
2546 ==============================================================================
2547
2548 TOSS / BOUNCE
2549
2550 ==============================================================================
2551 */
2552
2553 /*
2554 =============
2555 SV_CheckWaterTransition
2556
2557 =============
2558 */
2559 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2560 {
2561         vec3_t entorigin;
2562         prvm_prog_t *prog = SVVM_prog;
2563         // 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
2564         int cont;
2565         VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2566         cont = Mod_Q1BSP_NativeContentsFromSuperContents(SV_PointSuperContents(entorigin));
2567         if (!PRVM_serveredictfloat(ent, watertype))
2568         {
2569                 // just spawned here
2570                 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2571                 {
2572                         PRVM_serveredictfloat(ent, watertype) = cont;
2573                         PRVM_serveredictfloat(ent, waterlevel) = 1;
2574                         return;
2575                 }
2576         }
2577         // DRESK - Support for Entity Contents Transition Event
2578         // NOTE: Call here BEFORE updating the watertype below,
2579         // and suppress watersplash sound if a valid function
2580         // call was made to allow for custom "splash" sounds.
2581         else if( !SV_CheckContentsTransition(ent, cont) )
2582         { // Contents Transition Function Invalid; Potentially Play Water Sound
2583                 // check if the entity crossed into or out of water
2584                 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2585                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2586         }
2587
2588         if (cont <= CONTENTS_WATER)
2589         {
2590                 PRVM_serveredictfloat(ent, watertype) = cont;
2591                 PRVM_serveredictfloat(ent, waterlevel) = 1;
2592         }
2593         else
2594         {
2595                 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2596                 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2597         }
2598 }
2599
2600 /*
2601 =============
2602 SV_Physics_Toss
2603
2604 Toss, bounce, and fly movement.  When onground, do nothing.
2605 =============
2606 */
2607
2608 void SV_Physics_Toss (prvm_edict_t *ent)
2609 {
2610         prvm_prog_t *prog = SVVM_prog;
2611         trace_t trace;
2612         vec3_t move;
2613         vec_t movetime;
2614         int bump;
2615         prvm_edict_t *groundentity;
2616         float d, ent_gravity;
2617         float bouncefactor;
2618         float bouncestop;
2619
2620 // if onground, return without moving
2621         if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2622         {
2623                 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2624                 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2625                 {
2626                         // don't stick to ground if onground and moving upward
2627                         PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2628                 }
2629                 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2630                 {
2631                         // we can trust FL_ONGROUND if groundentity is world because it never moves
2632                         return;
2633                 }
2634                 else if (ent->priv.server->suspendedinairflag && groundentity->free)
2635                 {
2636                         // if ent was supported by a brush model on previous frame,
2637                         // and groundentity is now freed, set groundentity to 0 (world)
2638                         // which leaves it suspended in the air
2639                         PRVM_serveredictedict(ent, groundentity) = 0;
2640                         if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2641                                 return;
2642                 }
2643                 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2644                 {
2645                         // don't slide if still touching the groundentity
2646                         return;
2647                 }
2648         }
2649         ent->priv.server->suspendedinairflag = false;
2650
2651         SV_CheckVelocity (ent);
2652
2653 // add gravity
2654         if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2655                 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2656
2657 // move angles
2658         VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2659
2660         movetime = sv.frametime;
2661         for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2662         {
2663         // move origin
2664                 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2665                 if(!SV_PushEntity(&trace, ent, move, true))
2666                         return; // teleported
2667                 if (ent->free)
2668                         return;
2669                 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2670                 {
2671                         // try to unstick the entity
2672                         SV_UnstickEntity(ent);
2673                         if(!SV_PushEntity(&trace, ent, move, true))
2674                                 return; // teleported
2675                         if (ent->free)
2676                                 return;
2677                 }
2678                 if (trace.fraction == 1)
2679                         break;
2680                 movetime *= 1 - min(1, trace.fraction);
2681                 switch((int)PRVM_serveredictfloat(ent, movetype))
2682                 {
2683                 case MOVETYPE_BOUNCEMISSILE:
2684                         bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2685                         if (!bouncefactor)
2686                                 bouncefactor = 1.0f;
2687
2688                         ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2689                         PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2690                         if (!sv_gameplayfix_slidemoveprojectiles.integer)
2691                                 movetime = 0;
2692                         break;
2693                 case MOVETYPE_BOUNCE:
2694                         bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2695                         if (!bouncefactor)
2696                                 bouncefactor = 0.5f;
2697
2698                         bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2699                         if (!bouncestop)
2700                                 bouncestop = 60.0f / 800.0f;
2701
2702                         ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2703                         ent_gravity = PRVM_serveredictfloat(ent, gravity);
2704                         if (!ent_gravity)
2705                                 ent_gravity = 1.0f;
2706                         // LadyHavoc: fixed grenades not bouncing when fired down a slope
2707                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
2708                                 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2709                         else
2710                                 d = PRVM_serveredictvector(ent, velocity)[2];
2711                         if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2712                         {
2713                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2714                                 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2715                                 VectorClear(PRVM_serveredictvector(ent, velocity));
2716                                 VectorClear(PRVM_serveredictvector(ent, avelocity));
2717                                 movetime = 0;
2718                         }
2719                         else
2720                         {
2721                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2722                                 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2723                                         movetime = 0;
2724                         }
2725                         break;
2726                 default:
2727                         ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2728                         if (trace.plane.normal[2] > 0.7)
2729                         {
2730                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2731                                 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2732                                 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2733                                         ent->priv.server->suspendedinairflag = true;
2734                                 VectorClear (PRVM_serveredictvector(ent, velocity));
2735                                 VectorClear (PRVM_serveredictvector(ent, avelocity));
2736                                 movetime = 0;
2737                         }
2738                         else
2739                         {
2740                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2741                                 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2742                                         movetime = 0;
2743                         }
2744                         break;
2745                 }
2746         }
2747
2748 // check for in water
2749         SV_CheckWaterTransition (ent);
2750 }
2751
2752 /*
2753 ===============================================================================
2754
2755 STEPPING MOVEMENT
2756
2757 ===============================================================================
2758 */
2759
2760 /*
2761 =============
2762 SV_Physics_Step
2763
2764 Monsters freefall when they don't have a ground entity, otherwise
2765 all movement is done with discrete steps.
2766
2767 This is also used for objects that have become still on the ground, but
2768 will fall if the floor is pulled out from under them.
2769 =============
2770 */
2771 static void SV_Physics_Step (prvm_edict_t *ent)
2772 {
2773         prvm_prog_t *prog = SVVM_prog;
2774         int flags = (int)PRVM_serveredictfloat(ent, flags);
2775
2776         // DRESK
2777         // Backup Velocity in the event that movetypesteplandevent is called,
2778         // to provide a parameter with the entity's velocity at impact.
2779         vec3_t backupVelocity;
2780         VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2781         // don't fall at all if fly/swim
2782         if (!(flags & (FL_FLY | FL_SWIM)))
2783         {
2784                 if (flags & FL_ONGROUND)
2785                 {
2786                         // freefall if onground and moving upward
2787                         // freefall if not standing on a world surface (it may be a lift or trap door)
2788                         if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2789                         {
2790                                 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2791                                 SV_CheckVelocity(ent);
2792                                 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2793                                 SV_LinkEdict(ent);
2794                                 SV_LinkEdict_TouchAreaGrid(ent);
2795                                 ent->priv.server->waterposition_forceupdate = true;
2796                         }
2797                 }
2798                 else
2799                 {
2800                         // freefall if not onground
2801                         int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2802
2803                         SV_CheckVelocity(ent);
2804                         SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2805                         SV_LinkEdict(ent);
2806                         SV_LinkEdict_TouchAreaGrid(ent);
2807
2808                         // just hit ground
2809                         if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2810                         {
2811                                 // DRESK - Check for Entity Land Event Function
2812                                 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2813                                 { // Valid Function; Execute
2814                                         // Prepare Parameters
2815                                         // Assign Velocity at Impact
2816                                         PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2817                                         PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2818                                         PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2819                                         // Assign Self
2820                                         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2821                                         // Set Time
2822                                         PRVM_serverglobalfloat(time) = sv.time;
2823                                         // Execute VM Function
2824                                         prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2825                                 }
2826                                 else
2827                                 // Check for Engine Landing Sound
2828                                 if(sv_sound_land.string)
2829                                         SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2830                         }
2831                         ent->priv.server->waterposition_forceupdate = true;
2832                 }
2833         }
2834 }
2835
2836 //============================================================================
2837
2838 static void SV_Physics_Entity (prvm_edict_t *ent)
2839 {
2840         prvm_prog_t *prog = SVVM_prog;
2841         // don't run think/move on newly spawned projectiles as it messes up
2842         // movement interpolation and rocket trails, and is inconsistent with
2843         // respect to entities spawned in the same frame
2844         // (if an ent spawns a higher numbered ent, it moves in the same frame,
2845         //  but if it spawns a lower numbered ent, it doesn't - this never moves
2846         //  ents in the first frame regardless)
2847         qbool runmove = ent->priv.server->move;
2848         ent->priv.server->move = true;
2849         if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2850                 return;
2851         switch ((int) PRVM_serveredictfloat(ent, movetype))
2852         {
2853         case MOVETYPE_PUSH:
2854         case MOVETYPE_FAKEPUSH:
2855                 SV_Physics_Pusher (ent);
2856                 break;
2857         case MOVETYPE_NONE:
2858                 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2859                 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2860                         SV_RunThink (ent);
2861                 break;
2862         case MOVETYPE_FOLLOW:
2863                 if(SV_RunThink(ent))
2864                         SV_Physics_Follow (ent);
2865                 break;
2866         case MOVETYPE_NOCLIP:
2867                 if (SV_RunThink(ent))
2868                 {
2869                         SV_CheckWater(ent);
2870                         VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2871                         VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2872                 }
2873                 SV_LinkEdict(ent);
2874                 break;
2875         case MOVETYPE_STEP:
2876                 SV_Physics_Step (ent);
2877                 // regular thinking
2878                 if (SV_RunThink(ent))
2879                 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2880                 {
2881                         ent->priv.server->waterposition_forceupdate = false;
2882                         VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2883                         SV_CheckWaterTransition(ent);
2884                 }
2885                 break;
2886         case MOVETYPE_WALK:
2887                 if (SV_RunThink (ent))
2888                         SV_WalkMove (ent);
2889                 break;
2890         case MOVETYPE_TOSS:
2891         case MOVETYPE_BOUNCE:
2892         case MOVETYPE_BOUNCEMISSILE:
2893         case MOVETYPE_FLYMISSILE:
2894         case MOVETYPE_FLY:
2895         case MOVETYPE_FLY_WORLDONLY:
2896                 // regular thinking
2897                 if (SV_RunThink (ent))
2898                         SV_Physics_Toss (ent);
2899                 break;
2900         case MOVETYPE_PHYSICS:
2901                 if (SV_RunThink(ent))
2902                 {
2903                         SV_LinkEdict(ent);
2904                         SV_LinkEdict_TouchAreaGrid(ent);
2905                 }
2906                 break;
2907         default:
2908                 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2909                         break;
2910                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2911                 break;
2912         }
2913 }
2914
2915 static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
2916 {
2917         prvm_prog_t *prog = SVVM_prog;
2918
2919         // don't run think at all, that is done during server frames
2920         // instead, call the movetypes directly so they match client input
2921
2922         // This probably only makes sense for CSQC-networked (SendEntity field set) player entities
2923         switch ((int) PRVM_serveredictfloat(ent, movetype))
2924         {
2925         case MOVETYPE_PUSH:
2926         case MOVETYPE_FAKEPUSH:
2927                 // push physics relies heavily on think times and calls, and so cannot be predicted currently
2928                 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2929                 break;
2930         case MOVETYPE_NONE:
2931                 break;
2932         case MOVETYPE_FOLLOW:
2933                 SV_Physics_Follow (ent);
2934                 break;
2935         case MOVETYPE_NOCLIP:
2936                 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2937                 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2938                 break;
2939         case MOVETYPE_STEP:
2940                 SV_Physics_Step (ent);
2941                 break;
2942         case MOVETYPE_WALK:
2943                 SV_WalkMove (ent);
2944                 break;
2945         case MOVETYPE_TOSS:
2946         case MOVETYPE_BOUNCE:
2947         case MOVETYPE_BOUNCEMISSILE:
2948         case MOVETYPE_FLYMISSILE:
2949                 SV_Physics_Toss (ent);
2950                 break;
2951         case MOVETYPE_FLY:
2952         case MOVETYPE_FLY_WORLDONLY:
2953                 SV_WalkMove (ent);
2954                 break;
2955         case MOVETYPE_PHYSICS:
2956                 break;
2957         default:
2958                 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2959                         break;
2960                 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2961                 break;
2962         }
2963 }
2964
2965 void SV_Physics_ClientMove(void)
2966 {
2967         prvm_prog_t *prog = SVVM_prog;
2968         prvm_edict_t *ent;
2969         ent = host_client->edict;
2970
2971         // call player physics, this needs the proper frametime
2972         PRVM_serverglobalfloat(frametime) = sv.frametime;
2973         SV_PlayerPhysics();
2974
2975         // call standard client pre-think, with frametime = 0
2976         PRVM_serverglobalfloat(time) = sv.time;
2977         PRVM_serverglobalfloat(frametime) = 0;
2978         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2979         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2980         PRVM_serverglobalfloat(frametime) = sv.frametime;
2981
2982         // make sure the velocity is sane (not a NaN)
2983         SV_CheckVelocity(ent);
2984
2985         // perform movetype behaviour
2986         // note: will always be MOVETYPE_WALK if disableclientprediction = 0
2987         SV_Physics_ClientEntity_NoThink (ent);
2988
2989         // call standard player post-think, with frametime = 0
2990         PRVM_serverglobalfloat(time) = sv.time;
2991         PRVM_serverglobalfloat(frametime) = 0;
2992         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2993         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2994         PRVM_serverglobalfloat(frametime) = sv.frametime;
2995
2996         if(PRVM_serveredictfloat(ent, fixangle))
2997         {
2998                 // angle fixing was requested by physics code...
2999                 // so store the current angles for later use
3000                 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3001                 host_client->fixangle_angles_set = true;
3002
3003                 // and clear fixangle for the next frame
3004                 PRVM_serveredictfloat(ent, fixangle) = 0;
3005         }
3006 }
3007
3008 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
3009 {
3010         prvm_prog_t *prog = SVVM_prog;
3011         // don't do physics on disconnected clients, FrikBot relies on this
3012         if (!host_client->begun)
3013                 return;
3014
3015         // make sure the velocity is sane (not a NaN)
3016         SV_CheckVelocity(ent);
3017
3018         // don't run physics here if running asynchronously
3019         if (host_client->clmovement_inputtimeout <= 0)
3020         {
3021                 SV_PlayerPhysics();
3022                 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
3023         }
3024
3025         // make sure the velocity is still sane (not a NaN)
3026         SV_CheckVelocity(ent);
3027
3028         // call standard client pre-think
3029         PRVM_serverglobalfloat(time) = sv.time;
3030         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3031         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3032
3033         // make sure the velocity is still sane (not a NaN)
3034         SV_CheckVelocity(ent);
3035 }
3036
3037 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3038 {
3039         prvm_prog_t *prog = SVVM_prog;
3040         // don't do physics on disconnected clients, FrikBot relies on this
3041         if (!host_client->begun)
3042                 return;
3043
3044         // make sure the velocity is sane (not a NaN)
3045         SV_CheckVelocity(ent);
3046
3047         // call standard player post-think
3048         PRVM_serverglobalfloat(time) = sv.time;
3049         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3050         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3051
3052         // make sure the velocity is still sane (not a NaN)
3053         SV_CheckVelocity(ent);
3054
3055         if(PRVM_serveredictfloat(ent, fixangle))
3056         {
3057                 // angle fixing was requested by physics code...
3058                 // so store the current angles for later use
3059                 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3060                 host_client->fixangle_angles_set = true;
3061
3062                 // and clear fixangle for the next frame
3063                 PRVM_serveredictfloat(ent, fixangle) = 0;
3064         }
3065
3066         // decrement the countdown variable used to decide when to go back to
3067         // synchronous physics
3068         if (host_client->clmovement_inputtimeout > sv.frametime)
3069                 host_client->clmovement_inputtimeout -= sv.frametime;
3070         else
3071                 host_client->clmovement_inputtimeout = 0;
3072 }
3073
3074 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3075 {
3076         prvm_prog_t *prog = SVVM_prog;
3077         // don't do physics on disconnected clients, FrikBot relies on this
3078         if (!host_client->begun)
3079         {
3080                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3081                 return;
3082         }
3083
3084         // make sure the velocity is sane (not a NaN)
3085         SV_CheckVelocity(ent);
3086
3087         switch ((int) PRVM_serveredictfloat(ent, movetype))
3088         {
3089         case MOVETYPE_PUSH:
3090         case MOVETYPE_FAKEPUSH:
3091                 SV_Physics_Pusher (ent);
3092                 break;
3093         case MOVETYPE_NONE:
3094                 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3095                 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3096                         SV_RunThink (ent);
3097                 break;
3098         case MOVETYPE_FOLLOW:
3099                 SV_RunThink (ent);
3100                 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3101                         SV_Physics_Follow (ent);
3102                 break;
3103         case MOVETYPE_NOCLIP:
3104                 SV_RunThink(ent);
3105                 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3106                 {
3107                         SV_CheckWater(ent);
3108                         VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3109                         VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3110                 }
3111                 break;
3112         case MOVETYPE_STEP:
3113                 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3114                         SV_Physics_Step (ent);
3115                 if (SV_RunThink(ent))
3116                 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
3117                 {
3118                         ent->priv.server->waterposition_forceupdate = false;
3119                         VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
3120                         SV_CheckWaterTransition(ent);
3121                 }
3122                 break;
3123         case MOVETYPE_WALK:
3124                 SV_RunThink (ent);
3125                 // don't run physics here if running asynchronously
3126                 if (host_client->clmovement_inputtimeout <= 0)
3127                         SV_WalkMove (ent);
3128                 break;
3129         case MOVETYPE_TOSS:
3130         case MOVETYPE_BOUNCE:
3131         case MOVETYPE_BOUNCEMISSILE:
3132         case MOVETYPE_FLYMISSILE:
3133                 // regular thinking
3134                 SV_RunThink (ent);
3135                 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3136                         SV_Physics_Toss (ent);
3137                 break;
3138         case MOVETYPE_FLY:
3139         case MOVETYPE_FLY_WORLDONLY:
3140                 SV_RunThink (ent);
3141                 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3142                         SV_WalkMove (ent);
3143                 break;
3144         case MOVETYPE_PHYSICS:
3145                 SV_RunThink (ent);
3146                 break;
3147         default:
3148                 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
3149                         break;
3150                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3151                 break;
3152         }
3153
3154         SV_CheckVelocity (ent);
3155
3156         SV_LinkEdict(ent);
3157         SV_LinkEdict_TouchAreaGrid(ent);
3158
3159         SV_CheckVelocity (ent);
3160 }
3161
3162 /*
3163 ================
3164 SV_Physics
3165
3166 ================
3167 */
3168 void SV_Physics (void)
3169 {
3170         prvm_prog_t *prog = SVVM_prog;
3171         int i;
3172         prvm_edict_t *ent;
3173
3174         // free memory for resources that are no longer referenced
3175         PRVM_GarbageCollection(prog);
3176
3177 // let the progs know that a new frame has started
3178         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3179         PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3180         PRVM_serverglobalfloat(time) = sv.time;
3181         PRVM_serverglobalfloat(frametime) = sv.frametime;
3182         prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3183
3184 #ifdef USEODE
3185         // run physics engine
3186         World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3187 #endif
3188
3189 //
3190 // treat each object in turn
3191 //
3192
3193         // if force_retouch, relink all the entities
3194         if (PRVM_serverglobalfloat(force_retouch) > 0)
3195                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3196                         if (!ent->free)
3197                                 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3198
3199         if (sv_gameplayfix_consistentplayerprethink.integer)
3200         {
3201                 // run physics on the client entities in 3 stages
3202                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3203                         if (!ent->free)
3204                                 SV_Physics_ClientEntity_PreThink(ent);
3205
3206                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3207                         if (!ent->free)
3208                                 SV_Physics_ClientEntity(ent);
3209
3210                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3211                         if (!ent->free)
3212                                 SV_Physics_ClientEntity_PostThink(ent);
3213         }
3214         else
3215         {
3216                 // run physics on the client entities
3217                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3218                 {
3219                         if (!ent->free)
3220                         {
3221                                 SV_Physics_ClientEntity_PreThink(ent);
3222                                 SV_Physics_ClientEntity(ent);
3223                                 SV_Physics_ClientEntity_PostThink(ent);
3224                         }
3225                 }
3226         }
3227
3228         // run physics on all the non-client entities
3229         if (!sv_freezenonclients.integer)
3230         {
3231                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3232                         if (!ent->free)
3233                                 SV_Physics_Entity(ent);
3234                 // make a second pass to see if any ents spawned this frame and make
3235                 // sure they run their move/think
3236                 if (sv_gameplayfix_delayprojectiles.integer < 0)
3237                         for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3238                                 if (!ent->priv.server->move && !ent->free)
3239                                         SV_Physics_Entity(ent);
3240         }
3241
3242         if (PRVM_serverglobalfloat(force_retouch) > 0)
3243                 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3244
3245         // LadyHavoc: endframe support
3246         if (PRVM_serverfunction(EndFrame))
3247         {
3248                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3249                 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3250                 PRVM_serverglobalfloat(time) = sv.time;
3251                 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3252         }
3253
3254         // decrement prog->num_edicts if the highest number entities died
3255         for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3256
3257         if (!sv_freezenonclients.integer)
3258                 sv.time += sv.frametime;
3259 }