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