]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
undo the SUPERCONTENTS_SKY change, it should not be treated as solid,
[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         float d, ent_gravity;
2593         float bouncefactor;
2594         float bouncestop;
2595
2596 // if onground, return without moving
2597         if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2598         {
2599                 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2600                 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2601                 {
2602                         // don't stick to ground if onground and moving upward
2603                         PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2604                 }
2605                 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2606                 {
2607                         // we can trust FL_ONGROUND if groundentity is world because it never moves
2608                         return;
2609                 }
2610                 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2611                 {
2612                         // if ent was supported by a brush model on previous frame,
2613                         // and groundentity is now freed, set groundentity to 0 (world)
2614                         // which leaves it suspended in the air
2615                         PRVM_serveredictedict(ent, groundentity) = 0;
2616                         if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2617                                 return;
2618                 }
2619                 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2620                 {
2621                         // don't slide if still touching the groundentity
2622                         return;
2623                 }
2624         }
2625         ent->priv.server->suspendedinairflag = false;
2626
2627         SV_CheckVelocity (ent);
2628
2629 // add gravity
2630         if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2631                 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2632
2633 // move angles
2634         VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2635
2636         movetime = sv.frametime;
2637         for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2638         {
2639         // move origin
2640                 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2641                 if(!SV_PushEntity(&trace, ent, move, true, true))
2642                         return; // teleported
2643                 if (ent->priv.server->free)
2644                         return;
2645                 if (trace.bmodelstartsolid)
2646                 {
2647                         // try to unstick the entity
2648                         if (sv_gameplayfix_unstickentities.integer)
2649                                 SV_UnstickEntity(ent);
2650                         if(!SV_PushEntity(&trace, ent, move, false, true))
2651                                 return; // teleported
2652                         if (ent->priv.server->free)
2653                                 return;
2654                 }
2655                 if (trace.fraction == 1)
2656                         break;
2657                 movetime *= 1 - min(1, trace.fraction);
2658                 switch((int)PRVM_serveredictfloat(ent, movetype))
2659                 {
2660                 case MOVETYPE_BOUNCEMISSILE:
2661                         bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2662                         if (!bouncefactor)
2663                                 bouncefactor = 1.0f;
2664
2665                         ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2666                         PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2667                         if (!sv_gameplayfix_slidemoveprojectiles.integer)
2668                                 movetime = 0;
2669                         break;
2670                 case MOVETYPE_BOUNCE:
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                                 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2686                         else
2687                                 d = PRVM_serveredictvector(ent, velocity)[2];
2688                         if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2689                         {
2690                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2691                                 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2692                                 VectorClear(PRVM_serveredictvector(ent, velocity));
2693                                 VectorClear(PRVM_serveredictvector(ent, avelocity));
2694                                 movetime = 0;
2695                         }
2696                         else
2697                         {
2698                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2699                                 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2700                                         movetime = 0;
2701                         }
2702                         break;
2703                 default:
2704                         ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2705                         if (trace.plane.normal[2] > 0.7)
2706                         {
2707                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2708                                 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2709                                 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2710                                         ent->priv.server->suspendedinairflag = true;
2711                                 VectorClear (PRVM_serveredictvector(ent, velocity));
2712                                 VectorClear (PRVM_serveredictvector(ent, avelocity));
2713                         }
2714                         else
2715                                 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2716                         movetime = 0;
2717                         break;
2718                 }
2719         }
2720
2721 // check for in water
2722         SV_CheckWaterTransition (ent);
2723 }
2724
2725 /*
2726 ===============================================================================
2727
2728 STEPPING MOVEMENT
2729
2730 ===============================================================================
2731 */
2732
2733 /*
2734 =============
2735 SV_Physics_Step
2736
2737 Monsters freefall when they don't have a ground entity, otherwise
2738 all movement is done with discrete steps.
2739
2740 This is also used for objects that have become still on the ground, but
2741 will fall if the floor is pulled out from under them.
2742 =============
2743 */
2744 static void SV_Physics_Step (prvm_edict_t *ent)
2745 {
2746         prvm_prog_t *prog = SVVM_prog;
2747         int flags = (int)PRVM_serveredictfloat(ent, flags);
2748
2749         // DRESK
2750         // Backup Velocity in the event that movetypesteplandevent is called,
2751         // to provide a parameter with the entity's velocity at impact.
2752         vec3_t backupVelocity;
2753         VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2754         // don't fall at all if fly/swim
2755         if (!(flags & (FL_FLY | FL_SWIM)))
2756         {
2757                 if (flags & FL_ONGROUND)
2758                 {
2759                         // freefall if onground and moving upward
2760                         // freefall if not standing on a world surface (it may be a lift or trap door)
2761                         if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2762                         {
2763                                 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2764                                 SV_CheckVelocity(ent);
2765                                 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2766                                 SV_LinkEdict(ent);
2767                                 SV_LinkEdict_TouchAreaGrid(ent);
2768                                 ent->priv.server->waterposition_forceupdate = true;
2769                         }
2770                 }
2771                 else
2772                 {
2773                         // freefall if not onground
2774                         int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2775
2776                         SV_CheckVelocity(ent);
2777                         SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2778                         SV_LinkEdict(ent);
2779                         SV_LinkEdict_TouchAreaGrid(ent);
2780
2781                         // just hit ground
2782                         if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2783                         {
2784                                 // DRESK - Check for Entity Land Event Function
2785                                 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2786                                 { // Valid Function; Execute
2787                                         // Prepare Parameters
2788                                         // Assign Velocity at Impact
2789                                         PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2790                                         PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2791                                         PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2792                                         // Assign Self
2793                                         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2794                                         // Set Time
2795                                         PRVM_serverglobalfloat(time) = sv.time;
2796                                         // Execute VM Function
2797                                         prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2798                                 }
2799                                 else
2800                                 // Check for Engine Landing Sound
2801                                 if(sv_sound_land.string)
2802                                         SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2803                         }
2804                         ent->priv.server->waterposition_forceupdate = true;
2805                 }
2806         }
2807
2808 // regular thinking
2809         if (!SV_RunThink(ent))
2810                 return;
2811
2812         if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2813         {
2814                 ent->priv.server->waterposition_forceupdate = false;
2815                 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2816                 SV_CheckWaterTransition(ent);
2817         }
2818 }
2819
2820 //============================================================================
2821
2822 static void SV_Physics_Entity (prvm_edict_t *ent)
2823 {
2824         prvm_prog_t *prog = SVVM_prog;
2825         // don't run think/move on newly spawned projectiles as it messes up
2826         // movement interpolation and rocket trails, and is inconsistent with
2827         // respect to entities spawned in the same frame
2828         // (if an ent spawns a higher numbered ent, it moves in the same frame,
2829         //  but if it spawns a lower numbered ent, it doesn't - this never moves
2830         //  ents in the first frame regardless)
2831         qboolean runmove = ent->priv.server->move;
2832         ent->priv.server->move = true;
2833         if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2834                 return;
2835         switch ((int) PRVM_serveredictfloat(ent, movetype))
2836         {
2837         case MOVETYPE_PUSH:
2838         case MOVETYPE_FAKEPUSH:
2839                 SV_Physics_Pusher (ent);
2840                 break;
2841         case MOVETYPE_NONE:
2842                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2843                 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2844                         SV_RunThink (ent);
2845                 break;
2846         case MOVETYPE_FOLLOW:
2847                 SV_Physics_Follow (ent);
2848                 break;
2849         case MOVETYPE_NOCLIP:
2850                 if (SV_RunThink(ent))
2851                 {
2852                         SV_CheckWater(ent);
2853                         VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2854                         VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2855                 }
2856                 SV_LinkEdict(ent);
2857                 break;
2858         case MOVETYPE_STEP:
2859                 SV_Physics_Step (ent);
2860                 break;
2861         case MOVETYPE_WALK:
2862                 if (SV_RunThink (ent))
2863                         SV_WalkMove (ent);
2864                 break;
2865         case MOVETYPE_TOSS:
2866         case MOVETYPE_BOUNCE:
2867         case MOVETYPE_BOUNCEMISSILE:
2868         case MOVETYPE_FLYMISSILE:
2869         case MOVETYPE_FLY:
2870         case MOVETYPE_FLY_WORLDONLY:
2871                 // regular thinking
2872                 if (SV_RunThink (ent))
2873                         SV_Physics_Toss (ent);
2874                 break;
2875         case MOVETYPE_PHYSICS:
2876                 if (SV_RunThink(ent))
2877                 {
2878                         SV_LinkEdict(ent);
2879                         SV_LinkEdict_TouchAreaGrid(ent);
2880                 }
2881                 break;
2882         default:
2883                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2884                 break;
2885         }
2886 }
2887
2888 void SV_Physics_ClientMove(void)
2889 {
2890         prvm_prog_t *prog = SVVM_prog;
2891         prvm_edict_t *ent;
2892         ent = host_client->edict;
2893
2894         // call player physics, this needs the proper frametime
2895         PRVM_serverglobalfloat(frametime) = sv.frametime;
2896         SV_ClientThink();
2897
2898         // call standard client pre-think, with frametime = 0
2899         PRVM_serverglobalfloat(time) = sv.time;
2900         PRVM_serverglobalfloat(frametime) = 0;
2901         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2902         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2903         PRVM_serverglobalfloat(frametime) = sv.frametime;
2904
2905         // make sure the velocity is sane (not a NaN)
2906         SV_CheckVelocity(ent);
2907
2908         // perform MOVETYPE_WALK behavior
2909         SV_WalkMove (ent);
2910
2911         // call standard player post-think, with frametime = 0
2912         PRVM_serverglobalfloat(time) = sv.time;
2913         PRVM_serverglobalfloat(frametime) = 0;
2914         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2915         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2916         PRVM_serverglobalfloat(frametime) = sv.frametime;
2917
2918         if(PRVM_serveredictfloat(ent, fixangle))
2919         {
2920                 // angle fixing was requested by physics code...
2921                 // so store the current angles for later use
2922                 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2923                 host_client->fixangle_angles_set = TRUE;
2924
2925                 // and clear fixangle for the next frame
2926                 PRVM_serveredictfloat(ent, fixangle) = 0;
2927         }
2928 }
2929
2930 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2931 {
2932         prvm_prog_t *prog = SVVM_prog;
2933         // don't do physics on disconnected clients, FrikBot relies on this
2934         if (!host_client->spawned)
2935                 return;
2936
2937         // make sure the velocity is sane (not a NaN)
2938         SV_CheckVelocity(ent);
2939
2940         // don't run physics here if running asynchronously
2941         if (host_client->clmovement_inputtimeout <= 0)
2942         {
2943                 SV_ClientThink();
2944                 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2945         }
2946
2947         // make sure the velocity is still sane (not a NaN)
2948         SV_CheckVelocity(ent);
2949
2950         // call standard client pre-think
2951         PRVM_serverglobalfloat(time) = sv.time;
2952         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2953         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2954
2955         // make sure the velocity is still sane (not a NaN)
2956         SV_CheckVelocity(ent);
2957 }
2958
2959 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2960 {
2961         prvm_prog_t *prog = SVVM_prog;
2962         // don't do physics on disconnected clients, FrikBot relies on this
2963         if (!host_client->spawned)
2964                 return;
2965
2966         // make sure the velocity is sane (not a NaN)
2967         SV_CheckVelocity(ent);
2968
2969         // call standard player post-think
2970         PRVM_serverglobalfloat(time) = sv.time;
2971         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2972         prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2973
2974         // make sure the velocity is still sane (not a NaN)
2975         SV_CheckVelocity(ent);
2976
2977         if(PRVM_serveredictfloat(ent, fixangle))
2978         {
2979                 // angle fixing was requested by physics code...
2980                 // so store the current angles for later use
2981                 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2982                 host_client->fixangle_angles_set = TRUE;
2983
2984                 // and clear fixangle for the next frame
2985                 PRVM_serveredictfloat(ent, fixangle) = 0;
2986         }
2987
2988         // decrement the countdown variable used to decide when to go back to
2989         // synchronous physics
2990         if (host_client->clmovement_inputtimeout > sv.frametime)
2991                 host_client->clmovement_inputtimeout -= sv.frametime;
2992         else
2993                 host_client->clmovement_inputtimeout = 0;
2994 }
2995
2996 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2997 {
2998         prvm_prog_t *prog = SVVM_prog;
2999         // don't do physics on disconnected clients, FrikBot relies on this
3000         if (!host_client->spawned)
3001         {
3002                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3003                 return;
3004         }
3005
3006         // make sure the velocity is sane (not a NaN)
3007         SV_CheckVelocity(ent);
3008
3009         switch ((int) PRVM_serveredictfloat(ent, movetype))
3010         {
3011         case MOVETYPE_PUSH:
3012         case MOVETYPE_FAKEPUSH:
3013                 SV_Physics_Pusher (ent);
3014                 break;
3015         case MOVETYPE_NONE:
3016                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3017                 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3018                         SV_RunThink (ent);
3019                 break;
3020         case MOVETYPE_FOLLOW:
3021                 SV_Physics_Follow (ent);
3022                 break;
3023         case MOVETYPE_NOCLIP:
3024                 SV_RunThink(ent);
3025                 SV_CheckWater(ent);
3026                 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3027                 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3028                 break;
3029         case MOVETYPE_STEP:
3030                 SV_Physics_Step (ent);
3031                 break;
3032         case MOVETYPE_WALK:
3033                 SV_RunThink (ent);
3034                 // don't run physics here if running asynchronously
3035                 if (host_client->clmovement_inputtimeout <= 0)
3036                         SV_WalkMove (ent);
3037                 break;
3038         case MOVETYPE_TOSS:
3039         case MOVETYPE_BOUNCE:
3040         case MOVETYPE_BOUNCEMISSILE:
3041         case MOVETYPE_FLYMISSILE:
3042                 // regular thinking
3043                 SV_RunThink (ent);
3044                 SV_Physics_Toss (ent);
3045                 break;
3046         case MOVETYPE_FLY:
3047         case MOVETYPE_FLY_WORLDONLY:
3048                 SV_RunThink (ent);
3049                 SV_WalkMove (ent);
3050                 break;
3051         case MOVETYPE_PHYSICS:
3052                 SV_RunThink (ent);
3053                 break;
3054         default:
3055                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3056                 break;
3057         }
3058
3059         SV_CheckVelocity (ent);
3060
3061         SV_LinkEdict(ent);
3062         SV_LinkEdict_TouchAreaGrid(ent);
3063
3064         SV_CheckVelocity (ent);
3065 }
3066
3067 /*
3068 ================
3069 SV_Physics
3070
3071 ================
3072 */
3073 void SV_Physics (void)
3074 {
3075         prvm_prog_t *prog = SVVM_prog;
3076         int i;
3077         prvm_edict_t *ent;
3078
3079 // let the progs know that a new frame has started
3080         PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3081         PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3082         PRVM_serverglobalfloat(time) = sv.time;
3083         PRVM_serverglobalfloat(frametime) = sv.frametime;
3084         prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3085
3086         // run physics engine
3087         World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3088
3089 //
3090 // treat each object in turn
3091 //
3092
3093         // if force_retouch, relink all the entities
3094         if (PRVM_serverglobalfloat(force_retouch) > 0)
3095                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3096                         if (!ent->priv.server->free)
3097                                 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3098
3099         if (sv_gameplayfix_consistentplayerprethink.integer)
3100         {
3101                 // run physics on the client entities in 3 stages
3102                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3103                         if (!ent->priv.server->free)
3104                                 SV_Physics_ClientEntity_PreThink(ent);
3105
3106                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3107                         if (!ent->priv.server->free)
3108                                 SV_Physics_ClientEntity(ent);
3109
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_PostThink(ent);
3113         }
3114         else
3115         {
3116                 // run physics on the client entities
3117                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3118                 {
3119                         if (!ent->priv.server->free)
3120                         {
3121                                 SV_Physics_ClientEntity_PreThink(ent);
3122                                 SV_Physics_ClientEntity(ent);
3123                                 SV_Physics_ClientEntity_PostThink(ent);
3124                         }
3125                 }
3126         }
3127
3128         // run physics on all the non-client entities
3129         if (!sv_freezenonclients.integer)
3130         {
3131                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3132                         if (!ent->priv.server->free)
3133                                 SV_Physics_Entity(ent);
3134                 // make a second pass to see if any ents spawned this frame and make
3135                 // sure they run their move/think
3136                 if (sv_gameplayfix_delayprojectiles.integer < 0)
3137                         for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3138                                 if (!ent->priv.server->move && !ent->priv.server->free)
3139                                         SV_Physics_Entity(ent);
3140         }
3141
3142         if (PRVM_serverglobalfloat(force_retouch) > 0)
3143                 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3144
3145         // LordHavoc: endframe support
3146         if (PRVM_serverfunction(EndFrame))
3147         {
3148                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3149                 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3150                 PRVM_serverglobalfloat(time) = sv.time;
3151                 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3152         }
3153
3154         // decrement prog->num_edicts if the highest number entities died
3155         for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3156
3157         if (!sv_freezenonclients.integer)
3158                 sv.time += sv.frametime;
3159 }