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