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