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