]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
duplicated PRVM_ExecuteProgram to become MVM_ExecuteProgram,
[xonotic/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23
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: max velocity fix, inspired by Maddes's source fixes, but this is faster
1029         wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1030         if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1031         {
1032                 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1033                 ent->fields.server->velocity[0] *= wishspeed;
1034                 ent->fields.server->velocity[1] *= wishspeed;
1035                 ent->fields.server->velocity[2] *= wishspeed;
1036         }
1037 }
1038
1039 /*
1040 =============
1041 SV_RunThink
1042
1043 Runs thinking code if time.  There is some play in the exact time the think
1044 function will be called, because it is called before any movement is done
1045 in a frame.  Not used for pushmove objects, because they must be exact.
1046 Returns false if the entity removed itself.
1047 =============
1048 */
1049 qboolean SV_RunThink (prvm_edict_t *ent)
1050 {
1051         int iterations;
1052
1053         // don't let things stay in the past.
1054         // it is possible to start that way by a trigger with a local time.
1055         if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1056                 return true;
1057
1058         for (iterations = 0;iterations < 128  && !ent->priv.server->free;iterations++)
1059         {
1060                 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1061                 ent->fields.server->nextthink = 0;
1062                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1063                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1064                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1065                 // mods often set nextthink to time to cause a think every frame,
1066                 // we don't want to loop in that case, so exit if the new nextthink is
1067                 // <= the time the qc was told, also exit if it is past the end of the
1068                 // frame
1069                 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1070                         break;
1071         }
1072         return !ent->priv.server->free;
1073 }
1074
1075 /*
1076 ==================
1077 SV_Impact
1078
1079 Two entities have touched, so run their touch functions
1080 returns true if the impact kept the origin of the touching entity intact
1081 ==================
1082 */
1083 extern void VM_SetTraceGlobals(const trace_t *trace);
1084 extern sizebuf_t vm_tempstringsbuf;
1085 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1086 {
1087         int restorevm_tempstringsbuf_cursize;
1088         int old_self, old_other;
1089         vec3_t org;
1090         prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1091         prvm_eval_t *val;
1092
1093         old_self = prog->globals.server->self;
1094         old_other = prog->globals.server->other;
1095         restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1096
1097         VectorCopy(e1->fields.server->origin, org);
1098
1099         VM_SetTraceGlobals(trace);
1100
1101         prog->globals.server->time = sv.time;
1102         if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1103         {
1104                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1105                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1106                 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1107         }
1108
1109         if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1110         {
1111                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1112                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1113                 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1114                 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1115                 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1116                 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1117                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1118                         val->_float = 0;
1119                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1120                         val->_float = 0;
1121                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1122                         val->_float = 0;
1123                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1124                         val->string = 0;
1125                 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1126         }
1127
1128         prog->globals.server->self = old_self;
1129         prog->globals.server->other = old_other;
1130         vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1131
1132         return VectorCompare(e1->fields.server->origin, org);
1133 }
1134
1135
1136 /*
1137 ==================
1138 ClipVelocity
1139
1140 Slide off of the impacting object
1141 returns the blocked flags (1 = floor, 2 = step / wall)
1142 ==================
1143 */
1144 #define STOP_EPSILON 0.1
1145 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1146 {
1147         int i;
1148         float backoff;
1149
1150         backoff = -DotProduct (in, normal) * overbounce;
1151         VectorMA(in, backoff, normal, out);
1152
1153         for (i = 0;i < 3;i++)
1154                 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1155                         out[i] = 0;
1156 }
1157
1158
1159 /*
1160 ============
1161 SV_FlyMove
1162
1163 The basic solid body movement clip that slides along multiple planes
1164 Returns the clipflags if the velocity was modified (hit something solid)
1165 1 = floor
1166 2 = wall / step
1167 4 = dead stop
1168 8 = teleported by touch method
1169 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1170 ============
1171 */
1172 static float SV_Gravity (prvm_edict_t *ent);
1173 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1174 #define MAX_CLIP_PLANES 5
1175 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
1176 {
1177         int blocked, bumpcount;
1178         int i, j, numplanes;
1179         float d, time_left, gravity;
1180         vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1181 #if 0
1182         vec3_t end;
1183 #endif
1184         trace_t trace;
1185         if (time <= 0)
1186                 return 0;
1187         gravity = 0;
1188         if (applygravity)
1189         {
1190                 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1191                 {
1192                         gravity = SV_Gravity(ent) * 0.5f;
1193                         ent->fields.server->velocity[2] -= gravity;
1194                 }
1195                 else
1196                 {
1197                         applygravity = false;
1198                         ent->fields.server->velocity[2] -= SV_Gravity(ent);
1199                 }
1200         }
1201         blocked = 0;
1202         VectorCopy(ent->fields.server->velocity, original_velocity);
1203         VectorCopy(ent->fields.server->velocity, primal_velocity);
1204         numplanes = 0;
1205         time_left = time;
1206         for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1207         {
1208                 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1209                         break;
1210
1211                 VectorScale(ent->fields.server->velocity, time_left, push);
1212 #if 0
1213                 VectorAdd(ent->fields.server->origin, push, end);
1214 #endif
1215                 if(!SV_PushEntity(&trace, ent, push, false, false))
1216                 {
1217                         // we got teleported by a touch function
1218                         // let's abort the move
1219                         blocked |= 8;
1220                         break;
1221                 }
1222
1223 #if 0
1224                 //if (trace.fraction < 0.002)
1225                 {
1226 #if 1
1227                         vec3_t start;
1228                         trace_t testtrace;
1229                         VectorCopy(ent->fields.server->origin, start);
1230                         start[2] += 3;//0.03125;
1231                         VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1232                         end[2] += 3;//0.03125;
1233                         testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1234                         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)))
1235                         {
1236                                 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
1237                                 trace = testtrace;
1238                         }
1239 #endif
1240 #if 0
1241                         //j = -1;
1242                         for (i = 0;i < numplanes;i++)
1243                         {
1244                                 VectorCopy(ent->fields.server->origin, start);
1245                                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1246                                 VectorMA(start, 3, planes[i], start);
1247                                 VectorMA(end, 3, planes[i], end);
1248                                 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1249                                 if (trace.fraction < testtrace.fraction)
1250                                 {
1251                                         trace = testtrace;
1252                                         VectorCopy(start, ent->fields.server->origin);
1253                                         //j = i;
1254                                 }
1255                         }
1256                         //if (j >= 0)
1257                         //      VectorAdd(ent->fields.server->origin, planes[j], start);
1258 #endif
1259                 }
1260 #endif
1261
1262 #if 0
1263                 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);
1264                 if (trace.fraction < 1)
1265                         Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
1266                 Con_Print("\n");
1267 #endif
1268
1269 #if 0
1270                 if (trace.bmodelstartsolid)
1271                 {
1272                         // LordHavoc: note: this code is what makes entities stick in place
1273                         // if embedded in world only (you can walk through other objects if
1274                         // stuck)
1275                         // entity is trapped in another solid
1276                         VectorClear(ent->fields.server->velocity);
1277                         return 3;
1278                 }
1279 #endif
1280
1281                 if (trace.fraction == 1)
1282                         break;
1283                 if (trace.plane.normal[2])
1284                 {
1285                         if (trace.plane.normal[2] > 0.7)
1286                         {
1287                                 // floor
1288                                 blocked |= 1;
1289
1290                                 if (!trace.ent)
1291                                 {
1292                                         Con_Printf ("SV_FlyMove: !trace.ent");
1293                                         trace.ent = prog->edicts;
1294                                 }
1295
1296                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1297                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1298                         }
1299                 }
1300                 else
1301                 {
1302                         // step
1303                         blocked |= 2;
1304                         // save the trace for player extrafriction
1305                         if (stepnormal)
1306                                 VectorCopy(trace.plane.normal, stepnormal);
1307                 }
1308                 if (trace.fraction >= 0.001)
1309                 {
1310                         // actually covered some distance
1311                         VectorCopy(ent->fields.server->velocity, original_velocity);
1312                         numplanes = 0;
1313                 }
1314
1315                 time_left *= 1 - trace.fraction;
1316
1317                 // clipped to another plane
1318                 if (numplanes >= MAX_CLIP_PLANES)
1319                 {
1320                         // this shouldn't really happen
1321                         VectorClear(ent->fields.server->velocity);
1322                         blocked = 3;
1323                         break;
1324                 }
1325
1326                 /*
1327                 for (i = 0;i < numplanes;i++)
1328                         if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1329                                 break;
1330                 if (i < numplanes)
1331                 {
1332                         VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1333                         continue;
1334                 }
1335                 */
1336
1337                 VectorCopy(trace.plane.normal, planes[numplanes]);
1338                 numplanes++;
1339
1340                 if (sv_newflymove.integer)
1341                         ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1342                 else
1343                 {
1344                         // modify original_velocity so it parallels all of the clip planes
1345                         for (i = 0;i < numplanes;i++)
1346                         {
1347                                 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1348                                 for (j = 0;j < numplanes;j++)
1349                                 {
1350                                         if (j != i)
1351                                         {
1352                                                 // not ok
1353                                                 if (DotProduct(new_velocity, planes[j]) < 0)
1354                                                         break;
1355                                         }
1356                                 }
1357                                 if (j == numplanes)
1358                                         break;
1359                         }
1360
1361                         if (i != numplanes)
1362                         {
1363                                 // go along this plane
1364                                 VectorCopy(new_velocity, ent->fields.server->velocity);
1365                         }
1366                         else
1367                         {
1368                                 // go along the crease
1369                                 if (numplanes != 2)
1370                                 {
1371                                         VectorClear(ent->fields.server->velocity);
1372                                         blocked = 7;
1373                                         break;
1374                                 }
1375                                 CrossProduct(planes[0], planes[1], dir);
1376                                 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1377                                 VectorNormalize(dir);
1378                                 d = DotProduct(dir, ent->fields.server->velocity);
1379                                 VectorScale(dir, d, ent->fields.server->velocity);
1380                         }
1381                 }
1382
1383                 // if current velocity is against the original velocity,
1384                 // stop dead to avoid tiny occilations in sloping corners
1385                 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1386                 {
1387                         VectorClear(ent->fields.server->velocity);
1388                         break;
1389                 }
1390         }
1391
1392         //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]);
1393
1394         /*
1395         if ((blocked & 1) == 0 && bumpcount > 1)
1396         {
1397                 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1398                 // flag ONGROUND if there's ground under it
1399                 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1400         }
1401         */
1402
1403         // LordHavoc: this came from QW and allows you to get out of water more easily
1404         if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1405                 VectorCopy(primal_velocity, ent->fields.server->velocity);
1406         if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1407                 ent->fields.server->velocity[2] -= gravity;
1408         return blocked;
1409 }
1410
1411 /*
1412 ============
1413 SV_Gravity
1414
1415 ============
1416 */
1417 static float SV_Gravity (prvm_edict_t *ent)
1418 {
1419         float ent_gravity;
1420         prvm_eval_t *val;
1421
1422         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1423         if (val!=0 && val->_float)
1424                 ent_gravity = val->_float;
1425         else
1426                 ent_gravity = 1.0;
1427         return ent_gravity * sv_gravity.value * sv.frametime;
1428 }
1429
1430
1431 /*
1432 ===============================================================================
1433
1434 PUSHMOVE
1435
1436 ===============================================================================
1437 */
1438
1439 /*
1440 ============
1441 SV_PushEntity
1442
1443 Does not change the entities velocity at all
1444 The trace struct is filled with the trace that has been done.
1445 Returns true if the push did not result in the entity being teleported by QC code.
1446 ============
1447 */
1448 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1449 {
1450         int type;
1451         vec3_t end;
1452
1453         VectorAdd (ent->fields.server->origin, push, end);
1454
1455         if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1456                 type = MOVE_MISSILE;
1457         else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1458                 type = MOVE_NOMONSTERS; // only clip against bmodels
1459         else
1460                 type = MOVE_NORMAL;
1461
1462         *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1463         if (trace->bmodelstartsolid && failonbmodelstartsolid)
1464                 return true;
1465
1466         VectorCopy (trace->endpos, ent->fields.server->origin);
1467         SV_LinkEdict(ent);
1468
1469 #if 0
1470         if(!trace->startsolid)
1471         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)
1472         {
1473                 Con_Printf("something eeeeevil happened\n");
1474         }
1475 #endif
1476
1477         if (dolink)
1478                 SV_LinkEdict_TouchAreaGrid(ent);
1479
1480         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))))
1481                 return SV_Impact (ent, trace);
1482
1483         return true;
1484 }
1485
1486
1487 /*
1488 ============
1489 SV_PushMove
1490
1491 ============
1492 */
1493 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1494 {
1495         int i, e, index;
1496         int pusherowner, pusherprog;
1497         int checkcontents;
1498         qboolean rotated;
1499         float savesolid, movetime2, pushltime;
1500         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1501         int num_moved;
1502         int numcheckentities;
1503         static prvm_edict_t *checkentities[MAX_EDICTS];
1504         dp_model_t *pushermodel;
1505         trace_t trace, trace2;
1506         matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1507         unsigned short moved_edicts[MAX_EDICTS];
1508
1509         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])
1510         {
1511                 pusher->fields.server->ltime += movetime;
1512                 return;
1513         }
1514
1515         switch ((int) pusher->fields.server->solid)
1516         {
1517         // LordHavoc: valid pusher types
1518         case SOLID_BSP:
1519         case SOLID_BBOX:
1520         case SOLID_SLIDEBOX:
1521         case SOLID_CORPSE: // LordHavoc: this would be weird...
1522                 break;
1523         // LordHavoc: no collisions
1524         case SOLID_NOT:
1525         case SOLID_TRIGGER:
1526                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1527                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1528                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1529                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1530                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1531                 pusher->fields.server->ltime += movetime;
1532                 SV_LinkEdict(pusher);
1533                 return;
1534         default:
1535                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1536                 return;
1537         }
1538         index = (int) pusher->fields.server->modelindex;
1539         if (index < 1 || index >= MAX_MODELS)
1540         {
1541                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1542                 return;
1543         }
1544         pushermodel = sv.models[index];
1545         pusherowner = pusher->fields.server->owner;
1546         pusherprog = PRVM_EDICT_TO_PROG(pusher);
1547
1548         rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1549
1550         movetime2 = movetime;
1551         VectorScale(pusher->fields.server->velocity, movetime2, move1);
1552         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1553         if (moveangle[0] || moveangle[2])
1554         {
1555                 for (i = 0;i < 3;i++)
1556                 {
1557                         if (move1[i] > 0)
1558                         {
1559                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1560                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1561                         }
1562                         else
1563                         {
1564                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1565                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1566                         }
1567                 }
1568         }
1569         else if (moveangle[1])
1570         {
1571                 for (i = 0;i < 3;i++)
1572                 {
1573                         if (move1[i] > 0)
1574                         {
1575                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1576                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1577                         }
1578                         else
1579                         {
1580                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1581                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1582                         }
1583                 }
1584         }
1585         else
1586         {
1587                 for (i = 0;i < 3;i++)
1588                 {
1589                         if (move1[i] > 0)
1590                         {
1591                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1592                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1593                         }
1594                         else
1595                         {
1596                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1597                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1598                         }
1599                 }
1600         }
1601
1602         VectorNegate (moveangle, a);
1603         AngleVectorsFLU (a, forward, left, up);
1604
1605         VectorCopy (pusher->fields.server->origin, pushorig);
1606         VectorCopy (pusher->fields.server->angles, pushang);
1607         pushltime = pusher->fields.server->ltime;
1608
1609 // move the pusher to its final position
1610
1611         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1612         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1613         pusher->fields.server->ltime += movetime;
1614         SV_LinkEdict(pusher);
1615
1616         pushermodel = NULL;
1617         if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1618                 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1619         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);
1620         Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1621
1622         savesolid = pusher->fields.server->solid;
1623
1624 // see if any solid entities are inside the final position
1625         num_moved = 0;
1626
1627         numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1628         for (e = 0;e < numcheckentities;e++)
1629         {
1630                 prvm_edict_t *check = checkentities[e];
1631                 if (check->fields.server->movetype == MOVETYPE_NONE
1632                  || check->fields.server->movetype == MOVETYPE_PUSH
1633                  || check->fields.server->movetype == MOVETYPE_FOLLOW
1634                  || check->fields.server->movetype == MOVETYPE_NOCLIP
1635                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1636                         continue;
1637
1638                 if (check->fields.server->owner == pusherprog)
1639                         continue;
1640
1641                 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1642                         continue;
1643
1644                 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1645
1646                 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1647                 check->priv.server->waterposition_forceupdate = true;
1648
1649                 checkcontents = SV_GenericHitSuperContentsMask(check);
1650
1651                 // if the entity is standing on the pusher, it will definitely be moved
1652                 // if the entity is not standing on the pusher, but is in the pusher's
1653                 // final position, move it
1654                 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1655                 {
1656                         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);
1657                         //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1658                         if (!trace.startsolid)
1659                         {
1660                                 //Con_Printf("- not in solid\n");
1661                                 continue;
1662                         }
1663                 }
1664
1665                 if (rotated)
1666                 {
1667                         vec3_t org2;
1668                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1669                         org2[0] = DotProduct (org, forward);
1670                         org2[1] = DotProduct (org, left);
1671                         org2[2] = DotProduct (org, up);
1672                         VectorSubtract (org2, org, move);
1673                         VectorAdd (move, move1, move);
1674                 }
1675                 else
1676                         VectorCopy (move1, move);
1677
1678                 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1679
1680                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1681                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1682                 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1683
1684                 // try moving the contacted entity
1685                 pusher->fields.server->solid = SOLID_NOT;
1686                 if(!SV_PushEntity (&trace, check, move, true, true))
1687                 {
1688                         // entity "check" got teleported
1689                         check->fields.server->angles[1] += trace.fraction * moveangle[1];
1690                         pusher->fields.server->solid = savesolid; // was SOLID_BSP
1691                         continue; // pushed enough
1692                 }
1693                 // FIXME: turn players specially
1694                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1695                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1696                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1697
1698                 // this trace.fraction < 1 check causes items to fall off of pushers
1699                 // if they pass under or through a wall
1700                 // the groundentity check causes items to fall off of ledges
1701                 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1702                         check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1703
1704                 // if it is still inside the pusher, block
1705                 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);
1706                 if (trace.startsolid)
1707                 {
1708                         // try moving the contacted entity a tiny bit further to account for precision errors
1709                         vec3_t move2;
1710                         pusher->fields.server->solid = SOLID_NOT;
1711                         VectorScale(move, 1.1, move2);
1712                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1713                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1714                         if(!SV_PushEntity (&trace2, check, move2, true, true))
1715                         {
1716                                 // entity "check" got teleported
1717                                 continue;
1718                         }
1719                         pusher->fields.server->solid = savesolid;
1720                         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);
1721                         if (trace.startsolid)
1722                         {
1723                                 // try moving the contacted entity a tiny bit less to account for precision errors
1724                                 pusher->fields.server->solid = SOLID_NOT;
1725                                 VectorScale(move, 0.9, move2);
1726                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1727                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1728                                 if(!SV_PushEntity (&trace2, check, move2, true, true))
1729                                 {
1730                                         // entity "check" got teleported
1731                                         continue;
1732                                 }
1733                                 pusher->fields.server->solid = savesolid;
1734                                 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);
1735                                 if (trace.startsolid)
1736                                 {
1737                                         // still inside pusher, so it's really blocked
1738
1739                                         // fail the move
1740                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1741                                                 continue;
1742                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1743                                         {
1744                                                 // corpse
1745                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1746                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1747                                                 continue;
1748                                         }
1749
1750                                         VectorCopy (pushorig, pusher->fields.server->origin);
1751                                         VectorCopy (pushang, pusher->fields.server->angles);
1752                                         pusher->fields.server->ltime = pushltime;
1753                                         SV_LinkEdict(pusher);
1754
1755                                         // move back any entities we already moved
1756                                         for (i = 0;i < num_moved;i++)
1757                                         {
1758                                                 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1759                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1760                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1761                                                 SV_LinkEdict(ed);
1762                                         }
1763
1764                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1765                                         if (pusher->fields.server->blocked)
1766                                         {
1767                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1768                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1769                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1770                                         }
1771                                         break;
1772                                 }
1773                         }
1774                 }
1775         }
1776         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1777         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1778         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1779 }
1780
1781 /*
1782 ================
1783 SV_Physics_Pusher
1784
1785 ================
1786 */
1787 void SV_Physics_Pusher (prvm_edict_t *ent)
1788 {
1789         float thinktime, oldltime, movetime;
1790
1791         oldltime = ent->fields.server->ltime;
1792
1793         thinktime = ent->fields.server->nextthink;
1794         if (thinktime < ent->fields.server->ltime + sv.frametime)
1795         {
1796                 movetime = thinktime - ent->fields.server->ltime;
1797                 if (movetime < 0)
1798                         movetime = 0;
1799         }
1800         else
1801                 movetime = sv.frametime;
1802
1803         if (movetime)
1804                 // advances ent->fields.server->ltime if not blocked
1805                 SV_PushMove (ent, movetime);
1806
1807         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1808         {
1809                 ent->fields.server->nextthink = 0;
1810                 prog->globals.server->time = sv.time;
1811                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1812                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1813                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1814         }
1815 }
1816
1817
1818 /*
1819 ===============================================================================
1820
1821 CLIENT MOVEMENT
1822
1823 ===============================================================================
1824 */
1825
1826 static float unstickoffsets[] =
1827 {
1828         // poutting -/+z changes first as they are least weird
1829          0,  0,  -1,
1830          0,  0,  1,
1831          // x or y changes
1832         -1,  0,  0,
1833          1,  0,  0,
1834          0, -1,  0,
1835          0,  1,  0,
1836          // x and y changes
1837         -1, -1,  0,
1838          1, -1,  0,
1839         -1,  1,  0,
1840          1,  1,  0,
1841 };
1842
1843 typedef enum unstickresult_e
1844 {
1845         UNSTICK_STUCK = 0,
1846         UNSTICK_GOOD = 1,
1847         UNSTICK_UNSTUCK = 2
1848 }
1849 unstickresult_t;
1850
1851 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1852 {
1853         int i, maxunstick;
1854
1855         // if not stuck in a bmodel, just return
1856         if (!SV_TestEntityPosition(ent, vec3_origin))
1857                 return UNSTICK_GOOD;
1858
1859         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1860         {
1861                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1862                 {
1863                         VectorCopy(unstickoffsets + i, offset);
1864                         SV_LinkEdict(ent);
1865                         //SV_LinkEdict_TouchAreaGrid(ent);
1866                         return UNSTICK_UNSTUCK;
1867                 }
1868         }
1869
1870         maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1871         // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1872
1873         for(i = 2; i <= maxunstick; ++i)
1874         {
1875                 VectorClear(offset);
1876                 offset[2] = -i;
1877                 if (!SV_TestEntityPosition(ent, offset))
1878                 {
1879                         SV_LinkEdict(ent);
1880                         //SV_LinkEdict_TouchAreaGrid(ent);
1881                         return UNSTICK_UNSTUCK;
1882                 }
1883                 offset[2] = i;
1884                 if (!SV_TestEntityPosition(ent, offset))
1885                 {
1886                         SV_LinkEdict(ent);
1887                         //SV_LinkEdict_TouchAreaGrid(ent);
1888                         return UNSTICK_UNSTUCK;
1889                 }
1890         }
1891
1892         return UNSTICK_STUCK;
1893 }
1894
1895 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1896 {
1897         vec3_t offset;
1898         switch(SV_UnstickEntityReturnOffset(ent, offset))
1899         {
1900                 case UNSTICK_GOOD:
1901                         return true;
1902                 case UNSTICK_UNSTUCK:
1903                         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]);
1904                         return true;
1905                 case UNSTICK_STUCK:
1906                         if (developer.integer >= 100)
1907                                 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1908                         return false;
1909                 default:
1910                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1911                         return false;
1912         }
1913 }
1914
1915 /*
1916 =============
1917 SV_CheckStuck
1918
1919 This is a big hack to try and fix the rare case of getting stuck in the world
1920 clipping hull.
1921 =============
1922 */
1923 void SV_CheckStuck (prvm_edict_t *ent)
1924 {
1925         vec3_t offset;
1926
1927         switch(SV_UnstickEntityReturnOffset(ent, offset))
1928         {
1929                 case UNSTICK_GOOD:
1930                         VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1931                         break;
1932                 case UNSTICK_UNSTUCK:
1933                         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]);
1934                         break;
1935                 case UNSTICK_STUCK:
1936                         VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1937                         if (!SV_TestEntityPosition(ent, offset))
1938                         {
1939                                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1940                                 SV_LinkEdict(ent);
1941                                 //SV_LinkEdict_TouchAreaGrid(ent);
1942                         }
1943                         else
1944                                 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1945                         break;
1946                 default:
1947                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1948         }
1949 }
1950
1951
1952 /*
1953 =============
1954 SV_CheckWater
1955 =============
1956 */
1957 qboolean SV_CheckWater (prvm_edict_t *ent)
1958 {
1959         int cont;
1960         int nNativeContents;
1961         vec3_t point;
1962
1963         point[0] = ent->fields.server->origin[0];
1964         point[1] = ent->fields.server->origin[1];
1965         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1966
1967         // DRESK - Support for Entity Contents Transition Event
1968         // NOTE: Some logic needed to be slightly re-ordered
1969         // to not affect performance and allow for the feature.
1970
1971         // Acquire Super Contents Prior to Resets
1972         cont = SV_PointSuperContents(point);
1973         // Acquire Native Contents Here
1974         nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1975
1976         // DRESK - Support for Entity Contents Transition Event
1977         if(ent->fields.server->watertype)
1978                 // Entity did NOT Spawn; Check
1979                 SV_CheckContentsTransition(ent, nNativeContents);
1980
1981
1982         ent->fields.server->waterlevel = 0;
1983         ent->fields.server->watertype = CONTENTS_EMPTY;
1984         cont = SV_PointSuperContents(point);
1985         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1986         {
1987                 ent->fields.server->watertype = nNativeContents;
1988                 ent->fields.server->waterlevel = 1;
1989                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1990                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1991                 {
1992                         ent->fields.server->waterlevel = 2;
1993                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1994                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1995                                 ent->fields.server->waterlevel = 3;
1996                 }
1997         }
1998
1999         return ent->fields.server->waterlevel > 1;
2000 }
2001
2002 /*
2003 ============
2004 SV_WallFriction
2005
2006 ============
2007 */
2008 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2009 {
2010         float d, i;
2011         vec3_t forward, into, side;
2012
2013         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2014         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2015         {
2016                 // cut the tangential velocity
2017                 i = DotProduct (stepnormal, ent->fields.server->velocity);
2018                 VectorScale (stepnormal, i, into);
2019                 VectorSubtract (ent->fields.server->velocity, into, side);
2020                 ent->fields.server->velocity[0] = side[0] * (1 + d);
2021                 ent->fields.server->velocity[1] = side[1] * (1 + d);
2022         }
2023 }
2024
2025 #if 0
2026 /*
2027 =====================
2028 SV_TryUnstick
2029
2030 Player has come to a dead stop, possibly due to the problem with limited
2031 float precision at some angle joins in the BSP hull.
2032
2033 Try fixing by pushing one pixel in each direction.
2034
2035 This is a hack, but in the interest of good gameplay...
2036 ======================
2037 */
2038 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2039 {
2040         int i, clip;
2041         vec3_t oldorg, dir;
2042
2043         VectorCopy (ent->fields.server->origin, oldorg);
2044         VectorClear (dir);
2045
2046         for (i=0 ; i<8 ; i++)
2047         {
2048                 // try pushing a little in an axial direction
2049                 switch (i)
2050                 {
2051                         case 0: dir[0] = 2; dir[1] = 0; break;
2052                         case 1: dir[0] = 0; dir[1] = 2; break;
2053                         case 2: dir[0] = -2; dir[1] = 0; break;
2054                         case 3: dir[0] = 0; dir[1] = -2; break;
2055                         case 4: dir[0] = 2; dir[1] = 2; break;
2056                         case 5: dir[0] = -2; dir[1] = 2; break;
2057                         case 6: dir[0] = 2; dir[1] = -2; break;
2058                         case 7: dir[0] = -2; dir[1] = -2; break;
2059                 }
2060
2061                 SV_PushEntity (&trace, ent, dir, false, true);
2062
2063                 // retry the original move
2064                 ent->fields.server->velocity[0] = oldvel[0];
2065                 ent->fields.server->velocity[1] = oldvel[1];
2066                 ent->fields.server->velocity[2] = 0;
2067                 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2068
2069                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2070                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2071                 {
2072                         Con_DPrint("TryUnstick - success.\n");
2073                         return clip;
2074                 }
2075
2076                 // go back to the original pos and try again
2077                 VectorCopy (oldorg, ent->fields.server->origin);
2078         }
2079
2080         // still not moving
2081         VectorClear (ent->fields.server->velocity);
2082         Con_DPrint("TryUnstick - failure.\n");
2083         return 7;
2084 }
2085 #endif
2086
2087 /*
2088 =====================
2089 SV_WalkMove
2090
2091 Only used by players
2092 ======================
2093 */
2094 void SV_WalkMove (prvm_edict_t *ent)
2095 {
2096         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2097         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2098         trace_t downtrace, trace;
2099         qboolean applygravity;
2100
2101         // if frametime is 0 (due to client sending the same timestamp twice),
2102         // don't move
2103         if (sv.frametime <= 0)
2104                 return;
2105
2106         SV_CheckStuck (ent);
2107
2108         applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2109
2110         hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2111
2112         SV_CheckVelocity(ent);
2113
2114         // do a regular slide move unless it looks like you ran into a step
2115         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2116
2117         VectorCopy (ent->fields.server->origin, start_origin);
2118         VectorCopy (ent->fields.server->velocity, start_velocity);
2119
2120         clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2121
2122         // if the move did not hit the ground at any point, we're not on ground
2123         if (!(clip & 1))
2124                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2125
2126         SV_CheckVelocity(ent);
2127         SV_LinkEdict(ent);
2128         SV_LinkEdict_TouchAreaGrid(ent);
2129
2130         if(clip & 8) // teleport
2131                 return;
2132
2133         if ((int)ent->fields.server->flags & FL_WATERJUMP)
2134                 return;
2135
2136         if (sv_nostep.integer)
2137                 return;
2138
2139         VectorCopy(ent->fields.server->origin, originalmove_origin);
2140         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2141         originalmove_clip = clip;
2142         originalmove_flags = (int)ent->fields.server->flags;
2143         originalmove_groundentity = ent->fields.server->groundentity;
2144
2145         // if move didn't block on a step, return
2146         if (clip & 2)
2147         {
2148                 // if move was not trying to move into the step, return
2149                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2150                         return;
2151
2152                 if (ent->fields.server->movetype != MOVETYPE_FLY)
2153                 {
2154                         // return if gibbed by a trigger
2155                         if (ent->fields.server->movetype != MOVETYPE_WALK)
2156                                 return;
2157
2158                         // only step up while jumping if that is enabled
2159                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2160                                 if (!oldonground && ent->fields.server->waterlevel == 0)
2161                                         return;
2162                 }
2163
2164                 // try moving up and forward to go up a step
2165                 // back to start pos
2166                 VectorCopy (start_origin, ent->fields.server->origin);
2167                 VectorCopy (start_velocity, ent->fields.server->velocity);
2168
2169                 // move up
2170                 VectorClear (upmove);
2171                 upmove[2] = sv_stepheight.value;
2172                 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2173                 {
2174                         // we got teleported when upstepping... must abort the move
2175                         return;
2176                 }
2177
2178                 // move forward
2179                 ent->fields.server->velocity[2] = 0;
2180                 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2181                 ent->fields.server->velocity[2] += start_velocity[2];
2182                 if(clip & 8)
2183                 {
2184                         // we got teleported when upstepping... must abort the move
2185                         // note that z velocity handling may not be what QC expects here, but we cannot help it
2186                         return;
2187                 }
2188
2189                 SV_CheckVelocity(ent);
2190                 SV_LinkEdict(ent);
2191                 SV_LinkEdict_TouchAreaGrid(ent);
2192
2193                 // check for stuckness, possibly due to the limited precision of floats
2194                 // in the clipping hulls
2195                 if (clip
2196                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2197                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2198                 {
2199                         //Con_Printf("wall\n");
2200                         // stepping up didn't make any progress, revert to original move
2201                         VectorCopy(originalmove_origin, ent->fields.server->origin);
2202                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2203                         //clip = originalmove_clip;
2204                         ent->fields.server->flags = originalmove_flags;
2205                         ent->fields.server->groundentity = originalmove_groundentity;
2206                         // now try to unstick if needed
2207                         //clip = SV_TryUnstick (ent, oldvel);
2208                         return;
2209                 }
2210
2211                 //Con_Printf("step - ");
2212
2213                 // extra friction based on view angle
2214                 if (clip & 2 && sv_wallfriction.integer)
2215                         SV_WallFriction (ent, stepnormal);
2216         }
2217         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2218         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))
2219                 return;
2220
2221         // move down
2222         VectorClear (downmove);
2223         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2224         if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2225         {
2226                 // we got teleported when downstepping... must abort the move
2227                 return;
2228         }
2229
2230         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2231         {
2232                 // this has been disabled so that you can't jump when you are stepping
2233                 // up while already jumping (also known as the Quake2 double jump bug)
2234 #if 0
2235                 // LordHavoc: disabled this check so you can walk on monsters/players
2236                 //if (ent->fields.server->solid == SOLID_BSP)
2237                 {
2238                         //Con_Printf("onground\n");
2239                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
2240                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2241                 }
2242 #endif
2243         }
2244         else
2245         {
2246                 //Con_Printf("slope\n");
2247                 // if the push down didn't end up on good ground, use the move without
2248                 // the step up.  This happens near wall / slope combinations, and can
2249                 // cause the player to hop up higher on a slope too steep to climb
2250                 VectorCopy(originalmove_origin, ent->fields.server->origin);
2251                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2252                 //clip = originalmove_clip;
2253                 ent->fields.server->flags = originalmove_flags;
2254                 ent->fields.server->groundentity = originalmove_groundentity;
2255         }
2256
2257         SV_CheckVelocity(ent);
2258         SV_LinkEdict(ent);
2259         SV_LinkEdict_TouchAreaGrid(ent);
2260 }
2261
2262 //============================================================================
2263
2264 /*
2265 =============
2266 SV_Physics_Follow
2267
2268 Entities that are "stuck" to another entity
2269 =============
2270 */
2271 void SV_Physics_Follow (prvm_edict_t *ent)
2272 {
2273         vec3_t vf, vr, vu, angles, v;
2274         prvm_edict_t *e;
2275
2276         // regular thinking
2277         if (!SV_RunThink (ent))
2278                 return;
2279
2280         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2281         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2282         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])
2283         {
2284                 // quick case for no rotation
2285                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2286         }
2287         else
2288         {
2289                 angles[0] = -ent->fields.server->punchangle[0];
2290                 angles[1] =  ent->fields.server->punchangle[1];
2291                 angles[2] =  ent->fields.server->punchangle[2];
2292                 AngleVectors (angles, vf, vr, vu);
2293                 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];
2294                 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];
2295                 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];
2296                 angles[0] = -e->fields.server->angles[0];
2297                 angles[1] =  e->fields.server->angles[1];
2298                 angles[2] =  e->fields.server->angles[2];
2299                 AngleVectors (angles, vf, vr, vu);
2300                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2301                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2302                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2303         }
2304         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2305         SV_LinkEdict(ent);
2306         //SV_LinkEdict_TouchAreaGrid(ent);
2307 }
2308
2309 /*
2310 ==============================================================================
2311
2312 TOSS / BOUNCE
2313
2314 ==============================================================================
2315 */
2316
2317 /*
2318 =============
2319 SV_CheckWaterTransition
2320
2321 =============
2322 */
2323 void SV_CheckWaterTransition (prvm_edict_t *ent)
2324 {
2325         int cont;
2326         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2327         if (!ent->fields.server->watertype)
2328         {
2329                 // just spawned here
2330                 ent->fields.server->watertype = cont;
2331                 ent->fields.server->waterlevel = 1;
2332                 return;
2333         }
2334
2335         // DRESK - Support for Entity Contents Transition Event
2336         // NOTE: Call here BEFORE updating the watertype below,
2337         // and suppress watersplash sound if a valid function
2338         // call was made to allow for custom "splash" sounds.
2339         if( !SV_CheckContentsTransition(ent, cont) )
2340         { // Contents Transition Function Invalid; Potentially Play Water Sound
2341                 // check if the entity crossed into or out of water
2342                 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2343                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2344         }
2345
2346         if (cont <= CONTENTS_WATER)
2347         {
2348                 ent->fields.server->watertype = cont;
2349                 ent->fields.server->waterlevel = 1;
2350         }
2351         else
2352         {
2353                 ent->fields.server->watertype = CONTENTS_EMPTY;
2354                 ent->fields.server->waterlevel = 0;
2355         }
2356 }
2357
2358 /*
2359 =============
2360 SV_Physics_Toss
2361
2362 Toss, bounce, and fly movement.  When onground, do nothing.
2363 =============
2364 */
2365 void SV_Physics_Toss (prvm_edict_t *ent)
2366 {
2367         trace_t trace;
2368         vec3_t move;
2369         vec_t movetime;
2370         int bump;
2371
2372 // if onground, return without moving
2373         if ((int)ent->fields.server->flags & FL_ONGROUND)
2374         {
2375                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2376                 {
2377                         // don't stick to ground if onground and moving upward
2378                         ent->fields.server->flags -= FL_ONGROUND;
2379                 }
2380                 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2381                 {
2382                         // we can trust FL_ONGROUND if groundentity is world because it never moves
2383                         return;
2384                 }
2385                 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2386                 {
2387                         // if ent was supported by a brush model on previous frame,
2388                         // and groundentity is now freed, set groundentity to 0 (world)
2389                         // which leaves it suspended in the air
2390                         ent->fields.server->groundentity = 0;
2391                         if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2392                                 return;
2393                 }
2394         }
2395         ent->priv.server->suspendedinairflag = false;
2396
2397         SV_CheckVelocity (ent);
2398
2399 // add gravity
2400         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2401                 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2402
2403 // move angles
2404         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2405
2406         movetime = sv.frametime;
2407         for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2408         {
2409         // move origin
2410                 VectorScale (ent->fields.server->velocity, movetime, move);
2411                 if(!SV_PushEntity (&trace, ent, move, true, true))
2412                         return; // teleported
2413                 if (ent->priv.server->free)
2414                         return;
2415                 if (trace.bmodelstartsolid)
2416                 {
2417                         // try to unstick the entity
2418                         SV_UnstickEntity(ent);
2419                         if(!SV_PushEntity (&trace, ent, move, false, true))
2420                                 return; // teleported
2421                         if (ent->priv.server->free)
2422                                 return;
2423                 }
2424                 if (trace.fraction == 1)
2425                         break;
2426                 movetime *= 1 - min(1, trace.fraction);
2427                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2428                 {
2429                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
2430                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2431                 }
2432                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2433                 {
2434                         float d, ent_gravity;
2435                         prvm_eval_t *val;
2436                         float bouncefactor = 0.5f;
2437                         float bouncestop = 60.0f / 800.0f;
2438
2439                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2440                         if (val!=0 && val->_float)
2441                                 bouncefactor = val->_float;
2442
2443                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2444                         if (val!=0 && val->_float)
2445                                 bouncestop = val->_float;
2446
2447                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2448                         // LordHavoc: fixed grenades not bouncing when fired down a slope
2449                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2450                         if (val!=0 && val->_float)
2451                                 ent_gravity = val->_float;
2452                         else
2453                                 ent_gravity = 1.0;
2454                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
2455                         {
2456                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2457                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2458                                 {
2459                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2460                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2461                                         VectorClear (ent->fields.server->velocity);
2462                                         VectorClear (ent->fields.server->avelocity);
2463                                 }
2464                                 else
2465                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2466                         }
2467                         else
2468                         {
2469                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2470                                 {
2471                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2472                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2473                                         VectorClear (ent->fields.server->velocity);
2474                                         VectorClear (ent->fields.server->avelocity);
2475                                 }
2476                                 else
2477                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2478                         }
2479                 }
2480                 else
2481                 {
2482                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2483                         if (trace.plane.normal[2] > 0.7)
2484                         {
2485                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2486                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2487                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2488                                         ent->priv.server->suspendedinairflag = true;
2489                                 VectorClear (ent->fields.server->velocity);
2490                                 VectorClear (ent->fields.server->avelocity);
2491                         }
2492                         else
2493                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2494                 }
2495                 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2496                         break;
2497         }
2498
2499 // check for in water
2500         SV_CheckWaterTransition (ent);
2501 }
2502
2503 /*
2504 ===============================================================================
2505
2506 STEPPING MOVEMENT
2507
2508 ===============================================================================
2509 */
2510
2511 /*
2512 =============
2513 SV_Physics_Step
2514
2515 Monsters freefall when they don't have a ground entity, otherwise
2516 all movement is done with discrete steps.
2517
2518 This is also used for objects that have become still on the ground, but
2519 will fall if the floor is pulled out from under them.
2520 =============
2521 */
2522 void SV_Physics_Step (prvm_edict_t *ent)
2523 {
2524         int flags = (int)ent->fields.server->flags;
2525
2526         // DRESK
2527         // Backup Velocity in the event that movetypesteplandevent is called,
2528         // to provide a parameter with the entity's velocity at impact.
2529         prvm_eval_t *movetypesteplandevent;
2530         vec3_t backupVelocity;
2531         VectorCopy(ent->fields.server->velocity, backupVelocity);
2532         // don't fall at all if fly/swim
2533         if (!(flags & (FL_FLY | FL_SWIM)))
2534         {
2535                 if (flags & FL_ONGROUND)
2536                 {
2537                         // freefall if onground and moving upward
2538                         // freefall if not standing on a world surface (it may be a lift or trap door)
2539                         if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2540                         {
2541                                 ent->fields.server->flags -= FL_ONGROUND;
2542                                 SV_CheckVelocity(ent);
2543                                 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2544                                 SV_LinkEdict(ent);
2545                                 SV_LinkEdict_TouchAreaGrid(ent);
2546                                 ent->priv.server->waterposition_forceupdate = true;
2547                         }
2548                 }
2549                 else
2550                 {
2551                         // freefall if not onground
2552                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2553
2554                         SV_CheckVelocity(ent);
2555                         SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2556                         SV_LinkEdict(ent);
2557                         SV_LinkEdict_TouchAreaGrid(ent);
2558
2559                         // just hit ground
2560                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2561                         {
2562                                 // DRESK - Check for Entity Land Event Function
2563                                 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2564
2565                                 if(movetypesteplandevent->function)
2566                                 { // Valid Function; Execute
2567                                         // Prepare Parameters
2568                                                 // Assign Velocity at Impact
2569                                                 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2570                                                 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2571                                                 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2572                                                 // Assign Self
2573                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2574                                         // Execute VM Function
2575                                         PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2576                                 }
2577                                 else
2578                                 // Check for Engine Landing Sound
2579                                 if(sv_sound_land.string)
2580                                         SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2581                         }
2582                         ent->priv.server->waterposition_forceupdate = true;
2583                 }
2584         }
2585
2586 // regular thinking
2587         if (!SV_RunThink(ent))
2588                 return;
2589
2590         if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2591         {
2592                 ent->priv.server->waterposition_forceupdate = false;
2593                 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2594                 SV_CheckWaterTransition(ent);
2595         }
2596 }
2597
2598 //============================================================================
2599
2600 static void SV_Physics_Entity (prvm_edict_t *ent)
2601 {
2602         // don't run think/move on newly spawned projectiles as it messes up
2603         // movement interpolation and rocket trails, and is inconsistent with
2604         // respect to entities spawned in the same frame
2605         // (if an ent spawns a higher numbered ent, it moves in the same frame,
2606         //  but if it spawns a lower numbered ent, it doesn't - this never moves
2607         //  ents in the first frame regardless)
2608         qboolean runmove = ent->priv.server->move;
2609         ent->priv.server->move = true;
2610         if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2611                 return;
2612         switch ((int) ent->fields.server->movetype)
2613         {
2614         case MOVETYPE_PUSH:
2615         case MOVETYPE_FAKEPUSH:
2616                 SV_Physics_Pusher (ent);
2617                 break;
2618         case MOVETYPE_NONE:
2619                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2620                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2621                         SV_RunThink (ent);
2622                 break;
2623         case MOVETYPE_FOLLOW:
2624                 SV_Physics_Follow (ent);
2625                 break;
2626         case MOVETYPE_NOCLIP:
2627                 if (SV_RunThink(ent))
2628                 {
2629                         SV_CheckWater(ent);
2630                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2631                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2632                 }
2633                 SV_LinkEdict(ent);
2634                 break;
2635         case MOVETYPE_STEP:
2636                 SV_Physics_Step (ent);
2637                 break;
2638         case MOVETYPE_WALK:
2639                 if (SV_RunThink (ent))
2640                         SV_WalkMove (ent);
2641                 break;
2642         case MOVETYPE_TOSS:
2643         case MOVETYPE_BOUNCE:
2644         case MOVETYPE_BOUNCEMISSILE:
2645         case MOVETYPE_FLYMISSILE:
2646         case MOVETYPE_FLY:
2647                 // regular thinking
2648                 if (SV_RunThink (ent))
2649                         SV_Physics_Toss (ent);
2650                 break;
2651         default:
2652                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2653                 break;
2654         }
2655 }
2656
2657 void SV_Physics_ClientMove(void)
2658 {
2659         prvm_edict_t *ent;
2660         ent = host_client->edict;
2661
2662         // call player physics, this needs the proper frametime
2663         prog->globals.server->frametime = sv.frametime;
2664         SV_ClientThink();
2665
2666         // call standard client pre-think, with frametime = 0
2667         prog->globals.server->time = sv.time;
2668         prog->globals.server->frametime = 0;
2669         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2670         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2671         prog->globals.server->frametime = sv.frametime;
2672
2673         // make sure the velocity is sane (not a NaN)
2674         SV_CheckVelocity(ent);
2675         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2676         // player_run/player_stand1 does not horribly malfunction if the
2677         // velocity becomes a number that is both == 0 and != 0
2678         // (sounds to me like NaN but to be absolutely safe...)
2679         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2680                 VectorClear(ent->fields.server->velocity);
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 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2705 {
2706         // don't do physics on disconnected clients, FrikBot relies on this
2707         if (!host_client->spawned)
2708         {
2709                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2710                 return;
2711         }
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 sane (not a NaN)
2721         SV_CheckVelocity(ent);
2722         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2723         // player_run/player_stand1 does not horribly malfunction if the
2724         // velocity becomes a number that is both == 0 and != 0
2725         // (sounds to me like NaN but to be absolutely safe...)
2726         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2727                 VectorClear(ent->fields.server->velocity);
2728
2729         // call standard client pre-think
2730         prog->globals.server->time = sv.time;
2731         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2732         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2733         SV_CheckVelocity (ent);
2734
2735         switch ((int) ent->fields.server->movetype)
2736         {
2737         case MOVETYPE_PUSH:
2738         case MOVETYPE_FAKEPUSH:
2739                 SV_Physics_Pusher (ent);
2740                 break;
2741         case MOVETYPE_NONE:
2742                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2743                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2744                         SV_RunThink (ent);
2745                 break;
2746         case MOVETYPE_FOLLOW:
2747                 SV_Physics_Follow (ent);
2748                 break;
2749         case MOVETYPE_NOCLIP:
2750                 SV_RunThink(ent);
2751                 SV_CheckWater(ent);
2752                 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2753                 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2754                 break;
2755         case MOVETYPE_STEP:
2756                 SV_Physics_Step (ent);
2757                 break;
2758         case MOVETYPE_WALK:
2759                 SV_RunThink (ent);
2760                 // don't run physics here if running asynchronously
2761                 if (host_client->clmovement_inputtimeout <= 0)
2762                         SV_WalkMove (ent);
2763                 break;
2764         case MOVETYPE_TOSS:
2765         case MOVETYPE_BOUNCE:
2766         case MOVETYPE_BOUNCEMISSILE:
2767         case MOVETYPE_FLYMISSILE:
2768                 // regular thinking
2769                 SV_RunThink (ent);
2770                 SV_Physics_Toss (ent);
2771                 break;
2772         case MOVETYPE_FLY:
2773                 SV_RunThink (ent);
2774                 SV_WalkMove (ent);
2775                 break;
2776         default:
2777                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2778                 break;
2779         }
2780
2781         // decrement the countdown variable used to decide when to go back to
2782         // synchronous physics
2783         if (host_client->clmovement_inputtimeout > sv.frametime)
2784                 host_client->clmovement_inputtimeout -= sv.frametime;
2785         else
2786                 host_client->clmovement_inputtimeout = 0;
2787
2788         SV_CheckVelocity (ent);
2789
2790         SV_LinkEdict(ent);
2791         SV_LinkEdict_TouchAreaGrid(ent);
2792
2793         SV_CheckVelocity (ent);
2794
2795         // call standard player post-think
2796         prog->globals.server->time = sv.time;
2797         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2798         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2799
2800         if(ent->fields.server->fixangle)
2801         {
2802                 // angle fixing was requested by physics code...
2803                 // so store the current angles for later use
2804                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2805                 host_client->fixangle_angles_set = TRUE;
2806
2807                 // and clear fixangle for the next frame
2808                 ent->fields.server->fixangle = 0;
2809         }
2810 }
2811
2812 /*
2813 ================
2814 SV_Physics
2815
2816 ================
2817 */
2818 void SV_Physics (void)
2819 {
2820         int i;
2821         prvm_edict_t *ent;
2822
2823 // let the progs know that a new frame has started
2824         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2825         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2826         prog->globals.server->time = sv.time;
2827         prog->globals.server->frametime = sv.frametime;
2828         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2829
2830 //
2831 // treat each object in turn
2832 //
2833
2834         // if force_retouch, relink all the entities
2835         if (prog->globals.server->force_retouch > 0)
2836                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2837                         if (!ent->priv.server->free)
2838                                 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2839
2840         // run physics on the client entities
2841         for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2842                 if (!ent->priv.server->free)
2843                                 SV_Physics_ClientEntity(ent);
2844
2845         // run physics on all the non-client entities
2846         if (!sv_freezenonclients.integer)
2847         {
2848                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2849                         if (!ent->priv.server->free)
2850                                 SV_Physics_Entity(ent);
2851                 // make a second pass to see if any ents spawned this frame and make
2852                 // sure they run their move/think
2853                 if (sv_gameplayfix_delayprojectiles.integer < 0)
2854                         for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2855                                 if (!ent->priv.server->move && !ent->priv.server->free)
2856                                         SV_Physics_Entity(ent);
2857         }
2858
2859         if (prog->globals.server->force_retouch > 0)
2860                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2861
2862         // LordHavoc: endframe support
2863         if (prog->funcoffsets.EndFrame)
2864         {
2865                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2866                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2867                 prog->globals.server->time = sv.time;
2868                 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2869         }
2870
2871         // decrement prog->num_edicts if the highest number entities died
2872         for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2873
2874         if (!sv_freezenonclients.integer)
2875                 sv.time += sv.frametime;
2876 }