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