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