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