make DP compile with C++ again
[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 // LordHavoc: increased from 5 to 32
760 #define MAX_CLIP_PLANES 32
761 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
762 {
763         int blocked, bumpcount;
764         int i, j, impact, numplanes;
765         float d, time_left;
766         vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
767         trace_t trace;
768         if (time <= 0)
769                 return 0;
770         blocked = 0;
771         VectorCopy(ent->fields.server->velocity, original_velocity);
772         VectorCopy(ent->fields.server->velocity, primal_velocity);
773         numplanes = 0;
774         time_left = time;
775         for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
776         {
777                 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
778                         break;
779
780                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
781                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
782 #if 0
783                 //if (trace.fraction < 0.002)
784                 {
785 #if 1
786                         vec3_t start;
787                         trace_t testtrace;
788                         VectorCopy(ent->fields.server->origin, start);
789                         start[2] += 3;//0.03125;
790                         VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
791                         end[2] += 3;//0.03125;
792                         testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
793                         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)))
794                         {
795                                 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
796                                 trace = testtrace;
797                         }
798 #endif
799 #if 0
800                         //j = -1;
801                         for (i = 0;i < numplanes;i++)
802                         {
803                                 VectorCopy(ent->fields.server->origin, start);
804                                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
805                                 VectorMA(start, 3, planes[i], start);
806                                 VectorMA(end, 3, planes[i], end);
807                                 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
808                                 if (trace.fraction < testtrace.fraction)
809                                 {
810                                         trace = testtrace;
811                                         VectorCopy(start, ent->fields.server->origin);
812                                         //j = i;
813                                 }
814                         }
815                         //if (j >= 0)
816                         //      VectorAdd(ent->fields.server->origin, planes[j], start);
817 #endif
818                 }
819 #endif
820
821 #if 0
822                 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);
823                 if (trace.fraction < 1)
824                         Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
825                 Con_Print("\n");
826 #endif
827
828 #if 0
829                 if (trace.bmodelstartsolid)
830                 {
831                         // LordHavoc: note: this code is what makes entities stick in place
832                         // if embedded in world only (you can walk through other objects if
833                         // stuck)
834                         // entity is trapped in another solid
835                         VectorClear(ent->fields.server->velocity);
836                         return 3;
837                 }
838 #endif
839
840                 // break if it moved the entire distance
841                 if (trace.fraction == 1)
842                 {
843                         VectorCopy(trace.endpos, ent->fields.server->origin);
844                         break;
845                 }
846
847                 if (!trace.ent)
848                 {
849                         Con_Printf ("SV_FlyMove: !trace.ent");
850                         trace.ent = prog->edicts;
851                 }
852
853                 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
854
855                 if (trace.plane.normal[2])
856                 {
857                         if (trace.plane.normal[2] > 0.7)
858                         {
859                                 // floor
860                                 blocked |= 1;
861                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
862                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
863                         }
864                 }
865                 else
866                 {
867                         // step
868                         blocked |= 2;
869                         // save the trace for player extrafriction
870                         if (stepnormal)
871                                 VectorCopy(trace.plane.normal, stepnormal);
872                 }
873
874                 if (trace.fraction >= 0.001)
875                 {
876                         // actually covered some distance
877                         VectorCopy(trace.endpos, ent->fields.server->origin);
878                         VectorCopy(ent->fields.server->velocity, original_velocity);
879                         numplanes = 0;
880                 }
881
882                 // run the impact function
883                 if (impact)
884                 {
885                         SV_Impact(ent, &trace);
886
887                         // break if removed by the impact function
888                         if (ent->priv.server->free)
889                                 break;
890                 }
891
892                 time_left *= 1 - trace.fraction;
893
894                 // clipped to another plane
895                 if (numplanes >= MAX_CLIP_PLANES)
896                 {
897                         // this shouldn't really happen
898                         VectorClear(ent->fields.server->velocity);
899                         blocked = 3;
900                         break;
901                 }
902
903                 /*
904                 for (i = 0;i < numplanes;i++)
905                         if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
906                                 break;
907                 if (i < numplanes)
908                 {
909                         VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
910                         continue;
911                 }
912                 */
913
914                 VectorCopy(trace.plane.normal, planes[numplanes]);
915                 numplanes++;
916
917                 if (sv_newflymove.integer)
918                         ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
919                 else
920                 {
921                         // modify original_velocity so it parallels all of the clip planes
922                         for (i = 0;i < numplanes;i++)
923                         {
924                                 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
925                                 for (j = 0;j < numplanes;j++)
926                                 {
927                                         if (j != i)
928                                         {
929                                                 // not ok
930                                                 if (DotProduct(new_velocity, planes[j]) < 0)
931                                                         break;
932                                         }
933                                 }
934                                 if (j == numplanes)
935                                         break;
936                         }
937
938                         if (i != numplanes)
939                         {
940                                 // go along this plane
941                                 VectorCopy(new_velocity, ent->fields.server->velocity);
942                         }
943                         else
944                         {
945                                 // go along the crease
946                                 if (numplanes != 2)
947                                 {
948                                         VectorClear(ent->fields.server->velocity);
949                                         blocked = 7;
950                                         break;
951                                 }
952                                 CrossProduct(planes[0], planes[1], dir);
953                                 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
954                                 VectorNormalize(dir);
955                                 d = DotProduct(dir, ent->fields.server->velocity);
956                                 VectorScale(dir, d, ent->fields.server->velocity);
957                         }
958                 }
959
960                 // if current velocity is against the original velocity,
961                 // stop dead to avoid tiny occilations in sloping corners
962                 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
963                 {
964                         VectorClear(ent->fields.server->velocity);
965                         break;
966                 }
967         }
968
969         //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]);
970
971         /*
972         if ((blocked & 1) == 0 && bumpcount > 1)
973         {
974                 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
975                 // flag ONGROUND if there's ground under it
976                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
977         }
978         */
979
980         // LordHavoc: this came from QW and allows you to get out of water more easily
981         if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
982                 VectorCopy(primal_velocity, ent->fields.server->velocity);
983         return blocked;
984 }
985
986 /*
987 ============
988 SV_AddGravity
989
990 ============
991 */
992 void SV_AddGravity (prvm_edict_t *ent)
993 {
994         float ent_gravity;
995         prvm_eval_t *val;
996
997         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
998         if (val!=0 && val->_float)
999                 ent_gravity = val->_float;
1000         else
1001                 ent_gravity = 1.0;
1002         ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
1003 }
1004
1005
1006 /*
1007 ===============================================================================
1008
1009 PUSHMOVE
1010
1011 ===============================================================================
1012 */
1013
1014 /*
1015 ============
1016 SV_PushEntity
1017
1018 Does not change the entities velocity at all
1019 ============
1020 */
1021 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
1022 {
1023         int type;
1024         trace_t trace;
1025         vec3_t end;
1026
1027         VectorAdd (ent->fields.server->origin, push, end);
1028
1029         if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1030                 type = MOVE_MISSILE;
1031         else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1032                 type = MOVE_NOMONSTERS; // only clip against bmodels
1033         else
1034                 type = MOVE_NORMAL;
1035
1036         trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1037         if (trace.bmodelstartsolid && failonbmodelstartsolid)
1038                 return trace;
1039
1040         VectorCopy (trace.endpos, ent->fields.server->origin);
1041         SV_LinkEdict (ent, true);
1042
1043         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)))
1044                 SV_Impact (ent, &trace);
1045         return trace;
1046 }
1047
1048
1049 /*
1050 ============
1051 SV_PushMove
1052
1053 ============
1054 */
1055 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1056 {
1057         int i, e, index;
1058         int pusherowner, pusherprog;
1059         int checkcontents;
1060         qboolean rotated;
1061         float savesolid, movetime2, pushltime;
1062         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1063         int num_moved;
1064         int numcheckentities;
1065         static prvm_edict_t *checkentities[MAX_EDICTS];
1066         dp_model_t *pushermodel;
1067         trace_t trace;
1068         matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1069         unsigned short moved_edicts[MAX_EDICTS];
1070
1071         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])
1072         {
1073                 pusher->fields.server->ltime += movetime;
1074                 return;
1075         }
1076
1077         switch ((int) pusher->fields.server->solid)
1078         {
1079         // LordHavoc: valid pusher types
1080         case SOLID_BSP:
1081         case SOLID_BBOX:
1082         case SOLID_SLIDEBOX:
1083         case SOLID_CORPSE: // LordHavoc: this would be weird...
1084                 break;
1085         // LordHavoc: no collisions
1086         case SOLID_NOT:
1087         case SOLID_TRIGGER:
1088                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1089                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1090                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1091                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1092                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1093                 pusher->fields.server->ltime += movetime;
1094                 SV_LinkEdict (pusher, false);
1095                 return;
1096         default:
1097                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1098                 return;
1099         }
1100         index = (int) pusher->fields.server->modelindex;
1101         if (index < 1 || index >= MAX_MODELS)
1102         {
1103                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1104                 return;
1105         }
1106         pushermodel = sv.models[index];
1107         pusherowner = pusher->fields.server->owner;
1108         pusherprog = PRVM_EDICT_TO_PROG(pusher);
1109
1110         rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1111
1112         movetime2 = movetime;
1113         VectorScale(pusher->fields.server->velocity, movetime2, move1);
1114         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1115         if (moveangle[0] || moveangle[2])
1116         {
1117                 for (i = 0;i < 3;i++)
1118                 {
1119                         if (move1[i] > 0)
1120                         {
1121                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1122                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1123                         }
1124                         else
1125                         {
1126                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1127                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1128                         }
1129                 }
1130         }
1131         else if (moveangle[1])
1132         {
1133                 for (i = 0;i < 3;i++)
1134                 {
1135                         if (move1[i] > 0)
1136                         {
1137                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1138                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1139                         }
1140                         else
1141                         {
1142                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1143                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1144                         }
1145                 }
1146         }
1147         else
1148         {
1149                 for (i = 0;i < 3;i++)
1150                 {
1151                         if (move1[i] > 0)
1152                         {
1153                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1154                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1155                         }
1156                         else
1157                         {
1158                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1159                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1160                         }
1161                 }
1162         }
1163
1164         VectorNegate (moveangle, a);
1165         AngleVectorsFLU (a, forward, left, up);
1166
1167         VectorCopy (pusher->fields.server->origin, pushorig);
1168         VectorCopy (pusher->fields.server->angles, pushang);
1169         pushltime = pusher->fields.server->ltime;
1170
1171 // move the pusher to its final position
1172
1173         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1174         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1175         pusher->fields.server->ltime += movetime;
1176         SV_LinkEdict (pusher, false);
1177
1178         pushermodel = NULL;
1179         if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1180                 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1181         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);
1182         Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1183
1184         savesolid = pusher->fields.server->solid;
1185
1186 // see if any solid entities are inside the final position
1187         num_moved = 0;
1188
1189         numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1190         for (e = 0;e < numcheckentities;e++)
1191         {
1192                 prvm_edict_t *check = checkentities[e];
1193                 if (check->fields.server->movetype == MOVETYPE_NONE
1194                  || check->fields.server->movetype == MOVETYPE_PUSH
1195                  || check->fields.server->movetype == MOVETYPE_FOLLOW
1196                  || check->fields.server->movetype == MOVETYPE_NOCLIP
1197                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1198                         continue;
1199
1200                 if (check->fields.server->owner == pusherprog)
1201                         continue;
1202
1203                 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1204                         continue;
1205
1206                 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1207
1208                 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1209                 check->priv.server->waterposition_forceupdate = true;
1210
1211                 checkcontents = SV_GenericHitSuperContentsMask(check);
1212
1213                 // if the entity is standing on the pusher, it will definitely be moved
1214                 // if the entity is not standing on the pusher, but is in the pusher's
1215                 // final position, move it
1216                 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1217                 {
1218                         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);
1219                         //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1220                         if (!trace.startsolid)
1221                         {
1222                                 //Con_Printf("- not in solid\n");
1223                                 continue;
1224                         }
1225                 }
1226
1227                 if (rotated)
1228                 {
1229                         vec3_t org2;
1230                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1231                         org2[0] = DotProduct (org, forward);
1232                         org2[1] = DotProduct (org, left);
1233                         org2[2] = DotProduct (org, up);
1234                         VectorSubtract (org2, org, move);
1235                         VectorAdd (move, move1, move);
1236                 }
1237                 else
1238                         VectorCopy (move1, move);
1239
1240                 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1241
1242                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1243                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1244                 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1245
1246                 // try moving the contacted entity
1247                 pusher->fields.server->solid = SOLID_NOT;
1248                 trace = SV_PushEntity (check, move, true);
1249                 // FIXME: turn players specially
1250                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1251                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1252                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1253
1254                 // this trace.fraction < 1 check causes items to fall off of pushers
1255                 // if they pass under or through a wall
1256                 // the groundentity check causes items to fall off of ledges
1257                 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1258                         check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1259
1260                 // if it is still inside the pusher, block
1261                 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);
1262                 if (trace.startsolid)
1263                 {
1264                         // try moving the contacted entity a tiny bit further to account for precision errors
1265                         vec3_t move2;
1266                         pusher->fields.server->solid = SOLID_NOT;
1267                         VectorScale(move, 1.1, move2);
1268                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1269                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1270                         SV_PushEntity (check, move2, true);
1271                         pusher->fields.server->solid = savesolid;
1272                         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);
1273                         if (trace.startsolid)
1274                         {
1275                                 // try moving the contacted entity a tiny bit less to account for precision errors
1276                                 pusher->fields.server->solid = SOLID_NOT;
1277                                 VectorScale(move, 0.9, move2);
1278                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1279                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1280                                 SV_PushEntity (check, move2, true);
1281                                 pusher->fields.server->solid = savesolid;
1282                                 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);
1283                                 if (trace.startsolid)
1284                                 {
1285                                         // still inside pusher, so it's really blocked
1286
1287                                         // fail the move
1288                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1289                                                 continue;
1290                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1291                                         {
1292                                                 // corpse
1293                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1294                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1295                                                 continue;
1296                                         }
1297
1298                                         VectorCopy (pushorig, pusher->fields.server->origin);
1299                                         VectorCopy (pushang, pusher->fields.server->angles);
1300                                         pusher->fields.server->ltime = pushltime;
1301                                         SV_LinkEdict (pusher, false);
1302
1303                                         // move back any entities we already moved
1304                                         for (i = 0;i < num_moved;i++)
1305                                         {
1306                                                 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1307                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1308                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1309                                                 SV_LinkEdict (ed, false);
1310                                         }
1311
1312                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1313                                         if (pusher->fields.server->blocked)
1314                                         {
1315                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1316                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1317                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1318                                         }
1319                                         break;
1320                                 }
1321                         }
1322                 }
1323         }
1324         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1325         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1326         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1327 }
1328
1329 /*
1330 ================
1331 SV_Physics_Pusher
1332
1333 ================
1334 */
1335 void SV_Physics_Pusher (prvm_edict_t *ent)
1336 {
1337         float thinktime, oldltime, movetime;
1338
1339         oldltime = ent->fields.server->ltime;
1340
1341         thinktime = ent->fields.server->nextthink;
1342         if (thinktime < ent->fields.server->ltime + sv.frametime)
1343         {
1344                 movetime = thinktime - ent->fields.server->ltime;
1345                 if (movetime < 0)
1346                         movetime = 0;
1347         }
1348         else
1349                 movetime = sv.frametime;
1350
1351         if (movetime)
1352                 // advances ent->fields.server->ltime if not blocked
1353                 SV_PushMove (ent, movetime);
1354
1355         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1356         {
1357                 ent->fields.server->nextthink = 0;
1358                 prog->globals.server->time = sv.time;
1359                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1360                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1361                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1362         }
1363 }
1364
1365
1366 /*
1367 ===============================================================================
1368
1369 CLIENT MOVEMENT
1370
1371 ===============================================================================
1372 */
1373
1374 static float unstickoffsets[] =
1375 {
1376         -1,  0,  0,
1377          1,  0,  0,
1378          0, -1,  0,
1379          0,  1,  0,
1380         -1, -1,  0,
1381          1, -1,  0,
1382         -1,  1,  0,
1383          1,  1,  0,
1384          0,  0,  -1,
1385          0,  0,  1,
1386          0,  0,  -2,
1387          0,  0,  2,
1388          0,  0,  -3,
1389          0,  0,  3,
1390          0,  0,  -4,
1391          0,  0,  4,
1392          0,  0,  -5,
1393          0,  0,  5,
1394          0,  0,  -6,
1395          0,  0,  6,
1396          0,  0,  -7,
1397          0,  0,  7,
1398          0,  0,  -8,
1399          0,  0,  8,
1400          0,  0,  -9,
1401          0,  0,  9,
1402          0,  0,  -10,
1403          0,  0,  10,
1404          0,  0,  -11,
1405          0,  0,  11,
1406          0,  0,  -12,
1407          0,  0,  12,
1408          0,  0,  -13,
1409          0,  0,  13,
1410          0,  0,  -14,
1411          0,  0,  14,
1412          0,  0,  -15,
1413          0,  0,  15,
1414          0,  0,  -16,
1415          0,  0,  16,
1416          0,  0,  -17,
1417          0,  0,  17,
1418 };
1419
1420 /*
1421 =============
1422 SV_CheckStuck
1423
1424 This is a big hack to try and fix the rare case of getting stuck in the world
1425 clipping hull.
1426 =============
1427 */
1428 void SV_CheckStuck (prvm_edict_t *ent)
1429 {
1430         int i;
1431         vec3_t offset;
1432
1433         if (!SV_TestEntityPosition(ent, vec3_origin))
1434         {
1435                 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1436                 return;
1437         }
1438
1439         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1440         {
1441                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1442                 {
1443                         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]);
1444                         SV_LinkEdict (ent, true);
1445                         return;
1446                 }
1447         }
1448
1449         VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1450         if (!SV_TestEntityPosition(ent, offset))
1451         {
1452                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1453                 SV_LinkEdict (ent, true);
1454                 return;
1455         }
1456
1457         Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1458 }
1459
1460 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1461 {
1462         int i;
1463
1464         // if not stuck in a bmodel, just return
1465         if (!SV_TestEntityPosition(ent, vec3_origin))
1466                 return true;
1467
1468         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1469         {
1470                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1471                 {
1472                         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]);
1473                         SV_LinkEdict (ent, true);
1474                         return true;
1475                 }
1476         }
1477
1478         if (developer.integer >= 100)
1479                 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1480         return false;
1481 }
1482
1483
1484 /*
1485 =============
1486 SV_CheckWater
1487 =============
1488 */
1489 qboolean SV_CheckWater (prvm_edict_t *ent)
1490 {
1491         int cont;
1492         int nNativeContents;
1493         vec3_t point;
1494
1495         point[0] = ent->fields.server->origin[0];
1496         point[1] = ent->fields.server->origin[1];
1497         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1498
1499         // DRESK - Support for Entity Contents Transition Event
1500         // NOTE: Some logic needed to be slightly re-ordered
1501         // to not affect performance and allow for the feature.
1502
1503         // Acquire Super Contents Prior to Resets
1504         cont = SV_PointSuperContents(point);
1505         // Acquire Native Contents Here
1506         nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1507
1508         // DRESK - Support for Entity Contents Transition Event
1509         if(ent->fields.server->watertype)
1510                 // Entity did NOT Spawn; Check
1511                 SV_CheckContentsTransition(ent, nNativeContents);
1512
1513
1514         ent->fields.server->waterlevel = 0;
1515         ent->fields.server->watertype = CONTENTS_EMPTY;
1516         cont = SV_PointSuperContents(point);
1517         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1518         {
1519                 ent->fields.server->watertype = nNativeContents;
1520                 ent->fields.server->waterlevel = 1;
1521                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1522                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1523                 {
1524                         ent->fields.server->waterlevel = 2;
1525                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1526                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1527                                 ent->fields.server->waterlevel = 3;
1528                 }
1529         }
1530
1531         return ent->fields.server->waterlevel > 1;
1532 }
1533
1534 /*
1535 ============
1536 SV_WallFriction
1537
1538 ============
1539 */
1540 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1541 {
1542         float d, i;
1543         vec3_t forward, into, side;
1544
1545         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1546         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1547         {
1548                 // cut the tangential velocity
1549                 i = DotProduct (stepnormal, ent->fields.server->velocity);
1550                 VectorScale (stepnormal, i, into);
1551                 VectorSubtract (ent->fields.server->velocity, into, side);
1552                 ent->fields.server->velocity[0] = side[0] * (1 + d);
1553                 ent->fields.server->velocity[1] = side[1] * (1 + d);
1554         }
1555 }
1556
1557 #if 0
1558 /*
1559 =====================
1560 SV_TryUnstick
1561
1562 Player has come to a dead stop, possibly due to the problem with limited
1563 float precision at some angle joins in the BSP hull.
1564
1565 Try fixing by pushing one pixel in each direction.
1566
1567 This is a hack, but in the interest of good gameplay...
1568 ======================
1569 */
1570 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1571 {
1572         int i, clip;
1573         vec3_t oldorg, dir;
1574
1575         VectorCopy (ent->fields.server->origin, oldorg);
1576         VectorClear (dir);
1577
1578         for (i=0 ; i<8 ; i++)
1579         {
1580                 // try pushing a little in an axial direction
1581                 switch (i)
1582                 {
1583                         case 0: dir[0] = 2; dir[1] = 0; break;
1584                         case 1: dir[0] = 0; dir[1] = 2; break;
1585                         case 2: dir[0] = -2; dir[1] = 0; break;
1586                         case 3: dir[0] = 0; dir[1] = -2; break;
1587                         case 4: dir[0] = 2; dir[1] = 2; break;
1588                         case 5: dir[0] = -2; dir[1] = 2; break;
1589                         case 6: dir[0] = 2; dir[1] = -2; break;
1590                         case 7: dir[0] = -2; dir[1] = -2; break;
1591                 }
1592
1593                 SV_PushEntity (ent, dir, false);
1594
1595                 // retry the original move
1596                 ent->fields.server->velocity[0] = oldvel[0];
1597                 ent->fields.server->velocity[1] = oldvel[1];
1598                 ent->fields.server->velocity[2] = 0;
1599                 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1600
1601                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1602                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1603                 {
1604                         Con_DPrint("TryUnstick - success.\n");
1605                         return clip;
1606                 }
1607
1608                 // go back to the original pos and try again
1609                 VectorCopy (oldorg, ent->fields.server->origin);
1610         }
1611
1612         // still not moving
1613         VectorClear (ent->fields.server->velocity);
1614         Con_DPrint("TryUnstick - failure.\n");
1615         return 7;
1616 }
1617 #endif
1618
1619 /*
1620 =====================
1621 SV_WalkMove
1622
1623 Only used by players
1624 ======================
1625 */
1626 void SV_WalkMove (prvm_edict_t *ent)
1627 {
1628         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1629         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1630         trace_t downtrace;
1631
1632         // if frametime is 0 (due to client sending the same timestamp twice),
1633         // don't move
1634         if (sv.frametime <= 0)
1635                 return;
1636
1637         hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1638
1639         SV_CheckVelocity(ent);
1640
1641         // do a regular slide move unless it looks like you ran into a step
1642         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1643
1644         VectorCopy (ent->fields.server->origin, start_origin);
1645         VectorCopy (ent->fields.server->velocity, start_velocity);
1646
1647         clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1648
1649         // if the move did not hit the ground at any point, we're not on ground
1650         if (!(clip & 1))
1651                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1652
1653         SV_CheckVelocity(ent);
1654
1655         VectorCopy(ent->fields.server->origin, originalmove_origin);
1656         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1657         originalmove_clip = clip;
1658         originalmove_flags = (int)ent->fields.server->flags;
1659         originalmove_groundentity = ent->fields.server->groundentity;
1660
1661         if ((int)ent->fields.server->flags & FL_WATERJUMP)
1662                 return;
1663
1664         if (sv_nostep.integer)
1665                 return;
1666
1667         // if move didn't block on a step, return
1668         if (clip & 2)
1669         {
1670                 // if move was not trying to move into the step, return
1671                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1672                         return;
1673
1674                 if (ent->fields.server->movetype != MOVETYPE_FLY)
1675                 {
1676                         // return if gibbed by a trigger
1677                         if (ent->fields.server->movetype != MOVETYPE_WALK)
1678                                 return;
1679
1680                         // only step up while jumping if that is enabled
1681                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1682                                 if (!oldonground && ent->fields.server->waterlevel == 0)
1683                                         return;
1684                 }
1685
1686                 // try moving up and forward to go up a step
1687                 // back to start pos
1688                 VectorCopy (start_origin, ent->fields.server->origin);
1689                 VectorCopy (start_velocity, ent->fields.server->velocity);
1690
1691                 // move up
1692                 VectorClear (upmove);
1693                 upmove[2] = sv_stepheight.value;
1694                 // FIXME: don't link?
1695                 SV_PushEntity(ent, upmove, false);
1696
1697                 // move forward
1698                 ent->fields.server->velocity[2] = 0;
1699                 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1700                 ent->fields.server->velocity[2] += start_velocity[2];
1701
1702                 SV_CheckVelocity(ent);
1703
1704                 // check for stuckness, possibly due to the limited precision of floats
1705                 // in the clipping hulls
1706                 if (clip
1707                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1708                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1709                 {
1710                         //Con_Printf("wall\n");
1711                         // stepping up didn't make any progress, revert to original move
1712                         VectorCopy(originalmove_origin, ent->fields.server->origin);
1713                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1714                         //clip = originalmove_clip;
1715                         ent->fields.server->flags = originalmove_flags;
1716                         ent->fields.server->groundentity = originalmove_groundentity;
1717                         // now try to unstick if needed
1718                         //clip = SV_TryUnstick (ent, oldvel);
1719                         return;
1720                 }
1721
1722                 //Con_Printf("step - ");
1723
1724                 // extra friction based on view angle
1725                 if (clip & 2 && sv_wallfriction.integer)
1726                         SV_WallFriction (ent, stepnormal);
1727         }
1728         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1729         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))
1730                 return;
1731
1732         // move down
1733         VectorClear (downmove);
1734         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1735         // FIXME: don't link?
1736         downtrace = SV_PushEntity (ent, downmove, false);
1737
1738         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1739         {
1740                 // this has been disabled so that you can't jump when you are stepping
1741                 // up while already jumping (also known as the Quake2 double jump bug)
1742 #if 0
1743                 // LordHavoc: disabled this check so you can walk on monsters/players
1744                 //if (ent->fields.server->solid == SOLID_BSP)
1745                 {
1746                         //Con_Printf("onground\n");
1747                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
1748                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1749                 }
1750 #endif
1751         }
1752         else
1753         {
1754                 //Con_Printf("slope\n");
1755                 // if the push down didn't end up on good ground, use the move without
1756                 // the step up.  This happens near wall / slope combinations, and can
1757                 // cause the player to hop up higher on a slope too steep to climb
1758                 VectorCopy(originalmove_origin, ent->fields.server->origin);
1759                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1760                 //clip = originalmove_clip;
1761                 ent->fields.server->flags = originalmove_flags;
1762                 ent->fields.server->groundentity = originalmove_groundentity;
1763         }
1764
1765         SV_CheckVelocity(ent);
1766 }
1767
1768 //============================================================================
1769
1770 /*
1771 =============
1772 SV_Physics_Follow
1773
1774 Entities that are "stuck" to another entity
1775 =============
1776 */
1777 void SV_Physics_Follow (prvm_edict_t *ent)
1778 {
1779         vec3_t vf, vr, vu, angles, v;
1780         prvm_edict_t *e;
1781
1782         // regular thinking
1783         if (!SV_RunThink (ent))
1784                 return;
1785
1786         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1787         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1788         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])
1789         {
1790                 // quick case for no rotation
1791                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1792         }
1793         else
1794         {
1795                 angles[0] = -ent->fields.server->punchangle[0];
1796                 angles[1] =  ent->fields.server->punchangle[1];
1797                 angles[2] =  ent->fields.server->punchangle[2];
1798                 AngleVectors (angles, vf, vr, vu);
1799                 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];
1800                 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];
1801                 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];
1802                 angles[0] = -e->fields.server->angles[0];
1803                 angles[1] =  e->fields.server->angles[1];
1804                 angles[2] =  e->fields.server->angles[2];
1805                 AngleVectors (angles, vf, vr, vu);
1806                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1807                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1808                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1809         }
1810         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1811         SV_LinkEdict (ent, true);
1812 }
1813
1814 /*
1815 ==============================================================================
1816
1817 TOSS / BOUNCE
1818
1819 ==============================================================================
1820 */
1821
1822 /*
1823 =============
1824 SV_CheckWaterTransition
1825
1826 =============
1827 */
1828 void SV_CheckWaterTransition (prvm_edict_t *ent)
1829 {
1830         int cont;
1831         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1832         if (!ent->fields.server->watertype)
1833         {
1834                 // just spawned here
1835                 ent->fields.server->watertype = cont;
1836                 ent->fields.server->waterlevel = 1;
1837                 return;
1838         }
1839
1840         // DRESK - Support for Entity Contents Transition Event
1841         // NOTE: Call here BEFORE updating the watertype below,
1842         // and suppress watersplash sound if a valid function
1843         // call was made to allow for custom "splash" sounds.
1844         if( !SV_CheckContentsTransition(ent, cont) )
1845         { // Contents Transition Function Invalid; Potentially Play Water Sound
1846                 // check if the entity crossed into or out of water
1847                 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1848                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1849         }
1850
1851         if (cont <= CONTENTS_WATER)
1852         {
1853                 ent->fields.server->watertype = cont;
1854                 ent->fields.server->waterlevel = 1;
1855         }
1856         else
1857         {
1858                 ent->fields.server->watertype = CONTENTS_EMPTY;
1859                 ent->fields.server->waterlevel = 0;
1860         }
1861 }
1862
1863 /*
1864 =============
1865 SV_Physics_Toss
1866
1867 Toss, bounce, and fly movement.  When onground, do nothing.
1868 =============
1869 */
1870 void SV_Physics_Toss (prvm_edict_t *ent)
1871 {
1872         trace_t trace;
1873         vec3_t move;
1874
1875 // if onground, return without moving
1876         if ((int)ent->fields.server->flags & FL_ONGROUND)
1877         {
1878                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1879                 {
1880                         // don't stick to ground if onground and moving upward
1881                         ent->fields.server->flags -= FL_ONGROUND;
1882                 }
1883                 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1884                 {
1885                         // we can trust FL_ONGROUND if groundentity is world because it never moves
1886                         return;
1887                 }
1888                 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1889                 {
1890                         // if ent was supported by a brush model on previous frame,
1891                         // and groundentity is now freed, set groundentity to 0 (world)
1892                         // which leaves it suspended in the air
1893                         ent->fields.server->groundentity = 0;
1894                         return;
1895                 }
1896         }
1897         ent->priv.server->suspendedinairflag = false;
1898
1899         SV_CheckVelocity (ent);
1900
1901 // add gravity
1902         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1903                 SV_AddGravity (ent);
1904
1905 // move angles
1906         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1907
1908 // move origin
1909         VectorScale (ent->fields.server->velocity, sv.frametime, move);
1910         trace = SV_PushEntity (ent, move, true);
1911         if (ent->priv.server->free)
1912                 return;
1913         if (trace.bmodelstartsolid)
1914         {
1915                 // try to unstick the entity
1916                 SV_UnstickEntity(ent);
1917                 trace = SV_PushEntity (ent, move, false);
1918                 if (ent->priv.server->free)
1919                         return;
1920         }
1921
1922         if (trace.fraction < 1)
1923         {
1924                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1925                 {
1926                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1927                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1928                 }
1929                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1930                 {
1931                         float d;
1932                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1933                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1934                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
1935                         {
1936                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1937                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1938                                 {
1939                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1940                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1941                                         VectorClear (ent->fields.server->velocity);
1942                                         VectorClear (ent->fields.server->avelocity);
1943                                 }
1944                                 else
1945                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1946                         }
1947                         else
1948                         {
1949                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1950                                 {
1951                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1952                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1953                                         VectorClear (ent->fields.server->velocity);
1954                                         VectorClear (ent->fields.server->avelocity);
1955                                 }
1956                                 else
1957                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1958                         }
1959                 }
1960                 else
1961                 {
1962                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1963                         if (trace.plane.normal[2] > 0.7)
1964                         {
1965                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1966                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1967                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1968                                         ent->priv.server->suspendedinairflag = true;
1969                                 VectorClear (ent->fields.server->velocity);
1970                                 VectorClear (ent->fields.server->avelocity);
1971                         }
1972                         else
1973                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1974                 }
1975         }
1976
1977 // check for in water
1978         SV_CheckWaterTransition (ent);
1979 }
1980
1981 /*
1982 ===============================================================================
1983
1984 STEPPING MOVEMENT
1985
1986 ===============================================================================
1987 */
1988
1989 /*
1990 =============
1991 SV_Physics_Step
1992
1993 Monsters freefall when they don't have a ground entity, otherwise
1994 all movement is done with discrete steps.
1995
1996 This is also used for objects that have become still on the ground, but
1997 will fall if the floor is pulled out from under them.
1998 =============
1999 */
2000 void SV_Physics_Step (prvm_edict_t *ent)
2001 {
2002         int flags = (int)ent->fields.server->flags;
2003
2004         // DRESK
2005         // Backup Velocity in the event that movetypesteplandevent is called,
2006         // to provide a parameter with the entity's velocity at impact.
2007         prvm_eval_t *movetypesteplandevent;
2008         vec3_t backupVelocity;
2009         VectorCopy(ent->fields.server->velocity, backupVelocity);
2010         // don't fall at all if fly/swim
2011         if (!(flags & (FL_FLY | FL_SWIM)))
2012         {
2013                 if (flags & FL_ONGROUND)
2014                 {
2015                         // freefall if onground and moving upward
2016                         // freefall if not standing on a world surface (it may be a lift or trap door)
2017                         if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2018                         {
2019                                 ent->fields.server->flags -= FL_ONGROUND;
2020                                 SV_AddGravity(ent);
2021                                 SV_CheckVelocity(ent);
2022                                 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
2023                                 SV_LinkEdict(ent, true);
2024                                 ent->priv.server->waterposition_forceupdate = true;
2025                         }
2026                 }
2027                 else
2028                 {
2029                         // freefall if not onground
2030                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2031
2032                         SV_AddGravity(ent);
2033                         SV_CheckVelocity(ent);
2034                         SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
2035                         SV_LinkEdict(ent, true);
2036
2037                         // just hit ground
2038                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2039                         {
2040                                 // DRESK - Check for Entity Land Event Function
2041                                 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2042
2043                                 if(movetypesteplandevent->function)
2044                                 { // Valid Function; Execute
2045                                         // Prepare Parameters
2046                                                 // Assign Velocity at Impact
2047                                                 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2048                                                 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2049                                                 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2050                                                 // Assign Self
2051                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2052                                         // Execute VM Function
2053                                         PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2054                                 }
2055                                 else
2056                                 // Check for Engine Landing Sound
2057                                 if(sv_sound_land.string)
2058                                         SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2059                         }
2060                         ent->priv.server->waterposition_forceupdate = true;
2061                 }
2062         }
2063
2064 // regular thinking
2065         if (!SV_RunThink(ent))
2066                 return;
2067
2068         if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2069         {
2070                 ent->priv.server->waterposition_forceupdate = false;
2071                 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2072                 SV_CheckWaterTransition(ent);
2073         }
2074 }
2075
2076 //============================================================================
2077
2078 static void SV_Physics_Entity (prvm_edict_t *ent)
2079 {
2080         // don't run a move on newly spawned projectiles as it messes up movement
2081         // interpolation and rocket trails
2082         qboolean runmove = ent->priv.server->move;
2083         ent->priv.server->move = true;
2084         switch ((int) ent->fields.server->movetype)
2085         {
2086         case MOVETYPE_PUSH:
2087         case MOVETYPE_FAKEPUSH:
2088                 SV_Physics_Pusher (ent);
2089                 break;
2090         case MOVETYPE_NONE:
2091                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2092                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2093                         SV_RunThink (ent);
2094                 break;
2095         case MOVETYPE_FOLLOW:
2096                 SV_Physics_Follow (ent);
2097                 break;
2098         case MOVETYPE_NOCLIP:
2099                 if (SV_RunThink(ent))
2100                 {
2101                         SV_CheckWater(ent);
2102                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2103                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2104                 }
2105                 SV_LinkEdict(ent, false);
2106                 break;
2107         case MOVETYPE_STEP:
2108                 SV_Physics_Step (ent);
2109                 break;
2110         case MOVETYPE_WALK:
2111                 if (SV_RunThink (ent))
2112                 {
2113                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2114                                 SV_AddGravity (ent);
2115                         SV_CheckStuck (ent);
2116                         SV_WalkMove (ent);
2117                         SV_LinkEdict (ent, true);
2118                 }
2119                 break;
2120         case MOVETYPE_TOSS:
2121         case MOVETYPE_BOUNCE:
2122         case MOVETYPE_BOUNCEMISSILE:
2123         case MOVETYPE_FLYMISSILE:
2124         case MOVETYPE_FLY:
2125                 // regular thinking
2126                 if (SV_RunThink (ent) && (runmove || !sv_gameplayfix_delayprojectiles.integer))
2127                         SV_Physics_Toss (ent);
2128                 break;
2129         default:
2130                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2131                 break;
2132         }
2133 }
2134
2135 void SV_Physics_ClientMove(void)
2136 {
2137         prvm_edict_t *ent;
2138         ent = host_client->edict;
2139
2140         // call player physics, this needs the proper frametime
2141         prog->globals.server->frametime = sv.frametime;
2142         SV_ClientThink();
2143
2144         // call standard client pre-think, with frametime = 0
2145         prog->globals.server->time = sv.time;
2146         prog->globals.server->frametime = 0;
2147         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2148         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2149         prog->globals.server->frametime = sv.frametime;
2150
2151         // make sure the velocity is sane (not a NaN)
2152         SV_CheckVelocity(ent);
2153         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2154         // player_run/player_stand1 does not horribly malfunction if the
2155         // velocity becomes a number that is both == 0 and != 0
2156         // (sounds to me like NaN but to be absolutely safe...)
2157         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2158                 VectorClear(ent->fields.server->velocity);
2159
2160         // perform MOVETYPE_WALK behavior
2161         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2162                 SV_AddGravity (ent);
2163         SV_CheckStuck (ent);
2164         SV_WalkMove (ent);
2165
2166         SV_CheckVelocity (ent);
2167
2168         SV_LinkEdict (ent, true);
2169
2170         SV_CheckVelocity (ent);
2171
2172         // call standard player post-think, with frametime = 0
2173         prog->globals.server->time = sv.time;
2174         prog->globals.server->frametime = 0;
2175         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2176         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2177         prog->globals.server->frametime = sv.frametime;
2178
2179         if(ent->fields.server->fixangle)
2180         {
2181                 // angle fixing was requested by physics code...
2182                 // so store the current angles for later use
2183                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2184                 host_client->fixangle_angles_set = TRUE;
2185
2186                 // and clear fixangle for the next frame
2187                 ent->fields.server->fixangle = 0;
2188         }
2189 }
2190
2191 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2192 {
2193         // don't do physics on disconnected clients, FrikBot relies on this
2194         if (!host_client->spawned)
2195         {
2196                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2197                 return;
2198         }
2199
2200         // don't run physics here if running asynchronously
2201         if (host_client->clmovement_skipphysicsframes <= 0)
2202         {
2203                 SV_ClientThink();
2204                 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2205         }
2206
2207         // make sure the velocity is sane (not a NaN)
2208         SV_CheckVelocity(ent);
2209         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2210         // player_run/player_stand1 does not horribly malfunction if the
2211         // velocity becomes a number that is both == 0 and != 0
2212         // (sounds to me like NaN but to be absolutely safe...)
2213         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2214                 VectorClear(ent->fields.server->velocity);
2215
2216         // call standard client pre-think
2217         prog->globals.server->time = sv.time;
2218         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2219         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2220         SV_CheckVelocity (ent);
2221
2222         switch ((int) ent->fields.server->movetype)
2223         {
2224         case MOVETYPE_PUSH:
2225         case MOVETYPE_FAKEPUSH:
2226                 SV_Physics_Pusher (ent);
2227                 break;
2228         case MOVETYPE_NONE:
2229                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2230                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2231                         SV_RunThink (ent);
2232                 break;
2233         case MOVETYPE_FOLLOW:
2234                 SV_Physics_Follow (ent);
2235                 break;
2236         case MOVETYPE_NOCLIP:
2237                 SV_RunThink(ent);
2238                 SV_CheckWater(ent);
2239                 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2240                 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2241                 break;
2242         case MOVETYPE_STEP:
2243                 SV_Physics_Step (ent);
2244                 break;
2245         case MOVETYPE_WALK:
2246                 SV_RunThink (ent);
2247                 // don't run physics here if running asynchronously
2248                 if (host_client->clmovement_skipphysicsframes <= 0)
2249                 {
2250                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2251                                 SV_AddGravity (ent);
2252                         SV_CheckStuck (ent);
2253                         SV_WalkMove (ent);
2254                 }
2255                 break;
2256         case MOVETYPE_TOSS:
2257         case MOVETYPE_BOUNCE:
2258         case MOVETYPE_BOUNCEMISSILE:
2259         case MOVETYPE_FLYMISSILE:
2260                 // regular thinking
2261                 SV_RunThink (ent);
2262                 SV_Physics_Toss (ent);
2263                 break;
2264         case MOVETYPE_FLY:
2265                 SV_RunThink (ent);
2266                 SV_CheckWater (ent);
2267                 SV_WalkMove (ent);
2268                 break;
2269         default:
2270                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2271                 break;
2272         }
2273
2274         // decrement the countdown variable used to decide when to go back to
2275         // synchronous physics
2276         if (host_client->clmovement_skipphysicsframes > 0)
2277                 host_client->clmovement_skipphysicsframes--;
2278
2279         SV_CheckVelocity (ent);
2280
2281         SV_LinkEdict (ent, true);
2282
2283         SV_CheckVelocity (ent);
2284
2285         // call standard player post-think
2286         prog->globals.server->time = sv.time;
2287         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2288         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2289
2290         if(ent->fields.server->fixangle)
2291         {
2292                 // angle fixing was requested by physics code...
2293                 // so store the current angles for later use
2294                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2295                 host_client->fixangle_angles_set = TRUE;
2296
2297                 // and clear fixangle for the next frame
2298                 ent->fields.server->fixangle = 0;
2299         }
2300 }
2301
2302 /*
2303 ================
2304 SV_Physics
2305
2306 ================
2307 */
2308 void SV_Physics (void)
2309 {
2310         int i;
2311         prvm_edict_t *ent;
2312
2313 // let the progs know that a new frame has started
2314         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2315         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2316         prog->globals.server->time = sv.time;
2317         prog->globals.server->frametime = sv.frametime;
2318         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2319
2320 //
2321 // treat each object in turn
2322 //
2323
2324         // if force_retouch, relink all the entities
2325         if (prog->globals.server->force_retouch > 0)
2326                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2327                         if (!ent->priv.server->free)
2328                                 SV_LinkEdict (ent, true);       // force retouch even for stationary
2329
2330         // run physics on the client entities
2331         for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2332                 if (!ent->priv.server->free)
2333                                 SV_Physics_ClientEntity(ent);
2334
2335         // run physics on all the non-client entities
2336         if (!sv_freezenonclients.integer)
2337                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2338                         if (!ent->priv.server->free)
2339                                 SV_Physics_Entity(ent);
2340
2341         if (prog->globals.server->force_retouch > 0)
2342                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2343
2344         // LordHavoc: endframe support
2345         if (prog->funcoffsets.EndFrame)
2346         {
2347                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2348                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2349                 prog->globals.server->time = sv.time;
2350                 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2351         }
2352
2353         // decrement prog->num_edicts if the highest number entities died
2354         for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2355
2356         if (!sv_freezenonclients.integer)
2357                 sv.time += sv.frametime;
2358 }