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