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