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