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