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