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