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