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