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