]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
Add user defined movetypes extension
[xonotic/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23 #include "prvm_cmds.h"
24
25 /*
26
27
28 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
29
30 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
31
32 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
33 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
34 corpses are SOLID_NOT and MOVETYPE_TOSS
35 crates are SOLID_BBOX and MOVETYPE_TOSS
36 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
37 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38
39 solid_edge items only clip against bsp models.
40
41 */
42
43 #define MOVE_EPSILON    0.01
44
45 void SV_Physics_Toss (prvm_edict_t *ent);
46
47 int SV_GetPitchSign(prvm_prog_t *prog, prvm_edict_t *ent)
48 {
49         dp_model_t *model;
50         if (
51                         (model = SV_GetModelFromEdict(ent))
52                         ?
53                         model->type == mod_alias
54                         :
55                         (
56                          (((unsigned char)PRVM_serveredictfloat(ent, pflags)) & PFLAGS_FULLDYNAMIC)
57                          ||
58                          ((gamemode == GAME_TENEBRAE) && ((unsigned int)PRVM_serveredictfloat(ent, effects) & (16 | 32)))
59                         )
60            )
61                 return -1;
62         return 1;
63 }
64
65 /*
66 ===============================================================================
67
68 LINE TESTING IN HULLS
69
70 ===============================================================================
71 */
72
73 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
74 {
75         prvm_prog_t *prog = SVVM_prog;
76         if (passedict)
77         {
78                 int dphitcontentsmask = (int)PRVM_serveredictfloat(passedict, dphitcontentsmask);
79                 if (dphitcontentsmask)
80                         return dphitcontentsmask;
81                 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_SLIDEBOX)
82                 {
83                         if ((int)PRVM_serveredictfloat(passedict, flags) & FL_MONSTER)
84                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
85                         else
86                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
87                 }
88                 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_CORPSE)
89                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
90                 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_TRIGGER)
91                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
92                 else
93                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
94         }
95         else
96                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
97 }
98
99 /*
100 ==================
101 SV_TracePoint
102 ==================
103 */
104 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
105 {
106         prvm_prog_t *prog = SVVM_prog;
107         int i, bodysupercontents;
108         int passedictprog;
109         float pitchsign = 1;
110         prvm_edict_t *traceowner, *touch;
111         trace_t trace;
112         // temporary storage because prvm_vec_t may differ from vec_t
113         vec3_t touchmins, touchmaxs;
114         // bounding box of entire move area
115         vec3_t clipboxmins, clipboxmaxs;
116         // size when clipping against monsters
117         vec3_t clipmins2, clipmaxs2;
118         // start and end origin of move
119         vec3_t clipstart;
120         // trace results
121         trace_t cliptrace;
122         // matrices to transform into/out of other entity's space
123         matrix4x4_t matrix, imatrix;
124         // model of other entity
125         dp_model_t *model;
126         // list of entities to test for collisions
127         int numtouchedicts;
128         static prvm_edict_t *touchedicts[MAX_EDICTS];
129
130         //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
131
132         VectorCopy(start, clipstart);
133         VectorClear(clipmins2);
134         VectorClear(clipmaxs2);
135 #if COLLISIONPARANOID >= 3
136         Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
137 #endif
138
139         // clip to world
140         Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
141         cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
142         if (cliptrace.startsolid || cliptrace.fraction < 1)
143                 cliptrace.ent = prog->edicts;
144         if (type == MOVE_WORLDONLY)
145                 goto finished;
146
147         if (type == MOVE_MISSILE)
148         {
149                 // LordHavoc: modified this, was = -15, now -= 15
150                 for (i = 0;i < 3;i++)
151                 {
152                         clipmins2[i] -= 15;
153                         clipmaxs2[i] += 15;
154                 }
155         }
156
157         // create the bounding box of the entire move
158         for (i = 0;i < 3;i++)
159         {
160                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
161                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
162         }
163
164         // debug override to test against everything
165         if (sv_debugmove.integer)
166         {
167                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
168                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
169         }
170
171         // if the passedict is world, make it NULL (to avoid two checks each time)
172         if (passedict == prog->edicts)
173                 passedict = NULL;
174         // precalculate prog value for passedict for comparisons
175         passedictprog = PRVM_EDICT_TO_PROG(passedict);
176         // precalculate passedict's owner edict pointer for comparisons
177         traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
178
179         // clip to entities
180         // because this uses World_EntitiestoBox, we know all entity boxes overlap
181         // the clip region, so we can skip culling checks in the loop below
182         numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
183         if (numtouchedicts > MAX_EDICTS)
184         {
185                 // this never happens
186                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
187                 numtouchedicts = MAX_EDICTS;
188         }
189         for (i = 0;i < numtouchedicts;i++)
190         {
191                 touch = touchedicts[i];
192
193                 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
194                         continue;
195                 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
196                         continue;
197
198                 if (passedict)
199                 {
200                         // don't clip against self
201                         if (passedict == touch)
202                                 continue;
203                         // don't clip owned entities against owner
204                         if (traceowner == touch)
205                                 continue;
206                         // don't clip owner against owned entities
207                         if (passedictprog == PRVM_serveredictedict(touch, owner))
208                                 continue;
209                         // don't clip points against points (they can't collide)
210                         if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
211                                 continue;
212                 }
213
214                 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
215
216                 // might interact, so do an exact clip
217                 model = NULL;
218                 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
219                 {
220                         model = SV_GetModelFromEdict(touch);
221                         pitchsign = SV_GetPitchSign(prog, touch);
222                 }
223                 if (model)
224                         Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
225                 else
226                         Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
227                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
228                 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
229                 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
230                 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
231                 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
232                 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
233                 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
234                         Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask, 0.0f);
235                 else
236                         Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
237
238                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
239         }
240
241 finished:
242         return cliptrace;
243 }
244
245 /*
246 ==================
247 SV_TraceLine
248 ==================
249 */
250 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, 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);
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, 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, extend);
384                 else
385                         Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, 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, 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, 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);
439                 else
440                         trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, 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, 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, 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, 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)
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);
580         if (passedict)
581         {
582                 VectorCopy(trace.endpos, temp);
583                 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).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, 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, 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, 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);
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), 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), 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), 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)).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, 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, 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));
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 type;
2288         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2289         trace_t downtrace, trace;
2290         qboolean applygravity;
2291
2292         // if frametime is 0 (due to client sending the same timestamp twice),
2293         // don't move
2294         if (sv.frametime <= 0)
2295                 return;
2296
2297         if (sv_gameplayfix_unstickplayers.integer)
2298                 SV_CheckStuck (ent);
2299
2300         applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2301
2302         hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2303
2304         SV_CheckVelocity(ent);
2305
2306         // do a regular slide move unless it looks like you ran into a step
2307         oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2308
2309         VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2310         VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2311
2312         clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2313
2314         if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2315         if(!(clip & 1))
2316         {
2317                 // only try this if there was no floor in the way in the trace (no,
2318                 // this check seems to be not REALLY necessary, because if clip & 1,
2319                 // our trace will hit that thing too)
2320                 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2321                 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2322                 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2323                         type = MOVE_MISSILE;
2324                 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2325                         type = MOVE_WORLDONLY;
2326                 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2327                         type = MOVE_NOMONSTERS; // only clip against bmodels
2328                 else
2329                         type = MOVE_NORMAL;
2330                 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2331                 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2332                 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), collision_extendmovelength.value);
2333                 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2334                         clip |= 1; // but we HAVE found a floor
2335         }
2336
2337         // if the move did not hit the ground at any point, we're not on ground
2338         if(!(clip & 1))
2339                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2340
2341         SV_CheckVelocity(ent);
2342         SV_LinkEdict(ent);
2343         SV_LinkEdict_TouchAreaGrid(ent);
2344
2345         if(clip & 8) // teleport
2346                 return;
2347
2348         if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2349                 return;
2350
2351         if (sv_nostep.integer)
2352                 return;
2353
2354         VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2355         VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2356         //originalmove_clip = clip;
2357         originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2358         originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2359
2360         // if move didn't block on a step, return
2361         if (clip & 2)
2362         {
2363                 // if move was not trying to move into the step, return
2364                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2365                         return;
2366
2367                 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2368                 {
2369                         // return if gibbed by a trigger
2370                         if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2371                                 return;
2372
2373                         // return if attempting to jump while airborn (unless sv_jumpstep)
2374                         if (!sv_jumpstep.integer)
2375                                 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2376                                         return;
2377                 }
2378
2379                 // try moving up and forward to go up a step
2380                 // back to start pos
2381                 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2382                 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2383
2384                 // move up
2385                 VectorClear (upmove);
2386                 upmove[2] = sv_stepheight.value;
2387                 if(!SV_PushEntity(&trace, ent, upmove, true))
2388                 {
2389                         // we got teleported when upstepping... must abort the move
2390                         return;
2391                 }
2392
2393                 // move forward
2394                 PRVM_serveredictvector(ent, velocity)[2] = 0;
2395                 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2396                 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2397                 if(clip & 8)
2398                 {
2399                         // we got teleported when upstepping... must abort the move
2400                         // note that z velocity handling may not be what QC expects here, but we cannot help it
2401                         return;
2402                 }
2403
2404                 SV_CheckVelocity(ent);
2405                 SV_LinkEdict(ent);
2406                 SV_LinkEdict_TouchAreaGrid(ent);
2407
2408                 // check for stuckness, possibly due to the limited precision of floats
2409                 // in the clipping hulls
2410                 if (clip
2411                  && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2412                  && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2413                 {
2414                         //Con_Printf("wall\n");
2415                         // stepping up didn't make any progress, revert to original move
2416                         VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2417                         VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2418                         //clip = originalmove_clip;
2419                         PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2420                         PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2421                         // now try to unstick if needed
2422                         //clip = SV_TryUnstick (ent, oldvel);
2423                         return;
2424                 }
2425
2426                 //Con_Printf("step - ");
2427
2428                 // extra friction based on view angle
2429                 if (clip & 2 && sv_wallfriction.integer)
2430                         SV_WallFriction (ent, stepnormal);
2431         }
2432         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2433         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))
2434                 return;
2435
2436         // move down
2437         VectorClear (downmove);
2438         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2439         if(!SV_PushEntity (&downtrace, ent, downmove, true))
2440         {
2441                 // we got teleported when downstepping... must abort the move
2442                 return;
2443         }
2444
2445         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2446         {
2447                 // this has been disabled so that you can't jump when you are stepping
2448                 // up while already jumping (also known as the Quake2 double jump bug)
2449 #if 0
2450                 // LordHavoc: disabled this check so you can walk on monsters/players
2451                 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2452                 {
2453                         //Con_Printf("onground\n");
2454                         PRVM_serveredictfloat(ent, flags) =     (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2455                         PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2456                 }
2457 #endif
2458         }
2459         else
2460         {
2461                 //Con_Printf("slope\n");
2462                 // if the push down didn't end up on good ground, use the move without
2463                 // the step up.  This happens near wall / slope combinations, and can
2464                 // cause the player to hop up higher on a slope too steep to climb
2465                 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2466                 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2467                 //clip = originalmove_clip;
2468                 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2469                 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2470         }
2471
2472         SV_CheckVelocity(ent);
2473         SV_LinkEdict(ent);
2474         SV_LinkEdict_TouchAreaGrid(ent);
2475 }
2476
2477 //============================================================================
2478
2479 /*
2480 =============
2481 SV_Physics_Follow
2482
2483 Entities that are "stuck" to another entity
2484 =============
2485 */
2486 static void SV_Physics_Follow (prvm_edict_t *ent)
2487 {
2488         prvm_prog_t *prog = SVVM_prog;
2489         vec3_t vf, vr, vu, angles, v;
2490         prvm_edict_t *e;
2491
2492         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2493         e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2494         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])
2495         {
2496                 // quick case for no rotation
2497                 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2498         }
2499         else
2500         {
2501                 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2502                 angles[1] =  PRVM_serveredictvector(ent, punchangle)[1];
2503                 angles[2] =  PRVM_serveredictvector(ent, punchangle)[2];
2504                 AngleVectors (angles, vf, vr, vu);
2505                 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];
2506                 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];
2507                 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];
2508                 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2509                 angles[1] =  PRVM_serveredictvector(e, angles)[1];
2510                 angles[2] =  PRVM_serveredictvector(e, angles)[2];
2511                 AngleVectors (angles, vf, vr, vu);
2512                 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2513                 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2514                 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2515         }
2516         VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2517         SV_LinkEdict(ent);
2518         //SV_LinkEdict_TouchAreaGrid(ent);
2519 }
2520
2521 /*
2522 ==============================================================================
2523
2524 TOSS / BOUNCE
2525
2526 ==============================================================================
2527 */
2528
2529 /*
2530 =============
2531 SV_CheckWaterTransition
2532
2533 =============
2534 */
2535 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2536 {
2537         vec3_t entorigin;
2538         prvm_prog_t *prog = SVVM_prog;
2539         // 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
2540         int cont;
2541         VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2542         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(entorigin));
2543         if (!PRVM_serveredictfloat(ent, watertype))
2544         {
2545                 // just spawned here
2546                 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2547                 {
2548                         PRVM_serveredictfloat(ent, watertype) = cont;
2549                         PRVM_serveredictfloat(ent, waterlevel) = 1;
2550                         return;
2551                 }
2552         }
2553         // DRESK - Support for Entity Contents Transition Event
2554         // NOTE: Call here BEFORE updating the watertype below,
2555         // and suppress watersplash sound if a valid function
2556         // call was made to allow for custom "splash" sounds.
2557         else if( !SV_CheckContentsTransition(ent, cont) )
2558         { // Contents Transition Function Invalid; Potentially Play Water Sound
2559                 // check if the entity crossed into or out of water
2560                 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2561                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2562         }
2563
2564         if (cont <= CONTENTS_WATER)
2565         {
2566                 PRVM_serveredictfloat(ent, watertype) = cont;
2567                 PRVM_serveredictfloat(ent, waterlevel) = 1;
2568         }
2569         else
2570         {
2571                 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2572                 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2573         }
2574 }
2575
2576 /*
2577 =============
2578 SV_Physics_Toss
2579
2580 Toss, bounce, and fly movement.  When onground, do nothing.
2581 =============
2582 */
2583
2584 void SV_Physics_Toss (prvm_edict_t *ent)
2585 {
2586         prvm_prog_t *prog = SVVM_prog;
2587         trace_t trace;
2588         vec3_t move;
2589         vec_t movetime;
2590         int bump;
2591         prvm_edict_t *groundentity;
2592         float d, ent_gravity;
2593         float bouncefactor;
2594         float bouncestop;
2595
2596 // if onground, return without moving
2597         if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2598         {
2599                 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2600                 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2601                 {
2602                         // don't stick to ground if onground and moving upward
2603                         PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2604                 }
2605                 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2606                 {
2607                         // we can trust FL_ONGROUND if groundentity is world because it never moves
2608                         return;
2609                 }
2610                 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2611                 {
2612                         // if ent was supported by a brush model on previous frame,
2613                         // and groundentity is now freed, set groundentity to 0 (world)
2614                         // which leaves it suspended in the air
2615                         PRVM_serveredictedict(ent, groundentity) = 0;
2616                         if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2617                                 return;
2618                 }
2619                 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2620                 {
2621                         // don't slide if still touching the groundentity
2622                         return;
2623                 }
2624         }
2625         ent->priv.server->suspendedinairflag = false;
2626
2627         SV_CheckVelocity (ent);
2628
2629 // add gravity
2630         if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2631                 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2632
2633 // move angles
2634         VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2635
2636         movetime = sv.frametime;
2637         for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2638         {
2639         // move origin
2640                 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2641                 if(!SV_PushEntity(&trace, ent, move, true))
2642                         return; // teleported
2643                 if (ent->priv.server->free)
2644                         return;
2645                 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2646                 {
2647                         // try to unstick the entity
2648                         SV_UnstickEntity(ent);
2649                         if(!SV_PushEntity(&trace, ent, move, true))
2650                                 return; // teleported
2651                         if (ent->priv.server->free)
2652                                 return;
2653                 }
2654                 if (trace.fraction == 1)
2655                         break;
2656                 movetime *= 1 - min(1, trace.fraction);
2657                 switch((int)PRVM_serveredictfloat(ent, movetype))
2658                 {
2659                 case MOVETYPE_BOUNCEMISSILE:
2660                         bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2661                         if (!bouncefactor)
2662                                 bouncefactor = 1.0f;
2663
2664                         ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2665                         PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2666                         if (!sv_gameplayfix_slidemoveprojectiles.integer)
2667                                 movetime = 0;
2668                         break;
2669                 case MOVETYPE_BOUNCE:
2670                         bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2671                         if (!bouncefactor)
2672                                 bouncefactor = 0.5f;
2673
2674                         bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2675                         if (!bouncestop)
2676                                 bouncestop = 60.0f / 800.0f;
2677
2678                         ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2679                         ent_gravity = PRVM_serveredictfloat(ent, gravity);
2680                         if (!ent_gravity)
2681                                 ent_gravity = 1.0f;
2682                         // LordHavoc: fixed grenades not bouncing when fired down a slope
2683                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
2684                                 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2685                         else
2686                                 d = PRVM_serveredictvector(ent, velocity)[2];
2687                         if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2688                         {
2689                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2690                                 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2691                                 VectorClear(PRVM_serveredictvector(ent, velocity));
2692                                 VectorClear(PRVM_serveredictvector(ent, avelocity));
2693                                 movetime = 0;
2694                         }
2695                         else
2696                         {
2697                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2698                                 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2699                                         movetime = 0;
2700                         }
2701                         break;
2702                 default:
2703                         ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2704                         if (trace.plane.normal[2] > 0.7)
2705                         {
2706                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2707                                 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2708                                 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2709                                         ent->priv.server->suspendedinairflag = true;
2710                                 VectorClear (PRVM_serveredictvector(ent, velocity));
2711                                 VectorClear (PRVM_serveredictvector(ent, avelocity));
2712                                 movetime = 0;
2713                         }
2714                         else
2715                         {
2716                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2717                                 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2718                                         movetime = 0;
2719                         }
2720                         break;
2721                 }
2722         }
2723
2724 // check for in water
2725         SV_CheckWaterTransition (ent);
2726 }
2727
2728 /*
2729 ===============================================================================
2730
2731 STEPPING MOVEMENT
2732
2733 ===============================================================================
2734 */
2735
2736 /*
2737 =============
2738 SV_Physics_Step
2739
2740 Monsters freefall when they don't have a ground entity, otherwise
2741 all movement is done with discrete steps.
2742
2743 This is also used for objects that have become still on the ground, but
2744 will fall if the floor is pulled out from under them.
2745 =============
2746 */
2747 static void SV_Physics_Step (prvm_edict_t *ent)
2748 {
2749         prvm_prog_t *prog = SVVM_prog;
2750         int flags = (int)PRVM_serveredictfloat(ent, flags);
2751
2752         // DRESK
2753         // Backup Velocity in the event that movetypesteplandevent is called,
2754         // to provide a parameter with the entity's velocity at impact.
2755         vec3_t backupVelocity;
2756         VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2757         // don't fall at all if fly/swim
2758         if (!(flags & (FL_FLY | FL_SWIM)))
2759         {
2760                 if (flags & FL_ONGROUND)
2761                 {
2762                         // freefall if onground and moving upward
2763                         // freefall if not standing on a world surface (it may be a lift or trap door)
2764                         if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2765                         {
2766                                 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2767                                 SV_CheckVelocity(ent);
2768                                 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2769                                 SV_LinkEdict(ent);
2770                                 SV_LinkEdict_TouchAreaGrid(ent);
2771                                 ent->priv.server->waterposition_forceupdate = true;
2772                         }
2773                 }
2774                 else
2775                 {
2776                         // freefall if not onground
2777                         int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2778
2779                         SV_CheckVelocity(ent);
2780                         SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2781                         SV_LinkEdict(ent);
2782                         SV_LinkEdict_TouchAreaGrid(ent);
2783
2784                         // just hit ground
2785                         if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2786                         {
2787                                 // DRESK - Check for Entity Land Event Function
2788                                 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2789                                 { // Valid Function; Execute
2790                                         // Prepare Parameters
2791                                         // Assign Velocity at Impact
2792                                         PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2793                                         PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2794                                         PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2795                                         // Assign Self
2796                                         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2797                                         // Set Time
2798                                         PRVM_serverglobalfloat(time) = sv.time;
2799                                         // Execute VM Function
2800                                         prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2801                                 }
2802                                 else
2803                                 // Check for Engine Landing Sound
2804                                 if(sv_sound_land.string)
2805                                         SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2806                         }
2807                         ent->priv.server->waterposition_forceupdate = true;
2808                 }
2809         }
2810 }
2811
2812 //============================================================================
2813
2814 static void SV_Physics_Entity (prvm_edict_t *ent)
2815 {
2816         prvm_prog_t *prog = SVVM_prog;
2817         // don't run think/move on newly spawned projectiles as it messes up
2818         // movement interpolation and rocket trails, and is inconsistent with
2819         // respect to entities spawned in the same frame
2820         // (if an ent spawns a higher numbered ent, it moves in the same frame,
2821         //  but if it spawns a lower numbered ent, it doesn't - this never moves
2822         //  ents in the first frame regardless)
2823         qboolean runmove = ent->priv.server->move;
2824         ent->priv.server->move = true;
2825         if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2826                 return;
2827         switch ((int) PRVM_serveredictfloat(ent, movetype))
2828         {
2829         case MOVETYPE_PUSH:
2830         case MOVETYPE_FAKEPUSH:
2831                 SV_Physics_Pusher (ent);
2832                 break;
2833         case MOVETYPE_NONE:
2834                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2835                 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2836                         SV_RunThink (ent);
2837                 break;
2838         case MOVETYPE_FOLLOW:
2839                 if(SV_RunThink(ent))
2840                         SV_Physics_Follow (ent);
2841                 break;
2842         case MOVETYPE_NOCLIP:
2843                 if (SV_RunThink(ent))
2844                 {
2845                         SV_CheckWater(ent);
2846                         VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2847                         VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2848                 }
2849                 SV_LinkEdict(ent);
2850                 break;
2851         case MOVETYPE_STEP:
2852                 SV_Physics_Step (ent);
2853                 // regular thinking
2854                 if (SV_RunThink(ent))
2855                 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2856                 {
2857                         ent->priv.server->waterposition_forceupdate = false;
2858                         VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2859                         SV_CheckWaterTransition(ent);
2860                 }
2861                 break;
2862         case MOVETYPE_WALK:
2863                 if (SV_RunThink (ent))
2864                         SV_WalkMove (ent);
2865                 break;
2866         case MOVETYPE_TOSS:
2867         case MOVETYPE_BOUNCE:
2868         case MOVETYPE_BOUNCEMISSILE:
2869         case MOVETYPE_FLYMISSILE:
2870         case MOVETYPE_FLY:
2871         case MOVETYPE_FLY_WORLDONLY:
2872                 // regular thinking
2873                 if (SV_RunThink (ent))
2874                         SV_Physics_Toss (ent);
2875                 break;
2876         case MOVETYPE_PHYSICS:
2877                 if (SV_RunThink(ent))
2878                 {
2879                         SV_LinkEdict(ent);
2880                         SV_LinkEdict_TouchAreaGrid(ent);
2881                 }
2882                 break;
2883         default:
2884                 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2885                         break;
2886                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2887                 break;
2888         }
2889 }
2890
2891 static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
2892 {
2893         prvm_prog_t *prog = SVVM_prog;
2894
2895         // don't run think at all, that is done during server frames
2896         // instead, call the movetypes directly so they match client input
2897
2898         // This probably only makes sense for CSQC-networked (SendEntity field set) player entities
2899         switch ((int) PRVM_serveredictfloat(ent, movetype))
2900         {
2901         case MOVETYPE_PUSH:
2902         case MOVETYPE_FAKEPUSH:
2903                 // push physics relies heavily on think times and calls, and so cannot be predicted currently
2904                 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2905                 break;
2906         case MOVETYPE_NONE:
2907                 break;
2908         case MOVETYPE_FOLLOW:
2909                 SV_Physics_Follow (ent);
2910                 break;
2911         case MOVETYPE_NOCLIP:
2912                 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2913                 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2914                 break;
2915         case MOVETYPE_STEP:
2916                 SV_Physics_Step (ent);
2917                 break;
2918         case MOVETYPE_WALK:
2919                 SV_WalkMove (ent);
2920                 break;
2921         case MOVETYPE_TOSS:
2922         case MOVETYPE_BOUNCE:
2923         case MOVETYPE_BOUNCEMISSILE:
2924         case MOVETYPE_FLYMISSILE:
2925                 SV_Physics_Toss (ent);
2926                 break;
2927         case MOVETYPE_FLY:
2928         case MOVETYPE_FLY_WORLDONLY:
2929                 SV_WalkMove (ent);
2930                 break;
2931         case MOVETYPE_PHYSICS:
2932                 break;
2933         default:
2934                 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2935                         break;
2936                 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2937                 break;
2938         }
2939 }
2940
2941 void SV_Physics_ClientMove(void)
2942 {
2943         prvm_prog_t *prog = SVVM_prog;
2944         prvm_edict_t *ent;
2945         ent = host_client->edict;
2946
2947         // call player physics, this needs the proper frametime
2948         PRVM_serverglobalfloat(frametime) = sv.frametime;
2949         SV_ClientThink();
2950
2951         // call standard client pre-think, with frametime = 0
2952         PRVM_serverglobalfloat(time) = sv.time;
2953         PRVM_serverglobalfloat(frametime) = 0;
2954         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2955         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2956         PRVM_serverglobalfloat(frametime) = sv.frametime;
2957
2958         // make sure the velocity is sane (not a NaN)
2959         SV_CheckVelocity(ent);
2960
2961         // perform movetype behaviour
2962         // note: will always be MOVETYPE_WALK if disableclientprediction = 0
2963         SV_Physics_ClientEntity_NoThink (ent);
2964
2965         // call standard player post-think, with frametime = 0
2966         PRVM_serverglobalfloat(time) = sv.time;
2967         PRVM_serverglobalfloat(frametime) = 0;
2968         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2969         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2970         PRVM_serverglobalfloat(frametime) = sv.frametime;
2971
2972         if(PRVM_serveredictfloat(ent, fixangle))
2973         {
2974                 // angle fixing was requested by physics code...
2975                 // so store the current angles for later use
2976                 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2977                 host_client->fixangle_angles_set = TRUE;
2978
2979                 // and clear fixangle for the next frame
2980                 PRVM_serveredictfloat(ent, fixangle) = 0;
2981         }
2982 }
2983
2984 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2985 {
2986         prvm_prog_t *prog = SVVM_prog;
2987         // don't do physics on disconnected clients, FrikBot relies on this
2988         if (!host_client->begun)
2989                 return;
2990
2991         // make sure the velocity is sane (not a NaN)
2992         SV_CheckVelocity(ent);
2993
2994         // don't run physics here if running asynchronously
2995         if (host_client->clmovement_inputtimeout <= 0)
2996         {
2997                 SV_ClientThink();
2998                 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2999         }
3000
3001         // make sure the velocity is still sane (not a NaN)
3002         SV_CheckVelocity(ent);
3003
3004         // call standard client pre-think
3005         PRVM_serverglobalfloat(time) = sv.time;
3006         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3007         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3008
3009         // make sure the velocity is still sane (not a NaN)
3010         SV_CheckVelocity(ent);
3011 }
3012
3013 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3014 {
3015         prvm_prog_t *prog = SVVM_prog;
3016         // don't do physics on disconnected clients, FrikBot relies on this
3017         if (!host_client->begun)
3018                 return;
3019
3020         // make sure the velocity is sane (not a NaN)
3021         SV_CheckVelocity(ent);
3022
3023         // call standard player post-think
3024         PRVM_serverglobalfloat(time) = sv.time;
3025         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3026         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3027
3028         // make sure the velocity is still sane (not a NaN)
3029         SV_CheckVelocity(ent);
3030
3031         if(PRVM_serveredictfloat(ent, fixangle))
3032         {
3033                 // angle fixing was requested by physics code...
3034                 // so store the current angles for later use
3035                 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3036                 host_client->fixangle_angles_set = TRUE;
3037
3038                 // and clear fixangle for the next frame
3039                 PRVM_serveredictfloat(ent, fixangle) = 0;
3040         }
3041
3042         // decrement the countdown variable used to decide when to go back to
3043         // synchronous physics
3044         if (host_client->clmovement_inputtimeout > sv.frametime)
3045                 host_client->clmovement_inputtimeout -= sv.frametime;
3046         else
3047                 host_client->clmovement_inputtimeout = 0;
3048 }
3049
3050 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3051 {
3052         prvm_prog_t *prog = SVVM_prog;
3053         // don't do physics on disconnected clients, FrikBot relies on this
3054         if (!host_client->begun)
3055         {
3056                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3057                 return;
3058         }
3059
3060         // make sure the velocity is sane (not a NaN)
3061         SV_CheckVelocity(ent);
3062
3063         switch ((int) PRVM_serveredictfloat(ent, movetype))
3064         {
3065         case MOVETYPE_PUSH:
3066         case MOVETYPE_FAKEPUSH:
3067                 SV_Physics_Pusher (ent);
3068                 break;
3069         case MOVETYPE_NONE:
3070                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3071                 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3072                         SV_RunThink (ent);
3073                 break;
3074         case MOVETYPE_FOLLOW:
3075                 SV_RunThink (ent);
3076                 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3077                         SV_Physics_Follow (ent);
3078                 break;
3079         case MOVETYPE_NOCLIP:
3080                 SV_RunThink(ent);
3081                 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3082                 {
3083                         SV_CheckWater(ent);
3084                         VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3085                         VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3086                 }
3087                 break;
3088         case MOVETYPE_STEP:
3089                 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3090                         SV_Physics_Step (ent);
3091                 if (SV_RunThink(ent))
3092                 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
3093                 {
3094                         ent->priv.server->waterposition_forceupdate = false;
3095                         VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
3096                         SV_CheckWaterTransition(ent);
3097                 }
3098                 break;
3099         case MOVETYPE_WALK:
3100                 SV_RunThink (ent);
3101                 // don't run physics here if running asynchronously
3102                 if (host_client->clmovement_inputtimeout <= 0)
3103                         SV_WalkMove (ent);
3104                 break;
3105         case MOVETYPE_TOSS:
3106         case MOVETYPE_BOUNCE:
3107         case MOVETYPE_BOUNCEMISSILE:
3108         case MOVETYPE_FLYMISSILE:
3109                 // regular thinking
3110                 SV_RunThink (ent);
3111                 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3112                         SV_Physics_Toss (ent);
3113                 break;
3114         case MOVETYPE_FLY:
3115         case MOVETYPE_FLY_WORLDONLY:
3116                 SV_RunThink (ent);
3117                 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3118                         SV_WalkMove (ent);
3119                 break;
3120         case MOVETYPE_PHYSICS:
3121                 SV_RunThink (ent);
3122                 break;
3123         default:
3124                 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
3125                         break;
3126                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3127                 break;
3128         }
3129
3130         SV_CheckVelocity (ent);
3131
3132         SV_LinkEdict(ent);
3133         SV_LinkEdict_TouchAreaGrid(ent);
3134
3135         SV_CheckVelocity (ent);
3136 }
3137
3138 /*
3139 ================
3140 SV_Physics
3141
3142 ================
3143 */
3144 void SV_Physics (void)
3145 {
3146         prvm_prog_t *prog = SVVM_prog;
3147         int i;
3148         prvm_edict_t *ent;
3149
3150 // let the progs know that a new frame has started
3151         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3152         PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3153         PRVM_serverglobalfloat(time) = sv.time;
3154         PRVM_serverglobalfloat(frametime) = sv.frametime;
3155         prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3156
3157         // run physics engine
3158         World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3159
3160 //
3161 // treat each object in turn
3162 //
3163
3164         // if force_retouch, relink all the entities
3165         if (PRVM_serverglobalfloat(force_retouch) > 0)
3166                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3167                         if (!ent->priv.server->free)
3168                                 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3169
3170         if (sv_gameplayfix_consistentplayerprethink.integer)
3171         {
3172                 // run physics on the client entities in 3 stages
3173                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3174                         if (!ent->priv.server->free)
3175                                 SV_Physics_ClientEntity_PreThink(ent);
3176
3177                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3178                         if (!ent->priv.server->free)
3179                                 SV_Physics_ClientEntity(ent);
3180
3181                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3182                         if (!ent->priv.server->free)
3183                                 SV_Physics_ClientEntity_PostThink(ent);
3184         }
3185         else
3186         {
3187                 // run physics on the client entities
3188                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3189                 {
3190                         if (!ent->priv.server->free)
3191                         {
3192                                 SV_Physics_ClientEntity_PreThink(ent);
3193                                 SV_Physics_ClientEntity(ent);
3194                                 SV_Physics_ClientEntity_PostThink(ent);
3195                         }
3196                 }
3197         }
3198
3199         // run physics on all the non-client entities
3200         if (!sv_freezenonclients.integer)
3201         {
3202                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3203                         if (!ent->priv.server->free)
3204                                 SV_Physics_Entity(ent);
3205                 // make a second pass to see if any ents spawned this frame and make
3206                 // sure they run their move/think
3207                 if (sv_gameplayfix_delayprojectiles.integer < 0)
3208                         for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3209                                 if (!ent->priv.server->move && !ent->priv.server->free)
3210                                         SV_Physics_Entity(ent);
3211         }
3212
3213         if (PRVM_serverglobalfloat(force_retouch) > 0)
3214                 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3215
3216         // LordHavoc: endframe support
3217         if (PRVM_serverfunction(EndFrame))
3218         {
3219                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3220                 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3221                 PRVM_serverglobalfloat(time) = sv.time;
3222                 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3223         }
3224
3225         // decrement prog->num_edicts if the highest number entities died
3226         for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3227
3228         if (!sv_freezenonclients.integer)
3229                 sv.time += sv.frametime;
3230 }