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