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