]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
rewrote server sendstates building code, no longer builds two
[xonotic/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23
24 /*
25
26
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
28
29 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
30
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
37
38 solid_edge items only clip against bsp models.
39
40 */
41
42 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 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         unsigned short moved_edicts[MAX_EDICTS];
1033
1034         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])
1035         {
1036                 pusher->fields.server->ltime += movetime;
1037                 return;
1038         }
1039
1040         switch ((int) pusher->fields.server->solid)
1041         {
1042         // LordHavoc: valid pusher types
1043         case SOLID_BSP:
1044         case SOLID_BBOX:
1045         case SOLID_SLIDEBOX:
1046         case SOLID_CORPSE: // LordHavoc: this would be weird...
1047                 break;
1048         // LordHavoc: no collisions
1049         case SOLID_NOT:
1050         case SOLID_TRIGGER:
1051                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1052                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1053                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1054                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1055                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1056                 pusher->fields.server->ltime += movetime;
1057                 SV_LinkEdict (pusher, false);
1058                 return;
1059         default:
1060                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1061                 return;
1062         }
1063         index = (int) pusher->fields.server->modelindex;
1064         if (index < 1 || index >= MAX_MODELS)
1065         {
1066                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1067                 return;
1068         }
1069         pushermodel = sv.models[index];
1070
1071         movetime2 = movetime;
1072         VectorScale(pusher->fields.server->velocity, movetime2, move1);
1073         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1074         if (moveangle[0] || moveangle[2])
1075         {
1076                 for (i = 0;i < 3;i++)
1077                 {
1078                         if (move1[i] > 0)
1079                         {
1080                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1081                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1082                         }
1083                         else
1084                         {
1085                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1086                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1087                         }
1088                 }
1089         }
1090         else if (moveangle[1])
1091         {
1092                 for (i = 0;i < 3;i++)
1093                 {
1094                         if (move1[i] > 0)
1095                         {
1096                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1097                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1098                         }
1099                         else
1100                         {
1101                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1102                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1103                         }
1104                 }
1105         }
1106         else
1107         {
1108                 for (i = 0;i < 3;i++)
1109                 {
1110                         if (move1[i] > 0)
1111                         {
1112                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1113                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1114                         }
1115                         else
1116                         {
1117                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1118                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1119                         }
1120                 }
1121         }
1122
1123         VectorNegate (moveangle, a);
1124         AngleVectorsFLU (a, forward, left, up);
1125
1126         VectorCopy (pusher->fields.server->origin, pushorig);
1127         VectorCopy (pusher->fields.server->angles, pushang);
1128         pushltime = pusher->fields.server->ltime;
1129
1130 // move the pusher to its final position
1131
1132         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1133         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1134         pusher->fields.server->ltime += movetime;
1135         SV_LinkEdict (pusher, false);
1136
1137         pushermodel = NULL;
1138         if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1139                 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1140         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);
1141         Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1142
1143         savesolid = pusher->fields.server->solid;
1144
1145 // see if any solid entities are inside the final position
1146         num_moved = 0;
1147
1148         numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1149         for (e = 0;e < numcheckentities;e++)
1150         {
1151                 prvm_edict_t *check = checkentities[e];
1152                 int checkcontents = SV_GenericHitSuperContentsMask(check);
1153                 if (check->fields.server->movetype == MOVETYPE_NONE
1154                  || check->fields.server->movetype == MOVETYPE_PUSH
1155                  || check->fields.server->movetype == MOVETYPE_FOLLOW
1156                  || check->fields.server->movetype == MOVETYPE_NOCLIP
1157                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1158                         continue;
1159
1160                 // if the entity is standing on the pusher, it will definitely be moved
1161                 // if the entity is not standing on the pusher, but is in the pusher's
1162                 // final position, move it
1163                 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1164                 {
1165                         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, checkcontents);
1166                         if (!trace.startsolid)
1167                                 continue;
1168                 }
1169
1170
1171                 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1172                 {
1173                         vec3_t org2;
1174                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1175                         org2[0] = DotProduct (org, forward);
1176                         org2[1] = DotProduct (org, left);
1177                         org2[2] = DotProduct (org, up);
1178                         VectorSubtract (org2, org, move);
1179                         VectorAdd (move, move1, move);
1180                 }
1181                 else
1182                         VectorCopy (move1, move);
1183
1184                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1185                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1186                 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1187
1188                 // try moving the contacted entity
1189                 pusher->fields.server->solid = SOLID_NOT;
1190                 trace = SV_PushEntity (check, move, true);
1191                 // FIXME: turn players specially
1192                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1193                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1194                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1195
1196                 // this trace.fraction < 1 check causes items to fall off of pushers
1197                 // if they pass under or through a wall
1198                 // the groundentity check causes items to fall off of ledges
1199                 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1200                         check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1201
1202                 // if it is still inside the pusher, block
1203                 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, checkcontents);
1204                 if (trace.startsolid)
1205                 {
1206                         // try moving the contacted entity a tiny bit further to account for precision errors
1207                         vec3_t move2;
1208                         pusher->fields.server->solid = SOLID_NOT;
1209                         VectorScale(move, 1.1, move2);
1210                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1211                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1212                         SV_PushEntity (check, move2, true);
1213                         pusher->fields.server->solid = savesolid;
1214                         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, checkcontents);
1215                         if (trace.startsolid)
1216                         {
1217                                 // try moving the contacted entity a tiny bit less to account for precision errors
1218                                 pusher->fields.server->solid = SOLID_NOT;
1219                                 VectorScale(move, 0.9, move2);
1220                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1221                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1222                                 SV_PushEntity (check, move2, true);
1223                                 pusher->fields.server->solid = savesolid;
1224                                 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, checkcontents);
1225                                 if (trace.startsolid)
1226                                 {
1227                                         // still inside pusher, so it's really blocked
1228
1229                                         // fail the move
1230                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1231                                                 continue;
1232                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1233                                         {
1234                                                 // corpse
1235                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1236                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1237                                                 continue;
1238                                         }
1239
1240                                         VectorCopy (pushorig, pusher->fields.server->origin);
1241                                         VectorCopy (pushang, pusher->fields.server->angles);
1242                                         pusher->fields.server->ltime = pushltime;
1243                                         SV_LinkEdict (pusher, false);
1244
1245                                         // move back any entities we already moved
1246                                         for (i = 0;i < num_moved;i++)
1247                                         {
1248                                                 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1249                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1250                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1251                                                 SV_LinkEdict (ed, false);
1252                                         }
1253
1254                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1255                                         if (pusher->fields.server->blocked)
1256                                         {
1257                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1258                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1259                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1260                                         }
1261                                         break;
1262                                 }
1263                         }
1264                 }
1265         }
1266         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1267         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1268         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1269 }
1270
1271 /*
1272 ================
1273 SV_Physics_Pusher
1274
1275 ================
1276 */
1277 void SV_Physics_Pusher (prvm_edict_t *ent)
1278 {
1279         float thinktime, oldltime, movetime;
1280
1281         oldltime = ent->fields.server->ltime;
1282
1283         thinktime = ent->fields.server->nextthink;
1284         if (thinktime < ent->fields.server->ltime + sv.frametime)
1285         {
1286                 movetime = thinktime - ent->fields.server->ltime;
1287                 if (movetime < 0)
1288                         movetime = 0;
1289         }
1290         else
1291                 movetime = sv.frametime;
1292
1293         if (movetime)
1294                 // advances ent->fields.server->ltime if not blocked
1295                 SV_PushMove (ent, movetime);
1296
1297         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1298         {
1299                 ent->fields.server->nextthink = 0;
1300                 prog->globals.server->time = sv.time;
1301                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1302                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1303                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1304         }
1305 }
1306
1307
1308 /*
1309 ===============================================================================
1310
1311 CLIENT MOVEMENT
1312
1313 ===============================================================================
1314 */
1315
1316 static float unstickoffsets[] =
1317 {
1318         -1,  0,  0,
1319          1,  0,  0,
1320          0, -1,  0,
1321          0,  1,  0,
1322         -1, -1,  0,
1323          1, -1,  0,
1324         -1,  1,  0,
1325          1,  1,  0,
1326          0,  0,  -1,
1327          0,  0,  1,
1328          0,  0,  2,
1329          0,  0,  3,
1330          0,  0,  4,
1331          0,  0,  5,
1332          0,  0,  6,
1333          0,  0,  7,
1334          0,  0,  8,
1335          0,  0,  9,
1336          0,  0,  10,
1337          0,  0,  11,
1338          0,  0,  12,
1339          0,  0,  13,
1340          0,  0,  14,
1341          0,  0,  15,
1342          0,  0,  16,
1343          0,  0,  17,
1344 };
1345
1346 /*
1347 =============
1348 SV_CheckStuck
1349
1350 This is a big hack to try and fix the rare case of getting stuck in the world
1351 clipping hull.
1352 =============
1353 */
1354 void SV_CheckStuck (prvm_edict_t *ent)
1355 {
1356         int i;
1357         vec3_t offset;
1358
1359         if (!SV_TestEntityPosition(ent, vec3_origin))
1360         {
1361                 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1362                 return;
1363         }
1364
1365         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1366         {
1367                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1368                 {
1369                         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]);
1370                         SV_LinkEdict (ent, true);
1371                         return;
1372                 }
1373         }
1374
1375         VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1376         if (!SV_TestEntityPosition(ent, offset))
1377         {
1378                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1379                 SV_LinkEdict (ent, true);
1380                 return;
1381         }
1382
1383         Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1384 }
1385
1386 static void SV_UnstickEntity (prvm_edict_t *ent)
1387 {
1388         int i;
1389
1390         // if not stuck in a bmodel, just return
1391         if (!SV_TestEntityPosition(ent, vec3_origin))
1392                 return;
1393
1394         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1395         {
1396                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1397                 {
1398                         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]);
1399                         SV_LinkEdict (ent, true);
1400                         return;
1401                 }
1402         }
1403
1404         if (developer.integer >= 100)
1405                 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1406 }
1407
1408
1409 /*
1410 =============
1411 SV_CheckWater
1412 =============
1413 */
1414 qboolean SV_CheckWater (prvm_edict_t *ent)
1415 {
1416         int cont;
1417         int nNativeContents;
1418         vec3_t point;
1419
1420         point[0] = ent->fields.server->origin[0];
1421         point[1] = ent->fields.server->origin[1];
1422         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1423
1424         // DRESK - Support for Entity Contents Transition Event
1425         // NOTE: Some logic needed to be slightly re-ordered
1426         // to not affect performance and allow for the feature.
1427
1428         // Acquire Super Contents Prior to Resets
1429         cont = SV_PointSuperContents(point);
1430         // Acquire Native Contents Here
1431         nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1432
1433         // DRESK - Support for Entity Contents Transition Event
1434         if(ent->fields.server->watertype)
1435                 // Entity did NOT Spawn; Check
1436                 SV_CheckContentsTransition(ent, nNativeContents);
1437
1438
1439         ent->fields.server->waterlevel = 0;
1440         ent->fields.server->watertype = CONTENTS_EMPTY;
1441         cont = SV_PointSuperContents(point);
1442         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1443         {
1444                 ent->fields.server->watertype = nNativeContents;
1445                 ent->fields.server->waterlevel = 1;
1446                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1447                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1448                 {
1449                         ent->fields.server->waterlevel = 2;
1450                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1451                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1452                                 ent->fields.server->waterlevel = 3;
1453                 }
1454         }
1455
1456         return ent->fields.server->waterlevel > 1;
1457 }
1458
1459 /*
1460 ============
1461 SV_WallFriction
1462
1463 ============
1464 */
1465 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1466 {
1467         float d, i;
1468         vec3_t forward, into, side;
1469
1470         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1471         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1472         {
1473                 // cut the tangential velocity
1474                 i = DotProduct (stepnormal, ent->fields.server->velocity);
1475                 VectorScale (stepnormal, i, into);
1476                 VectorSubtract (ent->fields.server->velocity, into, side);
1477                 ent->fields.server->velocity[0] = side[0] * (1 + d);
1478                 ent->fields.server->velocity[1] = side[1] * (1 + d);
1479         }
1480 }
1481
1482 #if 0
1483 /*
1484 =====================
1485 SV_TryUnstick
1486
1487 Player has come to a dead stop, possibly due to the problem with limited
1488 float precision at some angle joins in the BSP hull.
1489
1490 Try fixing by pushing one pixel in each direction.
1491
1492 This is a hack, but in the interest of good gameplay...
1493 ======================
1494 */
1495 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1496 {
1497         int i, clip;
1498         vec3_t oldorg, dir;
1499
1500         VectorCopy (ent->fields.server->origin, oldorg);
1501         VectorClear (dir);
1502
1503         for (i=0 ; i<8 ; i++)
1504         {
1505                 // try pushing a little in an axial direction
1506                 switch (i)
1507                 {
1508                         case 0: dir[0] = 2; dir[1] = 0; break;
1509                         case 1: dir[0] = 0; dir[1] = 2; break;
1510                         case 2: dir[0] = -2; dir[1] = 0; break;
1511                         case 3: dir[0] = 0; dir[1] = -2; break;
1512                         case 4: dir[0] = 2; dir[1] = 2; break;
1513                         case 5: dir[0] = -2; dir[1] = 2; break;
1514                         case 6: dir[0] = 2; dir[1] = -2; break;
1515                         case 7: dir[0] = -2; dir[1] = -2; break;
1516                 }
1517
1518                 SV_PushEntity (ent, dir, false);
1519
1520                 // retry the original move
1521                 ent->fields.server->velocity[0] = oldvel[0];
1522                 ent->fields.server->velocity[1] = oldvel[1];
1523                 ent->fields.server->velocity[2] = 0;
1524                 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1525
1526                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1527                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1528                 {
1529                         Con_DPrint("TryUnstick - success.\n");
1530                         return clip;
1531                 }
1532
1533                 // go back to the original pos and try again
1534                 VectorCopy (oldorg, ent->fields.server->origin);
1535         }
1536
1537         // still not moving
1538         VectorClear (ent->fields.server->velocity);
1539         Con_DPrint("TryUnstick - failure.\n");
1540         return 7;
1541 }
1542 #endif
1543
1544 /*
1545 =====================
1546 SV_WalkMove
1547
1548 Only used by players
1549 ======================
1550 */
1551 void SV_WalkMove (prvm_edict_t *ent)
1552 {
1553         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1554         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1555         trace_t downtrace;
1556
1557         // if frametime is 0 (due to client sending the same timestamp twice),
1558         // don't move
1559         if (sv.frametime <= 0)
1560                 return;
1561
1562         hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1563
1564         SV_CheckVelocity(ent);
1565
1566         // do a regular slide move unless it looks like you ran into a step
1567         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1568
1569         VectorCopy (ent->fields.server->origin, start_origin);
1570         VectorCopy (ent->fields.server->velocity, start_velocity);
1571
1572         clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1573
1574         // if the move did not hit the ground at any point, we're not on ground
1575         if (!(clip & 1))
1576                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1577
1578         SV_CheckVelocity(ent);
1579
1580         VectorCopy(ent->fields.server->origin, originalmove_origin);
1581         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1582         originalmove_clip = clip;
1583         originalmove_flags = (int)ent->fields.server->flags;
1584         originalmove_groundentity = ent->fields.server->groundentity;
1585
1586         if ((int)ent->fields.server->flags & FL_WATERJUMP)
1587                 return;
1588
1589         if (sv_nostep.integer)
1590                 return;
1591
1592         // if move didn't block on a step, return
1593         if (clip & 2)
1594         {
1595                 // if move was not trying to move into the step, return
1596                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1597                         return;
1598
1599                 if (ent->fields.server->movetype != MOVETYPE_FLY)
1600                 {
1601                         // return if gibbed by a trigger
1602                         if (ent->fields.server->movetype != MOVETYPE_WALK)
1603                                 return;
1604
1605                         // only step up while jumping if that is enabled
1606                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1607                                 if (!oldonground && ent->fields.server->waterlevel == 0)
1608                                         return;
1609                 }
1610
1611                 // try moving up and forward to go up a step
1612                 // back to start pos
1613                 VectorCopy (start_origin, ent->fields.server->origin);
1614                 VectorCopy (start_velocity, ent->fields.server->velocity);
1615
1616                 // move up
1617                 VectorClear (upmove);
1618                 upmove[2] = sv_stepheight.value;
1619                 // FIXME: don't link?
1620                 SV_PushEntity(ent, upmove, false);
1621
1622                 // move forward
1623                 ent->fields.server->velocity[2] = 0;
1624                 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1625                 ent->fields.server->velocity[2] += start_velocity[2];
1626
1627                 SV_CheckVelocity(ent);
1628
1629                 // check for stuckness, possibly due to the limited precision of floats
1630                 // in the clipping hulls
1631                 if (clip
1632                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1633                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1634                 {
1635                         //Con_Printf("wall\n");
1636                         // stepping up didn't make any progress, revert to original move
1637                         VectorCopy(originalmove_origin, ent->fields.server->origin);
1638                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1639                         //clip = originalmove_clip;
1640                         ent->fields.server->flags = originalmove_flags;
1641                         ent->fields.server->groundentity = originalmove_groundentity;
1642                         // now try to unstick if needed
1643                         //clip = SV_TryUnstick (ent, oldvel);
1644                         return;
1645                 }
1646
1647                 //Con_Printf("step - ");
1648
1649                 // extra friction based on view angle
1650                 if (clip & 2 && sv_wallfriction.integer)
1651                         SV_WallFriction (ent, stepnormal);
1652         }
1653         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1654         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))
1655                 return;
1656
1657         // move down
1658         VectorClear (downmove);
1659         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1660         // FIXME: don't link?
1661         downtrace = SV_PushEntity (ent, downmove, false);
1662
1663         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1664         {
1665                 // this has been disabled so that you can't jump when you are stepping
1666                 // up while already jumping (also known as the Quake2 double jump bug)
1667 #if 0
1668                 // LordHavoc: disabled this check so you can walk on monsters/players
1669                 //if (ent->fields.server->solid == SOLID_BSP)
1670                 {
1671                         //Con_Printf("onground\n");
1672                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
1673                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1674                 }
1675 #endif
1676         }
1677         else
1678         {
1679                 //Con_Printf("slope\n");
1680                 // if the push down didn't end up on good ground, use the move without
1681                 // the step up.  This happens near wall / slope combinations, and can
1682                 // cause the player to hop up higher on a slope too steep to climb
1683                 VectorCopy(originalmove_origin, ent->fields.server->origin);
1684                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1685                 //clip = originalmove_clip;
1686                 ent->fields.server->flags = originalmove_flags;
1687                 ent->fields.server->groundentity = originalmove_groundentity;
1688         }
1689
1690         SV_CheckVelocity(ent);
1691 }
1692
1693 //============================================================================
1694
1695 /*
1696 =============
1697 SV_Physics_Follow
1698
1699 Entities that are "stuck" to another entity
1700 =============
1701 */
1702 void SV_Physics_Follow (prvm_edict_t *ent)
1703 {
1704         vec3_t vf, vr, vu, angles, v;
1705         prvm_edict_t *e;
1706
1707         // regular thinking
1708         if (!SV_RunThink (ent))
1709                 return;
1710
1711         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1712         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1713         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])
1714         {
1715                 // quick case for no rotation
1716                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1717         }
1718         else
1719         {
1720                 angles[0] = -ent->fields.server->punchangle[0];
1721                 angles[1] =  ent->fields.server->punchangle[1];
1722                 angles[2] =  ent->fields.server->punchangle[2];
1723                 AngleVectors (angles, vf, vr, vu);
1724                 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];
1725                 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];
1726                 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];
1727                 angles[0] = -e->fields.server->angles[0];
1728                 angles[1] =  e->fields.server->angles[1];
1729                 angles[2] =  e->fields.server->angles[2];
1730                 AngleVectors (angles, vf, vr, vu);
1731                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1732                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1733                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1734         }
1735         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1736         SV_LinkEdict (ent, true);
1737 }
1738
1739 /*
1740 ==============================================================================
1741
1742 TOSS / BOUNCE
1743
1744 ==============================================================================
1745 */
1746
1747 /*
1748 =============
1749 SV_CheckWaterTransition
1750
1751 =============
1752 */
1753 void SV_CheckWaterTransition (prvm_edict_t *ent)
1754 {
1755         int cont;
1756         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1757         if (!ent->fields.server->watertype)
1758         {
1759                 // just spawned here
1760                 ent->fields.server->watertype = cont;
1761                 ent->fields.server->waterlevel = 1;
1762                 return;
1763         }
1764
1765         // DRESK - Support for Entity Contents Transition Event
1766         // NOTE: Call here BEFORE updating the watertype below,
1767         // and suppress watersplash sound if a valid function
1768         // call was made to allow for custom "splash" sounds.
1769         if( !SV_CheckContentsTransition(ent, cont) )
1770         { // Contents Transition Function Invalid; Potentially Play Water Sound
1771                 // check if the entity crossed into or out of water
1772                 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1773                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1774         }
1775
1776         if (cont <= CONTENTS_WATER)
1777         {
1778                 ent->fields.server->watertype = cont;
1779                 ent->fields.server->waterlevel = 1;
1780         }
1781         else
1782         {
1783                 ent->fields.server->watertype = CONTENTS_EMPTY;
1784                 ent->fields.server->waterlevel = 0;
1785         }
1786 }
1787
1788 /*
1789 =============
1790 SV_Physics_Toss
1791
1792 Toss, bounce, and fly movement.  When onground, do nothing.
1793 =============
1794 */
1795 void SV_Physics_Toss (prvm_edict_t *ent)
1796 {
1797         trace_t trace;
1798         vec3_t move;
1799
1800 // if onground, return without moving
1801         if ((int)ent->fields.server->flags & FL_ONGROUND)
1802         {
1803                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1804                 {
1805                         // don't stick to ground if onground and moving upward
1806                         ent->fields.server->flags -= FL_ONGROUND;
1807                 }
1808                 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1809                 {
1810                         // we can trust FL_ONGROUND if groundentity is world because it never moves
1811                         return;
1812                 }
1813                 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1814                 {
1815                         // if ent was supported by a brush model on previous frame,
1816                         // and groundentity is now freed, set groundentity to 0 (world)
1817                         // which leaves it suspended in the air
1818                         ent->fields.server->groundentity = 0;
1819                         return;
1820                 }
1821         }
1822         ent->priv.server->suspendedinairflag = false;
1823
1824         SV_CheckVelocity (ent);
1825
1826 // add gravity
1827         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1828                 SV_AddGravity (ent);
1829
1830 // move angles
1831         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1832
1833 // move origin
1834         VectorScale (ent->fields.server->velocity, sv.frametime, move);
1835         trace = SV_PushEntity (ent, move, true);
1836         if (ent->priv.server->free)
1837                 return;
1838         if (trace.bmodelstartsolid)
1839         {
1840                 // try to unstick the entity
1841                 SV_UnstickEntity(ent);
1842                 trace = SV_PushEntity (ent, move, false);
1843                 if (ent->priv.server->free)
1844                         return;
1845         }
1846
1847         if (trace.fraction < 1)
1848         {
1849                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1850                 {
1851                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1852                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1853                 }
1854                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1855                 {
1856                         float d;
1857                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1858                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1859                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
1860                         {
1861                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1862                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1863                                 {
1864                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1865                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1866                                         VectorClear (ent->fields.server->velocity);
1867                                         VectorClear (ent->fields.server->avelocity);
1868                                 }
1869                                 else
1870                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1871                         }
1872                         else
1873                         {
1874                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1875                                 {
1876                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1877                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1878                                         VectorClear (ent->fields.server->velocity);
1879                                         VectorClear (ent->fields.server->avelocity);
1880                                 }
1881                                 else
1882                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1883                         }
1884                 }
1885                 else
1886                 {
1887                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1888                         if (trace.plane.normal[2] > 0.7)
1889                         {
1890                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1891                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1892                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1893                                         ent->priv.server->suspendedinairflag = true;
1894                                 VectorClear (ent->fields.server->velocity);
1895                                 VectorClear (ent->fields.server->avelocity);
1896                         }
1897                         else
1898                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1899                 }
1900         }
1901
1902 // check for in water
1903         SV_CheckWaterTransition (ent);
1904 }
1905
1906 /*
1907 ===============================================================================
1908
1909 STEPPING MOVEMENT
1910
1911 ===============================================================================
1912 */
1913
1914 /*
1915 =============
1916 SV_Physics_Step
1917
1918 Monsters freefall when they don't have a ground entity, otherwise
1919 all movement is done with discrete steps.
1920
1921 This is also used for objects that have become still on the ground, but
1922 will fall if the floor is pulled out from under them.
1923 =============
1924 */
1925 void SV_Physics_Step (prvm_edict_t *ent)
1926 {
1927         int flags = (int)ent->fields.server->flags;
1928         // don't fall at all if fly/swim
1929         if (!(flags & (FL_FLY | FL_SWIM)))
1930         {
1931                 if (flags & FL_ONGROUND)
1932                 {
1933                         // freefall if onground and moving upward
1934                         // freefall if not standing on a world surface (it may be a lift or trap door)
1935                         if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1936                         {
1937                                 ent->fields.server->flags -= FL_ONGROUND;
1938                                 SV_AddGravity(ent);
1939                                 SV_CheckVelocity(ent);
1940                                 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1941                                 SV_LinkEdict(ent, true);
1942                         }
1943                 }
1944                 else
1945                 {
1946                         // freefall if not onground
1947                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1948
1949                         SV_AddGravity(ent);
1950                         SV_CheckVelocity(ent);
1951                         SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1952                         SV_LinkEdict(ent, true);
1953
1954                         // just hit ground
1955                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1956                                 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1957                 }
1958         }
1959
1960 // regular thinking
1961         SV_RunThink(ent);
1962
1963         SV_CheckWaterTransition(ent);
1964 }
1965
1966 //============================================================================
1967
1968 static void SV_Physics_Entity (prvm_edict_t *ent)
1969 {
1970         // don't run a move on newly spawned projectiles as it messes up movement
1971         // interpolation and rocket trails
1972         qboolean runmove = ent->priv.server->move;
1973         ent->priv.server->move = true;
1974         switch ((int) ent->fields.server->movetype)
1975         {
1976         case MOVETYPE_PUSH:
1977         case MOVETYPE_FAKEPUSH:
1978                 SV_Physics_Pusher (ent);
1979                 break;
1980         case MOVETYPE_NONE:
1981                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1982                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1983                         SV_RunThink (ent);
1984                 break;
1985         case MOVETYPE_FOLLOW:
1986                 SV_Physics_Follow (ent);
1987                 break;
1988         case MOVETYPE_NOCLIP:
1989                 if (SV_RunThink(ent))
1990                 {
1991                         SV_CheckWater(ent);
1992                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1993                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1994                 }
1995                 SV_LinkEdict(ent, false);
1996                 break;
1997         case MOVETYPE_STEP:
1998                 SV_Physics_Step (ent);
1999                 break;
2000         case MOVETYPE_WALK:
2001                 if (SV_RunThink (ent))
2002                 {
2003                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2004                                 SV_AddGravity (ent);
2005                         SV_CheckStuck (ent);
2006                         SV_WalkMove (ent);
2007                         SV_LinkEdict (ent, true);
2008                 }
2009                 break;
2010         case MOVETYPE_TOSS:
2011         case MOVETYPE_BOUNCE:
2012         case MOVETYPE_BOUNCEMISSILE:
2013         case MOVETYPE_FLYMISSILE:
2014         case MOVETYPE_FLY:
2015                 // regular thinking
2016                 if (SV_RunThink (ent) && runmove)
2017                         SV_Physics_Toss (ent);
2018                 break;
2019         default:
2020                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2021                 break;
2022         }
2023 }
2024
2025 void SV_Physics_ClientMove(void)
2026 {
2027         prvm_edict_t *ent;
2028         ent = host_client->edict;
2029
2030         // call player physics, this needs the proper frametime
2031         prog->globals.server->frametime = sv.frametime;
2032         SV_ClientThink();
2033
2034         // call standard client pre-think, with frametime = 0
2035         prog->globals.server->time = sv.time;
2036         prog->globals.server->frametime = 0;
2037         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2038         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2039         prog->globals.server->frametime = sv.frametime;
2040
2041         // make sure the velocity is sane (not a NaN)
2042         SV_CheckVelocity(ent);
2043         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2044         // player_run/player_stand1 does not horribly malfunction if the
2045         // velocity becomes a number that is both == 0 and != 0
2046         // (sounds to me like NaN but to be absolutely safe...)
2047         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2048                 VectorClear(ent->fields.server->velocity);
2049
2050         // perform MOVETYPE_WALK behavior
2051         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2052                 SV_AddGravity (ent);
2053         SV_CheckStuck (ent);
2054         SV_WalkMove (ent);
2055
2056         SV_CheckVelocity (ent);
2057
2058         SV_LinkEdict (ent, true);
2059
2060         SV_CheckVelocity (ent);
2061
2062         // call standard player post-think, with frametime = 0
2063         prog->globals.server->time = sv.time;
2064         prog->globals.server->frametime = 0;
2065         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2066         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2067         prog->globals.server->frametime = sv.frametime;
2068
2069         if(ent->fields.server->fixangle)
2070         {
2071                 // angle fixing was requested by physics code...
2072                 // so store the current angles for later use
2073                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2074                 host_client->fixangle_angles_set = TRUE;
2075
2076                 // and clear fixangle for the next frame
2077                 ent->fields.server->fixangle = 0;
2078         }
2079 }
2080
2081 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2082 {
2083         // don't do physics on disconnected clients, FrikBot relies on this
2084         if (!host_client->spawned)
2085         {
2086                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2087                 return;
2088         }
2089
2090         // don't run physics here if running asynchronously
2091         if (host_client->clmovement_skipphysicsframes <= 0)
2092                 SV_ClientThink();
2093
2094         // make sure the velocity is sane (not a NaN)
2095         SV_CheckVelocity(ent);
2096         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2097         // player_run/player_stand1 does not horribly malfunction if the
2098         // velocity becomes a number that is both == 0 and != 0
2099         // (sounds to me like NaN but to be absolutely safe...)
2100         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2101                 VectorClear(ent->fields.server->velocity);
2102
2103         // call standard client pre-think
2104         prog->globals.server->time = sv.time;
2105         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2106         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2107         SV_CheckVelocity (ent);
2108
2109         switch ((int) ent->fields.server->movetype)
2110         {
2111         case MOVETYPE_PUSH:
2112         case MOVETYPE_FAKEPUSH:
2113                 SV_Physics_Pusher (ent);
2114                 break;
2115         case MOVETYPE_NONE:
2116                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2117                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2118                         SV_RunThink (ent);
2119                 break;
2120         case MOVETYPE_FOLLOW:
2121                 SV_Physics_Follow (ent);
2122                 break;
2123         case MOVETYPE_NOCLIP:
2124                 SV_RunThink(ent);
2125                 SV_CheckWater(ent);
2126                 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2127                 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2128                 break;
2129         case MOVETYPE_STEP:
2130                 SV_Physics_Step (ent);
2131                 break;
2132         case MOVETYPE_WALK:
2133                 SV_RunThink (ent);
2134                 // don't run physics here if running asynchronously
2135                 if (host_client->clmovement_skipphysicsframes <= 0)
2136                 {
2137                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2138                                 SV_AddGravity (ent);
2139                         SV_CheckStuck (ent);
2140                         SV_WalkMove (ent);
2141                 }
2142                 break;
2143         case MOVETYPE_TOSS:
2144         case MOVETYPE_BOUNCE:
2145         case MOVETYPE_BOUNCEMISSILE:
2146         case MOVETYPE_FLYMISSILE:
2147                 // regular thinking
2148                 SV_RunThink (ent);
2149                 SV_Physics_Toss (ent);
2150                 break;
2151         case MOVETYPE_FLY:
2152                 SV_RunThink (ent);
2153                 SV_CheckWater (ent);
2154                 SV_WalkMove (ent);
2155                 break;
2156         default:
2157                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2158                 break;
2159         }
2160
2161         // decrement the countdown variable used to decide when to go back to
2162         // synchronous physics
2163         if (host_client->clmovement_skipphysicsframes > 0)
2164                 host_client->clmovement_skipphysicsframes--;
2165
2166         SV_CheckVelocity (ent);
2167
2168         SV_LinkEdict (ent, true);
2169
2170         SV_CheckVelocity (ent);
2171
2172         // call standard player post-think
2173         prog->globals.server->time = sv.time;
2174         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2175         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2176
2177         if(ent->fields.server->fixangle)
2178         {
2179                 // angle fixing was requested by physics code...
2180                 // so store the current angles for later use
2181                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2182                 host_client->fixangle_angles_set = TRUE;
2183
2184                 // and clear fixangle for the next frame
2185                 ent->fields.server->fixangle = 0;
2186         }
2187 }
2188
2189 /*
2190 ================
2191 SV_Physics
2192
2193 ================
2194 */
2195 void SV_Physics (void)
2196 {
2197         int i;
2198         prvm_edict_t *ent;
2199
2200 // let the progs know that a new frame has started
2201         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2202         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2203         prog->globals.server->time = sv.time;
2204         prog->globals.server->frametime = sv.frametime;
2205         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2206
2207 //
2208 // treat each object in turn
2209 //
2210
2211         // if force_retouch, relink all the entities
2212         if (prog->globals.server->force_retouch > 0)
2213                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2214                         if (!ent->priv.server->free)
2215                                 SV_LinkEdict (ent, true);       // force retouch even for stationary
2216
2217         // run physics on the client entities
2218         for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2219                 if (!ent->priv.server->free)
2220                                 SV_Physics_ClientEntity(ent);
2221
2222         // run physics on all the non-client entities
2223         if (!sv_freezenonclients.integer)
2224                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2225                         if (!ent->priv.server->free)
2226                                 SV_Physics_Entity(ent);
2227
2228         if (prog->globals.server->force_retouch > 0)
2229                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2230
2231         // LordHavoc: endframe support
2232         if (prog->funcoffsets.EndFrame)
2233         {
2234                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2235                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2236                 prog->globals.server->time = sv.time;
2237                 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2238         }
2239
2240         // decrement prog->num_edicts if the highest number entities died
2241         for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2242
2243         if (!sv_freezenonclients.integer)
2244                 sv.time += sv.frametime;
2245 }