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