]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
fix Collision_ClipTrace_Line_Sphere calculation of impactdist (had a
[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 original, original_velocity;
1604         vec3_t start;
1605         vec3_t end;
1606
1607         solid = (int)PRVM_serveredictfloat(ent, solid);
1608         movetype = (int)PRVM_serveredictfloat(ent, movetype);
1609         VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1610         VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1611
1612         // move start position out of solids
1613         if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1614         {
1615                 SV_NudgeOutOfSolid(ent);
1616         }
1617
1618         VectorCopy(PRVM_serveredictvector(ent, origin), start);
1619         VectorAdd(start, push, end);
1620
1621         if (movetype == MOVETYPE_FLYMISSILE)
1622                 type = MOVE_MISSILE;
1623         else if (movetype == MOVETYPE_FLY_WORLDONLY)
1624                 type = MOVE_WORLDONLY;
1625         else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1626                 type = MOVE_NOMONSTERS; // only clip against bmodels
1627         else
1628                 type = MOVE_NORMAL;
1629
1630         *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1631         if (trace->bmodelstartsolid && failonbmodelstartsolid)
1632                 return true;
1633
1634         VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1635
1636         VectorCopy(PRVM_serveredictvector(ent, origin), original);
1637         VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1638
1639         SV_LinkEdict(ent);
1640
1641 #if 0
1642         if(!trace->startsolid)
1643         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)
1644         {
1645                 Con_Printf("something eeeeevil happened\n");
1646         }
1647 #endif
1648
1649         if (dolink)
1650                 SV_LinkEdict_TouchAreaGrid(ent);
1651
1652         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))))
1653                 SV_Impact (ent, trace);
1654
1655         return VectorCompare(PRVM_serveredictvector(ent, origin), original) && VectorCompare(PRVM_serveredictvector(ent, velocity), original_velocity);
1656 }
1657
1658
1659 /*
1660 ============
1661 SV_PushMove
1662
1663 ============
1664 */
1665 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1666 {
1667         prvm_prog_t *prog = SVVM_prog;
1668         int i, e, index;
1669         int pusherowner, pusherprog;
1670         int checkcontents;
1671         qboolean rotated;
1672         float savesolid, movetime2, pushltime;
1673         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1674         int num_moved;
1675         int numcheckentities;
1676         static prvm_edict_t *checkentities[MAX_EDICTS];
1677         dp_model_t *pushermodel;
1678         trace_t trace, trace2;
1679         matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1680         static unsigned short moved_edicts[MAX_EDICTS];
1681         vec3_t pivot;
1682
1683         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])
1684         {
1685                 PRVM_serveredictfloat(pusher, ltime) += movetime;
1686                 return;
1687         }
1688
1689         switch ((int) PRVM_serveredictfloat(pusher, solid))
1690         {
1691         // LordHavoc: valid pusher types
1692         case SOLID_BSP:
1693         case SOLID_BBOX:
1694         case SOLID_SLIDEBOX:
1695         case SOLID_CORPSE: // LordHavoc: this would be weird...
1696                 break;
1697         // LordHavoc: no collisions
1698         case SOLID_NOT:
1699         case SOLID_TRIGGER:
1700                 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1701                 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1702                 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1703                 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1704                 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1705                 PRVM_serveredictfloat(pusher, ltime) += movetime;
1706                 SV_LinkEdict(pusher);
1707                 return;
1708         default:
1709                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1710                 return;
1711         }
1712         index = (int) PRVM_serveredictfloat(pusher, modelindex);
1713         if (index < 1 || index >= MAX_MODELS)
1714         {
1715                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1716                 return;
1717         }
1718         pushermodel = SV_GetModelByIndex(index);
1719         pusherowner = PRVM_serveredictedict(pusher, owner);
1720         pusherprog = PRVM_EDICT_TO_PROG(pusher);
1721
1722         rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1723
1724         movetime2 = movetime;
1725         VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1726         VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1727         if (moveangle[0] || moveangle[2])
1728         {
1729                 for (i = 0;i < 3;i++)
1730                 {
1731                         if (move1[i] > 0)
1732                         {
1733                                 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1734                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1735                         }
1736                         else
1737                         {
1738                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1739                                 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1740                         }
1741                 }
1742         }
1743         else if (moveangle[1])
1744         {
1745                 for (i = 0;i < 3;i++)
1746                 {
1747                         if (move1[i] > 0)
1748                         {
1749                                 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1750                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1751                         }
1752                         else
1753                         {
1754                                 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1755                                 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1756                         }
1757                 }
1758         }
1759         else
1760         {
1761                 for (i = 0;i < 3;i++)
1762                 {
1763                         if (move1[i] > 0)
1764                         {
1765                                 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1766                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1767                         }
1768                         else
1769                         {
1770                                 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1771                                 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1772                         }
1773                 }
1774         }
1775
1776         VectorNegate (moveangle, a);
1777         AngleVectorsFLU (a, forward, left, up);
1778
1779         VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1780         VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1781         pushltime = PRVM_serveredictfloat(pusher, ltime);
1782
1783 // move the pusher to its final position
1784
1785         VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1786         VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1787         PRVM_serveredictfloat(pusher, ltime) += movetime;
1788         SV_LinkEdict(pusher);
1789
1790         pushermodel = SV_GetModelFromEdict(pusher);
1791         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);
1792         Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1793
1794         savesolid = PRVM_serveredictfloat(pusher, solid);
1795
1796 // see if any solid entities are inside the final position
1797         num_moved = 0;
1798
1799         if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1800                 numcheckentities = 0;
1801         else // MOVETYPE_PUSH
1802                 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1803         for (e = 0;e < numcheckentities;e++)
1804         {
1805                 prvm_edict_t *check = checkentities[e];
1806                 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1807                 switch(movetype)
1808                 {
1809                 case MOVETYPE_NONE:
1810                 case MOVETYPE_PUSH:
1811                 case MOVETYPE_FOLLOW:
1812                 case MOVETYPE_NOCLIP:
1813                 case MOVETYPE_FLY_WORLDONLY:
1814                         continue;
1815                 default:
1816                         break;
1817                 }
1818
1819                 if (PRVM_serveredictedict(check, owner) == pusherprog)
1820                         continue;
1821
1822                 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1823                         continue;
1824
1825                 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1826
1827                 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1828                 check->priv.server->waterposition_forceupdate = true;
1829
1830                 checkcontents = SV_GenericHitSuperContentsMask(check);
1831
1832                 // if the entity is standing on the pusher, it will definitely be moved
1833                 // if the entity is not standing on the pusher, but is in the pusher's
1834                 // final position, move it
1835                 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1836                 {
1837                         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);
1838                         //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1839                         if (!trace.startsolid)
1840                         {
1841                                 //Con_Printf("- not in solid\n");
1842                                 continue;
1843                         }
1844                 }
1845
1846                 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1847                 //VectorClear(pivot);
1848
1849                 if (rotated)
1850                 {
1851                         vec3_t org2;
1852                         VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1853                         VectorAdd (org, pivot, org);
1854                         org2[0] = DotProduct (org, forward);
1855                         org2[1] = DotProduct (org, left);
1856                         org2[2] = DotProduct (org, up);
1857                         VectorSubtract (org2, org, move);
1858                         VectorAdd (move, move1, move);
1859                 }
1860                 else
1861                         VectorCopy (move1, move);
1862
1863                 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1864
1865                 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1866                 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1867                 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1868
1869                 // physics objects need better collisions than this code can do
1870                 if (movetype == MOVETYPE_PHYSICS)
1871                 {
1872                         VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1873                         SV_LinkEdict(check);
1874                         SV_LinkEdict_TouchAreaGrid(check);
1875                         continue;
1876                 }
1877
1878                 // try moving the contacted entity
1879                 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1880                 if(!SV_PushEntity (&trace, check, move, true, true))
1881                 {
1882                         // entity "check" got teleported
1883                         PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1884                         PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1885                         continue; // pushed enough
1886                 }
1887                 // FIXME: turn players specially
1888                 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1889                 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1890                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1891
1892                 // this trace.fraction < 1 check causes items to fall off of pushers
1893                 // if they pass under or through a wall
1894                 // the groundentity check causes items to fall off of ledges
1895                 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1896                         PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1897
1898                 // if it is still inside the pusher, block
1899                 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);
1900                 if (trace.startsolid)
1901                 {
1902                         vec3_t move2;
1903                         if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1904                         {
1905                                 // hack to invoke all necessary movement triggers
1906                                 VectorClear(move2);
1907                                 if(!SV_PushEntity(&trace2, check, move2, true, true))
1908                                 {
1909                                         // entity "check" got teleported
1910                                         continue;
1911                                 }
1912                                 // we could fix it
1913                                 continue;
1914                         }
1915
1916                         // still inside pusher, so it's really blocked
1917
1918                         // fail the move
1919                         if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1920                                 continue;
1921                         if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1922                         {
1923                                 // corpse
1924                                 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1925                                 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1926                                 continue;
1927                         }
1928
1929                         VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1930                         VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1931                         PRVM_serveredictfloat(pusher, ltime) = pushltime;
1932                         SV_LinkEdict(pusher);
1933
1934                         // move back any entities we already moved
1935                         for (i = 0;i < num_moved;i++)
1936                         {
1937                                 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1938                                 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1939                                 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1940                                 SV_LinkEdict(ed);
1941                         }
1942
1943                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1944                         if (PRVM_serveredictfunction(pusher, blocked))
1945                         {
1946                                 PRVM_serverglobalfloat(time) = sv.time;
1947                                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1948                                 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1949                                 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1950                         }
1951                         break;
1952                 }
1953         }
1954         PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1955         PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1956         PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1957 }
1958
1959 /*
1960 ================
1961 SV_Physics_Pusher
1962
1963 ================
1964 */
1965 static void SV_Physics_Pusher (prvm_edict_t *ent)
1966 {
1967         prvm_prog_t *prog = SVVM_prog;
1968         float thinktime, oldltime, movetime;
1969
1970         oldltime = PRVM_serveredictfloat(ent, ltime);
1971
1972         thinktime = PRVM_serveredictfloat(ent, nextthink);
1973         if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1974         {
1975                 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1976                 if (movetime < 0)
1977                         movetime = 0;
1978         }
1979         else
1980                 movetime = sv.frametime;
1981
1982         if (movetime)
1983                 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
1984                 SV_PushMove (ent, movetime);
1985
1986         if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
1987         {
1988                 PRVM_serveredictfloat(ent, nextthink) = 0;
1989                 PRVM_serverglobalfloat(time) = sv.time;
1990                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1991                 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1992                 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1993         }
1994 }
1995
1996
1997 /*
1998 ===============================================================================
1999
2000 CLIENT MOVEMENT
2001
2002 ===============================================================================
2003 */
2004
2005 static float unstickoffsets[] =
2006 {
2007         // poutting -/+z changes first as they are least weird
2008          0,  0,  -1,
2009          0,  0,  1,
2010          // x or y changes
2011         -1,  0,  0,
2012          1,  0,  0,
2013          0, -1,  0,
2014          0,  1,  0,
2015          // x and y changes
2016         -1, -1,  0,
2017          1, -1,  0,
2018         -1,  1,  0,
2019          1,  1,  0,
2020 };
2021
2022 typedef enum unstickresult_e
2023 {
2024         UNSTICK_STUCK = 0,
2025         UNSTICK_GOOD = 1,
2026         UNSTICK_UNSTUCK = 2
2027 }
2028 unstickresult_t;
2029
2030 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2031 {
2032         prvm_prog_t *prog = SVVM_prog;
2033         int i, maxunstick;
2034
2035         // if not stuck in a bmodel, just return
2036         if (!SV_TestEntityPosition(ent, vec3_origin))
2037                 return UNSTICK_GOOD;
2038
2039         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2040         {
2041                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2042                 {
2043                         VectorCopy(unstickoffsets + i, offset);
2044                         SV_LinkEdict(ent);
2045                         //SV_LinkEdict_TouchAreaGrid(ent);
2046                         return UNSTICK_UNSTUCK;
2047                 }
2048         }
2049
2050         maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2051         // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2052
2053         for(i = 2; i <= maxunstick; ++i)
2054         {
2055                 VectorClear(offset);
2056                 offset[2] = -i;
2057                 if (!SV_TestEntityPosition(ent, offset))
2058                 {
2059                         SV_LinkEdict(ent);
2060                         //SV_LinkEdict_TouchAreaGrid(ent);
2061                         return UNSTICK_UNSTUCK;
2062                 }
2063                 offset[2] = i;
2064                 if (!SV_TestEntityPosition(ent, offset))
2065                 {
2066                         SV_LinkEdict(ent);
2067                         //SV_LinkEdict_TouchAreaGrid(ent);
2068                         return UNSTICK_UNSTUCK;
2069                 }
2070         }
2071
2072         return UNSTICK_STUCK;
2073 }
2074
2075 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2076 {
2077         prvm_prog_t *prog = SVVM_prog;
2078         vec3_t offset;
2079         switch(SV_UnstickEntityReturnOffset(ent, offset))
2080         {
2081                 case UNSTICK_GOOD:
2082                         return true;
2083                 case UNSTICK_UNSTUCK:
2084                         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]);
2085                         return true;
2086                 case UNSTICK_STUCK:
2087                         if (developer_extra.integer)
2088                                 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2089                         return false;
2090                 default:
2091                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2092                         return false;
2093         }
2094 }
2095
2096 /*
2097 =============
2098 SV_CheckStuck
2099
2100 This is a big hack to try and fix the rare case of getting stuck in the world
2101 clipping hull.
2102 =============
2103 */
2104 static void SV_CheckStuck (prvm_edict_t *ent)
2105 {
2106         prvm_prog_t *prog = SVVM_prog;
2107         vec3_t offset;
2108
2109         switch(SV_UnstickEntityReturnOffset(ent, offset))
2110         {
2111                 case UNSTICK_GOOD:
2112                         VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2113                         break;
2114                 case UNSTICK_UNSTUCK:
2115                         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]);
2116                         break;
2117                 case UNSTICK_STUCK:
2118                         VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2119                         if (!SV_TestEntityPosition(ent, offset))
2120                         {
2121                                 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)));
2122                                 SV_LinkEdict(ent);
2123                                 //SV_LinkEdict_TouchAreaGrid(ent);
2124                         }
2125                         else
2126                                 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2127                         break;
2128                 default:
2129                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2130         }
2131 }
2132
2133
2134 /*
2135 =============
2136 SV_CheckWater
2137 =============
2138 */
2139 static qboolean SV_CheckWater (prvm_edict_t *ent)
2140 {
2141         prvm_prog_t *prog = SVVM_prog;
2142         int cont;
2143         int nNativeContents;
2144         vec3_t point;
2145
2146         point[0] = PRVM_serveredictvector(ent, origin)[0];
2147         point[1] = PRVM_serveredictvector(ent, origin)[1];
2148         point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2149
2150         // DRESK - Support for Entity Contents Transition Event
2151         // NOTE: Some logic needed to be slightly re-ordered
2152         // to not affect performance and allow for the feature.
2153
2154         // Acquire Super Contents Prior to Resets
2155         cont = SV_PointSuperContents(point);
2156         // Acquire Native Contents Here
2157         nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2158
2159         // DRESK - Support for Entity Contents Transition Event
2160         if(PRVM_serveredictfloat(ent, watertype))
2161                 // Entity did NOT Spawn; Check
2162                 SV_CheckContentsTransition(ent, nNativeContents);
2163
2164
2165         PRVM_serveredictfloat(ent, waterlevel) = 0;
2166         PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2167         cont = SV_PointSuperContents(point);
2168         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2169         {
2170                 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2171                 PRVM_serveredictfloat(ent, waterlevel) = 1;
2172                 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2173                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2174                 {
2175                         PRVM_serveredictfloat(ent, waterlevel) = 2;
2176                         point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2177                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2178                                 PRVM_serveredictfloat(ent, waterlevel) = 3;
2179                 }
2180         }
2181
2182         return PRVM_serveredictfloat(ent, waterlevel) > 1;
2183 }
2184
2185 /*
2186 ============
2187 SV_WallFriction
2188
2189 ============
2190 */
2191 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2192 {
2193         prvm_prog_t *prog = SVVM_prog;
2194         float d, i;
2195         vec3_t forward, into, side;
2196
2197         AngleVectors (PRVM_serveredictvector(ent, v_angle), forward, NULL, NULL);
2198         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2199         {
2200                 // cut the tangential velocity
2201                 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2202                 VectorScale (stepnormal, i, into);
2203                 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2204                 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2205                 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2206         }
2207 }
2208
2209 #if 0
2210 /*
2211 =====================
2212 SV_TryUnstick
2213
2214 Player has come to a dead stop, possibly due to the problem with limited
2215 float precision at some angle joins in the BSP hull.
2216
2217 Try fixing by pushing one pixel in each direction.
2218
2219 This is a hack, but in the interest of good gameplay...
2220 ======================
2221 */
2222 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2223 {
2224         int i, clip;
2225         vec3_t oldorg, dir;
2226
2227         VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2228         VectorClear (dir);
2229
2230         for (i=0 ; i<8 ; i++)
2231         {
2232                 // try pushing a little in an axial direction
2233                 switch (i)
2234                 {
2235                         case 0: dir[0] = 2; dir[1] = 0; break;
2236                         case 1: dir[0] = 0; dir[1] = 2; break;
2237                         case 2: dir[0] = -2; dir[1] = 0; break;
2238                         case 3: dir[0] = 0; dir[1] = -2; break;
2239                         case 4: dir[0] = 2; dir[1] = 2; break;
2240                         case 5: dir[0] = -2; dir[1] = 2; break;
2241                         case 6: dir[0] = 2; dir[1] = -2; break;
2242                         case 7: dir[0] = -2; dir[1] = -2; break;
2243                 }
2244
2245                 SV_PushEntity (&trace, ent, dir, false, true);
2246
2247                 // retry the original move
2248                 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2249                 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2250                 PRVM_serveredictvector(ent, velocity)[2] = 0;
2251                 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2252
2253                 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2254                  || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2255                 {
2256                         Con_DPrint("TryUnstick - success.\n");
2257                         return clip;
2258                 }
2259
2260                 // go back to the original pos and try again
2261                 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2262         }
2263
2264         // still not moving
2265         VectorClear (PRVM_serveredictvector(ent, velocity));
2266         Con_DPrint("TryUnstick - failure.\n");
2267         return 7;
2268 }
2269 #endif
2270
2271 /*
2272 =====================
2273 SV_WalkMove
2274
2275 Only used by players
2276 ======================
2277 */
2278 static void SV_WalkMove (prvm_edict_t *ent)
2279 {
2280         prvm_prog_t *prog = SVVM_prog;
2281         int clip;
2282         int oldonground;
2283         //int originalmove_clip;
2284         int originalmove_flags;
2285         int originalmove_groundentity;
2286         int hitsupercontentsmask;
2287         int type;
2288         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2289         trace_t downtrace, trace;
2290         qboolean applygravity;
2291
2292         // if frametime is 0 (due to client sending the same timestamp twice),
2293         // don't move
2294         if (sv.frametime <= 0)
2295                 return;
2296
2297         if (sv_gameplayfix_unstickplayers.integer)
2298                 SV_CheckStuck (ent);
2299
2300         applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2301
2302         hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2303
2304         SV_CheckVelocity(ent);
2305
2306         // do a regular slide move unless it looks like you ran into a step
2307         oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2308
2309         VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2310         VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2311
2312         clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2313
2314         if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2315         if(!(clip & 1))
2316         {
2317                 // only try this if there was no floor in the way in the trace (no,
2318                 // this check seems to be not REALLY necessary, because if clip & 1,
2319                 // our trace will hit that thing too)
2320                 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2321                 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2322                 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2323                         type = MOVE_MISSILE;
2324                 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2325                         type = MOVE_WORLDONLY;
2326                 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2327                         type = MOVE_NOMONSTERS; // only clip against bmodels
2328                 else
2329                         type = MOVE_NORMAL;
2330                 trace = SV_TraceBox(upmove, PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2331                 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2332                         clip |= 1; // but we HAVE found a floor
2333         }
2334
2335         // if the move did not hit the ground at any point, we're not on ground
2336         if(!(clip & 1))
2337                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2338
2339         SV_CheckVelocity(ent);
2340         SV_LinkEdict(ent);
2341         SV_LinkEdict_TouchAreaGrid(ent);
2342
2343         if(clip & 8) // teleport
2344                 return;
2345
2346         if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2347                 return;
2348
2349         if (sv_nostep.integer)
2350                 return;
2351
2352         VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2353         VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2354         //originalmove_clip = clip;
2355         originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2356         originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2357
2358         // if move didn't block on a step, return
2359         if (clip & 2)
2360         {
2361                 // if move was not trying to move into the step, return
2362                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2363                         return;
2364
2365                 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2366                 {
2367                         // return if gibbed by a trigger
2368                         if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2369                                 return;
2370
2371                         // only step up while jumping if that is enabled
2372                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2373                                 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2374                                         return;
2375                 }
2376
2377                 // try moving up and forward to go up a step
2378                 // back to start pos
2379                 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2380                 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2381
2382                 // move up
2383                 VectorClear (upmove);
2384                 upmove[2] = sv_stepheight.value;
2385                 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2386                 {
2387                         // we got teleported when upstepping... must abort the move
2388                         return;
2389                 }
2390
2391                 // move forward
2392                 PRVM_serveredictvector(ent, velocity)[2] = 0;
2393                 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2394                 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2395                 if(clip & 8)
2396                 {
2397                         // we got teleported when upstepping... must abort the move
2398                         // note that z velocity handling may not be what QC expects here, but we cannot help it
2399                         return;
2400                 }
2401
2402                 SV_CheckVelocity(ent);
2403                 SV_LinkEdict(ent);
2404                 SV_LinkEdict_TouchAreaGrid(ent);
2405
2406                 // check for stuckness, possibly due to the limited precision of floats
2407                 // in the clipping hulls
2408                 if (clip
2409                  && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2410                  && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2411                 {
2412                         //Con_Printf("wall\n");
2413                         // stepping up didn't make any progress, revert to original move
2414                         VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2415                         VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2416                         //clip = originalmove_clip;
2417                         PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2418                         PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2419                         // now try to unstick if needed
2420                         //clip = SV_TryUnstick (ent, oldvel);
2421                         return;
2422                 }
2423
2424                 //Con_Printf("step - ");
2425
2426                 // extra friction based on view angle
2427                 if (clip & 2 && sv_wallfriction.integer)
2428                         SV_WallFriction (ent, stepnormal);
2429         }
2430         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2431         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))
2432                 return;
2433
2434         // move down
2435         VectorClear (downmove);
2436         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2437         if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2438         {
2439                 // we got teleported when downstepping... must abort the move
2440                 return;
2441         }
2442
2443         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2444         {
2445                 // this has been disabled so that you can't jump when you are stepping
2446                 // up while already jumping (also known as the Quake2 double jump bug)
2447 #if 0
2448                 // LordHavoc: disabled this check so you can walk on monsters/players
2449                 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2450                 {
2451                         //Con_Printf("onground\n");
2452                         PRVM_serveredictfloat(ent, flags) =     (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2453                         PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2454                 }
2455 #endif
2456         }
2457         else
2458         {
2459                 //Con_Printf("slope\n");
2460                 // if the push down didn't end up on good ground, use the move without
2461                 // the step up.  This happens near wall / slope combinations, and can
2462                 // cause the player to hop up higher on a slope too steep to climb
2463                 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2464                 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2465                 //clip = originalmove_clip;
2466                 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2467                 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2468         }
2469
2470         SV_CheckVelocity(ent);
2471         SV_LinkEdict(ent);
2472         SV_LinkEdict_TouchAreaGrid(ent);
2473 }
2474
2475 //============================================================================
2476
2477 /*
2478 =============
2479 SV_Physics_Follow
2480
2481 Entities that are "stuck" to another entity
2482 =============
2483 */
2484 static void SV_Physics_Follow (prvm_edict_t *ent)
2485 {
2486         prvm_prog_t *prog = SVVM_prog;
2487         vec3_t vf, vr, vu, angles, v;
2488         prvm_edict_t *e;
2489
2490         // regular thinking
2491         if (!SV_RunThink (ent))
2492                 return;
2493
2494         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2495         e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2496         if (PRVM_serveredictvector(e, angles)[0] == PRVM_serveredictvector(ent, punchangle)[0] && PRVM_serveredictvector(e, angles)[1] == PRVM_serveredictvector(ent, punchangle)[1] && PRVM_serveredictvector(e, angles)[2] == PRVM_serveredictvector(ent, punchangle)[2])
2497         {
2498                 // quick case for no rotation
2499                 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2500         }
2501         else
2502         {
2503                 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2504                 angles[1] =  PRVM_serveredictvector(ent, punchangle)[1];
2505                 angles[2] =  PRVM_serveredictvector(ent, punchangle)[2];
2506                 AngleVectors (angles, vf, vr, vu);
2507                 v[0] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[0] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[0] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[0];
2508                 v[1] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[1] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[1] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[1];
2509                 v[2] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[2] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[2] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[2];
2510                 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2511                 angles[1] =  PRVM_serveredictvector(e, angles)[1];
2512                 angles[2] =  PRVM_serveredictvector(e, angles)[2];
2513                 AngleVectors (angles, vf, vr, vu);
2514                 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2515                 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2516                 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2517         }
2518         VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2519         SV_LinkEdict(ent);
2520         //SV_LinkEdict_TouchAreaGrid(ent);
2521 }
2522
2523 /*
2524 ==============================================================================
2525
2526 TOSS / BOUNCE
2527
2528 ==============================================================================
2529 */
2530
2531 /*
2532 =============
2533 SV_CheckWaterTransition
2534
2535 =============
2536 */
2537 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2538 {
2539         prvm_prog_t *prog = SVVM_prog;
2540         // 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
2541         int cont;
2542         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(PRVM_serveredictvector(ent, origin)));
2543         if (!PRVM_serveredictfloat(ent, watertype))
2544         {
2545                 // just spawned here
2546                 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2547                 {
2548                         PRVM_serveredictfloat(ent, watertype) = cont;
2549                         PRVM_serveredictfloat(ent, waterlevel) = 1;
2550                         return;
2551                 }
2552         }
2553         // DRESK - Support for Entity Contents Transition Event
2554         // NOTE: Call here BEFORE updating the watertype below,
2555         // and suppress watersplash sound if a valid function
2556         // call was made to allow for custom "splash" sounds.
2557         else if( !SV_CheckContentsTransition(ent, cont) )
2558         { // Contents Transition Function Invalid; Potentially Play Water Sound
2559                 // check if the entity crossed into or out of water
2560                 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2561                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2562         }
2563
2564         if (cont <= CONTENTS_WATER)
2565         {
2566                 PRVM_serveredictfloat(ent, watertype) = cont;
2567                 PRVM_serveredictfloat(ent, waterlevel) = 1;
2568         }
2569         else
2570         {
2571                 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2572                 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2573         }
2574 }
2575
2576 /*
2577 =============
2578 SV_Physics_Toss
2579
2580 Toss, bounce, and fly movement.  When onground, do nothing.
2581 =============
2582 */
2583
2584 void SV_Physics_Toss (prvm_edict_t *ent)
2585 {
2586         prvm_prog_t *prog = SVVM_prog;
2587         trace_t trace;
2588         vec3_t move;
2589         vec_t movetime;
2590         int bump;
2591         prvm_edict_t *groundentity;
2592
2593 // if onground, return without moving
2594         if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2595         {
2596                 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2597                 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2598                 {
2599                         // don't stick to ground if onground and moving upward
2600                         PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2601                 }
2602                 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2603                 {
2604                         // we can trust FL_ONGROUND if groundentity is world because it never moves
2605                         return;
2606                 }
2607                 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2608                 {
2609                         // if ent was supported by a brush model on previous frame,
2610                         // and groundentity is now freed, set groundentity to 0 (world)
2611                         // which leaves it suspended in the air
2612                         PRVM_serveredictedict(ent, groundentity) = 0;
2613                         if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2614                                 return;
2615                 }
2616                 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2617                 {
2618                         // don't slide if still touching the groundentity
2619                         return;
2620                 }
2621         }
2622         ent->priv.server->suspendedinairflag = false;
2623
2624         SV_CheckVelocity (ent);
2625
2626 // add gravity
2627         if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2628                 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2629
2630 // move angles
2631         VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2632
2633         movetime = sv.frametime;
2634         for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2635         {
2636         // move origin
2637                 VectorScale (PRVM_serveredictvector(ent, velocity), movetime, move);
2638                 if(!SV_PushEntity (&trace, ent, move, true, true))
2639                         return; // teleported
2640                 if (ent->priv.server->free)
2641                         return;
2642                 if (trace.bmodelstartsolid)
2643                 {
2644                         // try to unstick the entity
2645                         if (sv_gameplayfix_unstickentities.integer)
2646                                 SV_UnstickEntity(ent);
2647                         if(!SV_PushEntity (&trace, ent, move, false, true))
2648                                 return; // teleported
2649                         if (ent->priv.server->free)
2650                                 return;
2651                 }
2652                 if (trace.fraction == 1)
2653                         break;
2654                 movetime *= 1 - min(1, trace.fraction);
2655                 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCEMISSILE)
2656                 {
2657                         float bouncefactor;
2658                         bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2659                         if (!bouncefactor)
2660                                 bouncefactor = 1.0f;
2661
2662                         ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2663                         PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2664                 }
2665                 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2666                 {
2667                         float d, ent_gravity;
2668                         float bouncefactor;
2669                         float bouncestop;
2670
2671                         bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2672                         if (!bouncefactor)
2673                                 bouncefactor = 0.5f;
2674
2675                         bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2676                         if (!bouncestop)
2677                                 bouncestop = 60.0f / 800.0f;
2678
2679                         ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2680                         ent_gravity = PRVM_serveredictfloat(ent, gravity);
2681                         if (!ent_gravity)
2682                                 ent_gravity = 1.0f;
2683                         // LordHavoc: fixed grenades not bouncing when fired down a slope
2684                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
2685                         {
2686                                 d = DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity));
2687                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2688                                 {
2689                                         PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2690                                         PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2691                                         VectorClear (PRVM_serveredictvector(ent, velocity));
2692                                         VectorClear (PRVM_serveredictvector(ent, avelocity));
2693                                 }
2694                                 else
2695                                         PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2696                         }
2697                         else
2698                         {
2699                                 if (trace.plane.normal[2] > 0.7 && PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * bouncestop * ent_gravity)
2700                                 {
2701                                         PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2702                                         PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2703                                         VectorClear (PRVM_serveredictvector(ent, velocity));
2704                                         VectorClear (PRVM_serveredictvector(ent, avelocity));
2705                                 }
2706                                 else
2707                                         PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2708                         }
2709                 }
2710                 else
2711                 {
2712                         ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2713                         if (trace.plane.normal[2] > 0.7)
2714                         {
2715                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2716                                 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2717                                 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2718                                         ent->priv.server->suspendedinairflag = true;
2719                                 VectorClear (PRVM_serveredictvector(ent, velocity));
2720                                 VectorClear (PRVM_serveredictvector(ent, avelocity));
2721                         }
2722                         else
2723                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2724                 }
2725                 if (!sv_gameplayfix_slidemoveprojectiles.integer || (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_BOUNCE && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCEMISSILE) || ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
2726                         break;
2727         }
2728
2729 // check for in water
2730         SV_CheckWaterTransition (ent);
2731 }
2732
2733 /*
2734 ===============================================================================
2735
2736 STEPPING MOVEMENT
2737
2738 ===============================================================================
2739 */
2740
2741 /*
2742 =============
2743 SV_Physics_Step
2744
2745 Monsters freefall when they don't have a ground entity, otherwise
2746 all movement is done with discrete steps.
2747
2748 This is also used for objects that have become still on the ground, but
2749 will fall if the floor is pulled out from under them.
2750 =============
2751 */
2752 static void SV_Physics_Step (prvm_edict_t *ent)
2753 {
2754         prvm_prog_t *prog = SVVM_prog;
2755         int flags = (int)PRVM_serveredictfloat(ent, flags);
2756
2757         // DRESK
2758         // Backup Velocity in the event that movetypesteplandevent is called,
2759         // to provide a parameter with the entity's velocity at impact.
2760         vec3_t backupVelocity;
2761         VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2762         // don't fall at all if fly/swim
2763         if (!(flags & (FL_FLY | FL_SWIM)))
2764         {
2765                 if (flags & FL_ONGROUND)
2766                 {
2767                         // freefall if onground and moving upward
2768                         // freefall if not standing on a world surface (it may be a lift or trap door)
2769                         if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2770                         {
2771                                 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2772                                 SV_CheckVelocity(ent);
2773                                 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2774                                 SV_LinkEdict(ent);
2775                                 SV_LinkEdict_TouchAreaGrid(ent);
2776                                 ent->priv.server->waterposition_forceupdate = true;
2777                         }
2778                 }
2779                 else
2780                 {
2781                         // freefall if not onground
2782                         int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2783
2784                         SV_CheckVelocity(ent);
2785                         SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2786                         SV_LinkEdict(ent);
2787                         SV_LinkEdict_TouchAreaGrid(ent);
2788
2789                         // just hit ground
2790                         if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2791                         {
2792                                 // DRESK - Check for Entity Land Event Function
2793                                 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2794                                 { // Valid Function; Execute
2795                                         // Prepare Parameters
2796                                         // Assign Velocity at Impact
2797                                         PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2798                                         PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2799                                         PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2800                                         // Assign Self
2801                                         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2802                                         // Set Time
2803                                         PRVM_serverglobalfloat(time) = sv.time;
2804                                         // Execute VM Function
2805                                         prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2806                                 }
2807                                 else
2808                                 // Check for Engine Landing Sound
2809                                 if(sv_sound_land.string)
2810                                         SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2811                         }
2812                         ent->priv.server->waterposition_forceupdate = true;
2813                 }
2814         }
2815
2816 // regular thinking
2817         if (!SV_RunThink(ent))
2818                 return;
2819
2820         if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2821         {
2822                 ent->priv.server->waterposition_forceupdate = false;
2823                 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2824                 SV_CheckWaterTransition(ent);
2825         }
2826 }
2827
2828 //============================================================================
2829
2830 static void SV_Physics_Entity (prvm_edict_t *ent)
2831 {
2832         prvm_prog_t *prog = SVVM_prog;
2833         // don't run think/move on newly spawned projectiles as it messes up
2834         // movement interpolation and rocket trails, and is inconsistent with
2835         // respect to entities spawned in the same frame
2836         // (if an ent spawns a higher numbered ent, it moves in the same frame,
2837         //  but if it spawns a lower numbered ent, it doesn't - this never moves
2838         //  ents in the first frame regardless)
2839         qboolean runmove = ent->priv.server->move;
2840         ent->priv.server->move = true;
2841         if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2842                 return;
2843         switch ((int) PRVM_serveredictfloat(ent, movetype))
2844         {
2845         case MOVETYPE_PUSH:
2846         case MOVETYPE_FAKEPUSH:
2847                 SV_Physics_Pusher (ent);
2848                 break;
2849         case MOVETYPE_NONE:
2850                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2851                 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2852                         SV_RunThink (ent);
2853                 break;
2854         case MOVETYPE_FOLLOW:
2855                 SV_Physics_Follow (ent);
2856                 break;
2857         case MOVETYPE_NOCLIP:
2858                 if (SV_RunThink(ent))
2859                 {
2860                         SV_CheckWater(ent);
2861                         VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2862                         VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2863                 }
2864                 SV_LinkEdict(ent);
2865                 break;
2866         case MOVETYPE_STEP:
2867                 SV_Physics_Step (ent);
2868                 break;
2869         case MOVETYPE_WALK:
2870                 if (SV_RunThink (ent))
2871                         SV_WalkMove (ent);
2872                 break;
2873         case MOVETYPE_TOSS:
2874         case MOVETYPE_BOUNCE:
2875         case MOVETYPE_BOUNCEMISSILE:
2876         case MOVETYPE_FLYMISSILE:
2877         case MOVETYPE_FLY:
2878         case MOVETYPE_FLY_WORLDONLY:
2879                 // regular thinking
2880                 if (SV_RunThink (ent))
2881                         SV_Physics_Toss (ent);
2882                 break;
2883         case MOVETYPE_PHYSICS:
2884                 if (SV_RunThink(ent))
2885                 {
2886                         SV_LinkEdict(ent);
2887                         SV_LinkEdict_TouchAreaGrid(ent);
2888                 }
2889                 break;
2890         default:
2891                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2892                 break;
2893         }
2894 }
2895
2896 void SV_Physics_ClientMove(void)
2897 {
2898         prvm_prog_t *prog = SVVM_prog;
2899         prvm_edict_t *ent;
2900         ent = host_client->edict;
2901
2902         // call player physics, this needs the proper frametime
2903         PRVM_serverglobalfloat(frametime) = sv.frametime;
2904         SV_ClientThink();
2905
2906         // call standard client pre-think, with frametime = 0
2907         PRVM_serverglobalfloat(time) = sv.time;
2908         PRVM_serverglobalfloat(frametime) = 0;
2909         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2910         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2911         PRVM_serverglobalfloat(frametime) = sv.frametime;
2912
2913         // make sure the velocity is sane (not a NaN)
2914         SV_CheckVelocity(ent);
2915
2916         // perform MOVETYPE_WALK behavior
2917         SV_WalkMove (ent);
2918
2919         // call standard player post-think, with frametime = 0
2920         PRVM_serverglobalfloat(time) = sv.time;
2921         PRVM_serverglobalfloat(frametime) = 0;
2922         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2923         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2924         PRVM_serverglobalfloat(frametime) = sv.frametime;
2925
2926         if(PRVM_serveredictfloat(ent, fixangle))
2927         {
2928                 // angle fixing was requested by physics code...
2929                 // so store the current angles for later use
2930                 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2931                 host_client->fixangle_angles_set = TRUE;
2932
2933                 // and clear fixangle for the next frame
2934                 PRVM_serveredictfloat(ent, fixangle) = 0;
2935         }
2936 }
2937
2938 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2939 {
2940         prvm_prog_t *prog = SVVM_prog;
2941         // don't do physics on disconnected clients, FrikBot relies on this
2942         if (!host_client->spawned)
2943                 return;
2944
2945         // make sure the velocity is sane (not a NaN)
2946         SV_CheckVelocity(ent);
2947
2948         // don't run physics here if running asynchronously
2949         if (host_client->clmovement_inputtimeout <= 0)
2950         {
2951                 SV_ClientThink();
2952                 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2953         }
2954
2955         // make sure the velocity is still sane (not a NaN)
2956         SV_CheckVelocity(ent);
2957
2958         // call standard client pre-think
2959         PRVM_serverglobalfloat(time) = sv.time;
2960         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2961         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2962
2963         // make sure the velocity is still sane (not a NaN)
2964         SV_CheckVelocity(ent);
2965 }
2966
2967 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2968 {
2969         prvm_prog_t *prog = SVVM_prog;
2970         // don't do physics on disconnected clients, FrikBot relies on this
2971         if (!host_client->spawned)
2972                 return;
2973
2974         // make sure the velocity is sane (not a NaN)
2975         SV_CheckVelocity(ent);
2976
2977         // call standard player post-think
2978         PRVM_serverglobalfloat(time) = sv.time;
2979         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2980         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2981
2982         // make sure the velocity is still sane (not a NaN)
2983         SV_CheckVelocity(ent);
2984
2985         if(PRVM_serveredictfloat(ent, fixangle))
2986         {
2987                 // angle fixing was requested by physics code...
2988                 // so store the current angles for later use
2989                 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2990                 host_client->fixangle_angles_set = TRUE;
2991
2992                 // and clear fixangle for the next frame
2993                 PRVM_serveredictfloat(ent, fixangle) = 0;
2994         }
2995
2996         // decrement the countdown variable used to decide when to go back to
2997         // synchronous physics
2998         if (host_client->clmovement_inputtimeout > sv.frametime)
2999                 host_client->clmovement_inputtimeout -= sv.frametime;
3000         else
3001                 host_client->clmovement_inputtimeout = 0;
3002 }
3003
3004 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3005 {
3006         prvm_prog_t *prog = SVVM_prog;
3007         // don't do physics on disconnected clients, FrikBot relies on this
3008         if (!host_client->spawned)
3009         {
3010                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3011                 return;
3012         }
3013
3014         // make sure the velocity is sane (not a NaN)
3015         SV_CheckVelocity(ent);
3016
3017         switch ((int) PRVM_serveredictfloat(ent, movetype))
3018         {
3019         case MOVETYPE_PUSH:
3020         case MOVETYPE_FAKEPUSH:
3021                 SV_Physics_Pusher (ent);
3022                 break;
3023         case MOVETYPE_NONE:
3024                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3025                 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3026                         SV_RunThink (ent);
3027                 break;
3028         case MOVETYPE_FOLLOW:
3029                 SV_Physics_Follow (ent);
3030                 break;
3031         case MOVETYPE_NOCLIP:
3032                 SV_RunThink(ent);
3033                 SV_CheckWater(ent);
3034                 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3035                 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3036                 break;
3037         case MOVETYPE_STEP:
3038                 SV_Physics_Step (ent);
3039                 break;
3040         case MOVETYPE_WALK:
3041                 SV_RunThink (ent);
3042                 // don't run physics here if running asynchronously
3043                 if (host_client->clmovement_inputtimeout <= 0)
3044                         SV_WalkMove (ent);
3045                 break;
3046         case MOVETYPE_TOSS:
3047         case MOVETYPE_BOUNCE:
3048         case MOVETYPE_BOUNCEMISSILE:
3049         case MOVETYPE_FLYMISSILE:
3050                 // regular thinking
3051                 SV_RunThink (ent);
3052                 SV_Physics_Toss (ent);
3053                 break;
3054         case MOVETYPE_FLY:
3055         case MOVETYPE_FLY_WORLDONLY:
3056                 SV_RunThink (ent);
3057                 SV_WalkMove (ent);
3058                 break;
3059         case MOVETYPE_PHYSICS:
3060                 SV_RunThink (ent);
3061                 break;
3062         default:
3063                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3064                 break;
3065         }
3066
3067         SV_CheckVelocity (ent);
3068
3069         SV_LinkEdict(ent);
3070         SV_LinkEdict_TouchAreaGrid(ent);
3071
3072         SV_CheckVelocity (ent);
3073 }
3074
3075 /*
3076 ================
3077 SV_Physics
3078
3079 ================
3080 */
3081 void SV_Physics (void)
3082 {
3083         prvm_prog_t *prog = SVVM_prog;
3084         int i;
3085         prvm_edict_t *ent;
3086
3087 // let the progs know that a new frame has started
3088         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3089         PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3090         PRVM_serverglobalfloat(time) = sv.time;
3091         PRVM_serverglobalfloat(frametime) = sv.frametime;
3092         prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3093
3094         // run physics engine
3095         World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3096
3097 //
3098 // treat each object in turn
3099 //
3100
3101         // if force_retouch, relink all the entities
3102         if (PRVM_serverglobalfloat(force_retouch) > 0)
3103                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3104                         if (!ent->priv.server->free)
3105                                 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3106
3107         if (sv_gameplayfix_consistentplayerprethink.integer)
3108         {
3109                 // run physics on the client entities in 3 stages
3110                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3111                         if (!ent->priv.server->free)
3112                                 SV_Physics_ClientEntity_PreThink(ent);
3113
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(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_PostThink(ent);
3121         }
3122         else
3123         {
3124                 // run physics on the client entities
3125                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3126                 {
3127                         if (!ent->priv.server->free)
3128                         {
3129                                 SV_Physics_ClientEntity_PreThink(ent);
3130                                 SV_Physics_ClientEntity(ent);
3131                                 SV_Physics_ClientEntity_PostThink(ent);
3132                         }
3133                 }
3134         }
3135
3136         // run physics on all the non-client entities
3137         if (!sv_freezenonclients.integer)
3138         {
3139                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3140                         if (!ent->priv.server->free)
3141                                 SV_Physics_Entity(ent);
3142                 // make a second pass to see if any ents spawned this frame and make
3143                 // sure they run their move/think
3144                 if (sv_gameplayfix_delayprojectiles.integer < 0)
3145                         for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3146                                 if (!ent->priv.server->move && !ent->priv.server->free)
3147                                         SV_Physics_Entity(ent);
3148         }
3149
3150         if (PRVM_serverglobalfloat(force_retouch) > 0)
3151                 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3152
3153         // LordHavoc: endframe support
3154         if (PRVM_serverfunction(EndFrame))
3155         {
3156                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3157                 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3158                 PRVM_serverglobalfloat(time) = sv.time;
3159                 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3160         }
3161
3162         // decrement prog->num_edicts if the highest number entities died
3163         for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3164
3165         if (!sv_freezenonclients.integer)
3166                 sv.time += sv.frametime;
3167 }