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