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