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