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