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