]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
make SV_PushMove push by playerclip faces too (by using the right contents mask for...
[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, vec3_t offset)
463 {
464         vec3_t org;
465         trace_t trace;
466         VectorAdd(ent->fields.server->origin, offset, org);
467         trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
468         if (trace.startsupercontents & SUPERCONTENTS_SOLID)
469                 return true;
470         else
471         {
472                 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
473                 {
474                         // q1bsp/hlbsp use hulls and if the entity does not exactly match
475                         // a hull size it is incorrectly tested, so this code tries to
476                         // 'fix' it slightly...
477                         // FIXME: this breaks entities larger than the hull size
478                         int i;
479                         vec3_t v, m1, m2, s;
480                         VectorAdd(org, ent->fields.server->mins, m1);
481                         VectorAdd(org, ent->fields.server->maxs, m2);
482                         VectorSubtract(m2, m1, s);
483 #define EPSILON (1.0f / 32.0f)
484                         if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
485                         if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
486                         if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
487                         for (i = 0;i < 8;i++)
488                         {
489                                 v[0] = (i & 1) ? m2[0] : m1[0];
490                                 v[1] = (i & 2) ? m2[1] : m1[1];
491                                 v[2] = (i & 4) ? m2[2] : m1[2];
492                                 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
493                                         return true;
494                         }
495                 }
496         }
497         // if the trace found a better position for the entity, move it there
498         if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
499                 VectorCopy(trace.endpos, ent->fields.server->origin);
500         return false;
501 }
502
503 /*
504 ================
505 SV_CheckAllEnts
506 ================
507 */
508 void SV_CheckAllEnts (void)
509 {
510         int e;
511         prvm_edict_t *check;
512
513         // see if any solid entities are inside the final position
514         check = PRVM_NEXT_EDICT(prog->edicts);
515         for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
516         {
517                 if (check->priv.server->free)
518                         continue;
519                 if (check->fields.server->movetype == MOVETYPE_PUSH
520                  || check->fields.server->movetype == MOVETYPE_NONE
521                  || check->fields.server->movetype == MOVETYPE_FOLLOW
522                  || check->fields.server->movetype == MOVETYPE_NOCLIP)
523                         continue;
524
525                 if (SV_TestEntityPosition (check, vec3_origin))
526                         Con_Print("entity in invalid position\n");
527         }
528 }
529
530 // DRESK - Support for Entity Contents Transition Event
531 /*
532 ================
533 SV_CheckContentsTransition
534
535 returns true if entity had a valid contentstransition function call
536 ================
537 */
538 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
539 {
540         int bValidFunctionCall;
541         prvm_eval_t *contentstransition;
542
543         // Default Valid Function Call to False
544         bValidFunctionCall = false;
545
546         if(ent->fields.server->watertype != nContents)
547         { // Changed Contents
548                 // Acquire Contents Transition Function from QC
549                 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
550
551                 if(contentstransition->function)
552                 { // Valid Function; Execute
553                         // Assign Valid Function
554                         bValidFunctionCall = true;
555                         // Prepare Parameters (Original Contents, New Contents)
556                                 // Original Contents
557                                 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
558                                 // New Contents
559                                 PRVM_G_FLOAT(OFS_PARM1) = nContents;
560                         // Execute VM Function
561                         PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
562                 }
563         }
564
565         // Return if Function Call was Valid
566         return bValidFunctionCall;
567 }
568
569
570 /*
571 ================
572 SV_CheckVelocity
573 ================
574 */
575 void SV_CheckVelocity (prvm_edict_t *ent)
576 {
577         int i;
578         float wishspeed;
579
580 //
581 // bound velocity
582 //
583         for (i=0 ; i<3 ; i++)
584         {
585                 if (IS_NAN(ent->fields.server->velocity[i]))
586                 {
587                         Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
588                         ent->fields.server->velocity[i] = 0;
589                 }
590                 if (IS_NAN(ent->fields.server->origin[i]))
591                 {
592                         Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
593                         ent->fields.server->origin[i] = 0;
594                 }
595         }
596
597         // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
598         wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
599         if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
600         {
601                 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
602                 ent->fields.server->velocity[0] *= wishspeed;
603                 ent->fields.server->velocity[1] *= wishspeed;
604                 ent->fields.server->velocity[2] *= wishspeed;
605         }
606 }
607
608 /*
609 =============
610 SV_RunThink
611
612 Runs thinking code if time.  There is some play in the exact time the think
613 function will be called, because it is called before any movement is done
614 in a frame.  Not used for pushmove objects, because they must be exact.
615 Returns false if the entity removed itself.
616 =============
617 */
618 qboolean SV_RunThink (prvm_edict_t *ent)
619 {
620         float thinktime;
621
622         thinktime = ent->fields.server->nextthink;
623         if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
624                 return true;
625
626         // don't let things stay in the past.
627         // it is possible to start that way by a trigger with a local time.
628         if (thinktime < sv.time)
629                 thinktime = sv.time;
630
631         ent->fields.server->nextthink = 0;
632         prog->globals.server->time = thinktime;
633         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
634         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
635         PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
636         return !ent->priv.server->free;
637 }
638
639 /*
640 ==================
641 SV_Impact
642
643 Two entities have touched, so run their touch functions
644 ==================
645 */
646 extern void VM_SetTraceGlobals(const trace_t *trace);
647 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
648 {
649         int old_self, old_other;
650         prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
651         prvm_eval_t *val;
652
653         old_self = prog->globals.server->self;
654         old_other = prog->globals.server->other;
655
656         VM_SetTraceGlobals(trace);
657
658         prog->globals.server->time = sv.time;
659         if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
660         {
661                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
662                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
663                 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
664         }
665
666         if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
667         {
668                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
669                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
670                 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
671                 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
672                 prog->globals.server->trace_plane_dist = -trace->plane.dist;
673                 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
674                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
675                         val->_float = 0;
676                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
677                         val->_float = 0;
678                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
679                         val->_float = 0;
680                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
681                         val->string = 0;
682                 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
683         }
684
685         prog->globals.server->self = old_self;
686         prog->globals.server->other = old_other;
687 }
688
689
690 /*
691 ==================
692 ClipVelocity
693
694 Slide off of the impacting object
695 returns the blocked flags (1 = floor, 2 = step / wall)
696 ==================
697 */
698 #define STOP_EPSILON 0.1
699 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
700 {
701         int i;
702         float backoff;
703
704         backoff = -DotProduct (in, normal) * overbounce;
705         VectorMA(in, backoff, normal, out);
706
707         for (i = 0;i < 3;i++)
708                 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
709                         out[i] = 0;
710 }
711
712
713 /*
714 ============
715 SV_FlyMove
716
717 The basic solid body movement clip that slides along multiple planes
718 Returns the clipflags if the velocity was modified (hit something solid)
719 1 = floor
720 2 = wall / step
721 4 = dead stop
722 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
723 ============
724 */
725 // LordHavoc: increased from 5 to 32
726 #define MAX_CLIP_PLANES 32
727 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
728 {
729         int blocked, bumpcount;
730         int i, j, impact, numplanes;
731         float d, time_left;
732         vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
733         trace_t trace;
734         if (time <= 0)
735                 return 0;
736         blocked = 0;
737         VectorCopy(ent->fields.server->velocity, original_velocity);
738         VectorCopy(ent->fields.server->velocity, primal_velocity);
739         numplanes = 0;
740         time_left = time;
741         for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
742         {
743                 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
744                         break;
745
746                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
747                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
748 #if 0
749                 //if (trace.fraction < 0.002)
750                 {
751 #if 1
752                         vec3_t start;
753                         trace_t testtrace;
754                         VectorCopy(ent->fields.server->origin, start);
755                         start[2] += 3;//0.03125;
756                         VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
757                         end[2] += 3;//0.03125;
758                         testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
759                         if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
760                         {
761                                 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
762                                 trace = testtrace;
763                         }
764 #endif
765 #if 0
766                         //j = -1;
767                         for (i = 0;i < numplanes;i++)
768                         {
769                                 VectorCopy(ent->fields.server->origin, start);
770                                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
771                                 VectorMA(start, 3, planes[i], start);
772                                 VectorMA(end, 3, planes[i], end);
773                                 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
774                                 if (trace.fraction < testtrace.fraction)
775                                 {
776                                         trace = testtrace;
777                                         VectorCopy(start, ent->fields.server->origin);
778                                         //j = i;
779                                 }
780                         }
781                         //if (j >= 0)
782                         //      VectorAdd(ent->fields.server->origin, planes[j], start);
783 #endif
784                 }
785 #endif
786
787 #if 0
788                 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
789                 if (trace.fraction < 1)
790                         Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
791                 Con_Print("\n");
792 #endif
793
794 #if 0
795                 if (trace.bmodelstartsolid)
796                 {
797                         // LordHavoc: note: this code is what makes entities stick in place
798                         // if embedded in world only (you can walk through other objects if
799                         // stuck)
800                         // entity is trapped in another solid
801                         VectorClear(ent->fields.server->velocity);
802                         return 3;
803                 }
804 #endif
805
806                 // break if it moved the entire distance
807                 if (trace.fraction == 1)
808                 {
809                         VectorCopy(trace.endpos, ent->fields.server->origin);
810                         break;
811                 }
812
813                 if (!trace.ent)
814                 {
815                         Con_Printf ("SV_FlyMove: !trace.ent");
816                         trace.ent = prog->edicts;
817                 }
818
819                 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
820
821                 if (trace.plane.normal[2])
822                 {
823                         if (trace.plane.normal[2] > 0.7)
824                         {
825                                 // floor
826                                 blocked |= 1;
827                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
828                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
829                         }
830                 }
831                 else
832                 {
833                         // step
834                         blocked |= 2;
835                         // save the trace for player extrafriction
836                         if (stepnormal)
837                                 VectorCopy(trace.plane.normal, stepnormal);
838                 }
839
840                 if (trace.fraction >= 0.001)
841                 {
842                         // actually covered some distance
843                         VectorCopy(trace.endpos, ent->fields.server->origin);
844                         VectorCopy(ent->fields.server->velocity, original_velocity);
845                         numplanes = 0;
846                 }
847
848                 // run the impact function
849                 if (impact)
850                 {
851                         SV_Impact(ent, &trace);
852
853                         // break if removed by the impact function
854                         if (ent->priv.server->free)
855                                 break;
856                 }
857
858                 time_left *= 1 - trace.fraction;
859
860                 // clipped to another plane
861                 if (numplanes >= MAX_CLIP_PLANES)
862                 {
863                         // this shouldn't really happen
864                         VectorClear(ent->fields.server->velocity);
865                         blocked = 3;
866                         break;
867                 }
868
869                 /*
870                 for (i = 0;i < numplanes;i++)
871                         if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
872                                 break;
873                 if (i < numplanes)
874                 {
875                         VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
876                         continue;
877                 }
878                 */
879
880                 VectorCopy(trace.plane.normal, planes[numplanes]);
881                 numplanes++;
882
883                 if (sv_newflymove.integer)
884                         ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
885                 else
886                 {
887                         // modify original_velocity so it parallels all of the clip planes
888                         for (i = 0;i < numplanes;i++)
889                         {
890                                 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
891                                 for (j = 0;j < numplanes;j++)
892                                 {
893                                         if (j != i)
894                                         {
895                                                 // not ok
896                                                 if (DotProduct(new_velocity, planes[j]) < 0)
897                                                         break;
898                                         }
899                                 }
900                                 if (j == numplanes)
901                                         break;
902                         }
903
904                         if (i != numplanes)
905                         {
906                                 // go along this plane
907                                 VectorCopy(new_velocity, ent->fields.server->velocity);
908                         }
909                         else
910                         {
911                                 // go along the crease
912                                 if (numplanes != 2)
913                                 {
914                                         VectorClear(ent->fields.server->velocity);
915                                         blocked = 7;
916                                         break;
917                                 }
918                                 CrossProduct(planes[0], planes[1], dir);
919                                 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
920                                 VectorNormalize(dir);
921                                 d = DotProduct(dir, ent->fields.server->velocity);
922                                 VectorScale(dir, d, ent->fields.server->velocity);
923                         }
924                 }
925
926                 // if current velocity is against the original velocity,
927                 // stop dead to avoid tiny occilations in sloping corners
928                 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
929                 {
930                         VectorClear(ent->fields.server->velocity);
931                         break;
932                 }
933         }
934
935         //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2]);
936
937         /*
938         if ((blocked & 1) == 0 && bumpcount > 1)
939         {
940                 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
941                 // flag ONGROUND if there's ground under it
942                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
943         }
944         */
945
946         // LordHavoc: this came from QW and allows you to get out of water more easily
947         if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
948                 VectorCopy(primal_velocity, ent->fields.server->velocity);
949         return blocked;
950 }
951
952 /*
953 ============
954 SV_AddGravity
955
956 ============
957 */
958 void SV_AddGravity (prvm_edict_t *ent)
959 {
960         float ent_gravity;
961         prvm_eval_t *val;
962
963         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
964         if (val!=0 && val->_float)
965                 ent_gravity = val->_float;
966         else
967                 ent_gravity = 1.0;
968         ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
969 }
970
971
972 /*
973 ===============================================================================
974
975 PUSHMOVE
976
977 ===============================================================================
978 */
979
980 /*
981 ============
982 SV_PushEntity
983
984 Does not change the entities velocity at all
985 ============
986 */
987 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
988 {
989         int type;
990         trace_t trace;
991         vec3_t end;
992
993         VectorAdd (ent->fields.server->origin, push, end);
994
995         if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
996                 type = MOVE_MISSILE;
997         else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
998                 type = MOVE_NOMONSTERS; // only clip against bmodels
999         else
1000                 type = MOVE_NORMAL;
1001
1002         trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1003         if (trace.bmodelstartsolid && failonbmodelstartsolid)
1004                 return trace;
1005
1006         VectorCopy (trace.endpos, ent->fields.server->origin);
1007         SV_LinkEdict (ent, true);
1008
1009         if (ent->fields.server->solid >= SOLID_TRIGGER && trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
1010                 SV_Impact (ent, &trace);
1011         return trace;
1012 }
1013
1014
1015 /*
1016 ============
1017 SV_PushMove
1018
1019 ============
1020 */
1021 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1022 {
1023         int i, e, index;
1024         float savesolid, movetime2, pushltime;
1025         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1026         int num_moved;
1027         int numcheckentities;
1028         static prvm_edict_t *checkentities[MAX_EDICTS];
1029         model_t *pushermodel;
1030         trace_t trace;
1031         matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1032
1033         if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
1034         {
1035                 pusher->fields.server->ltime += movetime;
1036                 return;
1037         }
1038
1039         switch ((int) pusher->fields.server->solid)
1040         {
1041         // LordHavoc: valid pusher types
1042         case SOLID_BSP:
1043         case SOLID_BBOX:
1044         case SOLID_SLIDEBOX:
1045         case SOLID_CORPSE: // LordHavoc: this would be weird...
1046                 break;
1047         // LordHavoc: no collisions
1048         case SOLID_NOT:
1049         case SOLID_TRIGGER:
1050                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1051                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1052                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1053                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1054                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1055                 pusher->fields.server->ltime += movetime;
1056                 SV_LinkEdict (pusher, false);
1057                 return;
1058         default:
1059                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1060                 return;
1061         }
1062         index = (int) pusher->fields.server->modelindex;
1063         if (index < 1 || index >= MAX_MODELS)
1064         {
1065                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1066                 return;
1067         }
1068         pushermodel = sv.models[index];
1069
1070         movetime2 = movetime;
1071         VectorScale(pusher->fields.server->velocity, movetime2, move1);
1072         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1073         if (moveangle[0] || moveangle[2])
1074         {
1075                 for (i = 0;i < 3;i++)
1076                 {
1077                         if (move1[i] > 0)
1078                         {
1079                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1080                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1081                         }
1082                         else
1083                         {
1084                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1085                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1086                         }
1087                 }
1088         }
1089         else if (moveangle[1])
1090         {
1091                 for (i = 0;i < 3;i++)
1092                 {
1093                         if (move1[i] > 0)
1094                         {
1095                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1096                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1097                         }
1098                         else
1099                         {
1100                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1101                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1102                         }
1103                 }
1104         }
1105         else
1106         {
1107                 for (i = 0;i < 3;i++)
1108                 {
1109                         if (move1[i] > 0)
1110                         {
1111                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1112                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1113                         }
1114                         else
1115                         {
1116                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1117                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1118                         }
1119                 }
1120         }
1121
1122         VectorNegate (moveangle, a);
1123         AngleVectorsFLU (a, forward, left, up);
1124
1125         VectorCopy (pusher->fields.server->origin, pushorig);
1126         VectorCopy (pusher->fields.server->angles, pushang);
1127         pushltime = pusher->fields.server->ltime;
1128
1129 // move the pusher to its final position
1130
1131         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1132         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1133         pusher->fields.server->ltime += movetime;
1134         SV_LinkEdict (pusher, false);
1135
1136         pushermodel = NULL;
1137         if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1138                 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1139         Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, pusher->fields.server->origin[0], pusher->fields.server->origin[1], pusher->fields.server->origin[2], pusher->fields.server->angles[0], pusher->fields.server->angles[1], pusher->fields.server->angles[2], 1);
1140         Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1141
1142         savesolid = pusher->fields.server->solid;
1143
1144 // see if any solid entities are inside the final position
1145         num_moved = 0;
1146
1147         numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1148         for (e = 0;e < numcheckentities;e++)
1149         {
1150                 prvm_edict_t *check = checkentities[e];
1151                 int checkcontents = SV_GenericHitSuperContentsMask(check);
1152                 if (check->fields.server->movetype == MOVETYPE_NONE
1153                  || check->fields.server->movetype == MOVETYPE_PUSH
1154                  || check->fields.server->movetype == MOVETYPE_FOLLOW
1155                  || check->fields.server->movetype == MOVETYPE_NOCLIP
1156                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1157                         continue;
1158
1159                 // if the entity is standing on the pusher, it will definitely be moved
1160                 // if the entity is not standing on the pusher, but is in the pusher's
1161                 // final position, move it
1162                 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1163                 {
1164                         Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1165                         if (!trace.startsolid)
1166                                 continue;
1167                 }
1168
1169
1170                 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1171                 {
1172                         vec3_t org2;
1173                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1174                         org2[0] = DotProduct (org, forward);
1175                         org2[1] = DotProduct (org, left);
1176                         org2[2] = DotProduct (org, up);
1177                         VectorSubtract (org2, org, move);
1178                         VectorAdd (move, move1, move);
1179                 }
1180                 else
1181                         VectorCopy (move1, move);
1182
1183                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1184                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1185                 sv.moved_edicts[num_moved++] = check;
1186
1187                 // try moving the contacted entity
1188                 pusher->fields.server->solid = SOLID_NOT;
1189                 trace = SV_PushEntity (check, move, true);
1190                 // FIXME: turn players specially
1191                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1192                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1193                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1194
1195                 // this trace.fraction < 1 check causes items to fall off of pushers
1196                 // if they pass under or through a wall
1197                 // the groundentity check causes items to fall off of ledges
1198                 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1199                         check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1200
1201                 // if it is still inside the pusher, block
1202                 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1203                 if (trace.startsolid)
1204                 {
1205                         // try moving the contacted entity a tiny bit further to account for precision errors
1206                         vec3_t move2;
1207                         pusher->fields.server->solid = SOLID_NOT;
1208                         VectorScale(move, 1.1, move2);
1209                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1210                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1211                         SV_PushEntity (check, move2, true);
1212                         pusher->fields.server->solid = savesolid;
1213                         Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1214                         if (trace.startsolid)
1215                         {
1216                                 // try moving the contacted entity a tiny bit less to account for precision errors
1217                                 pusher->fields.server->solid = SOLID_NOT;
1218                                 VectorScale(move, 0.9, move2);
1219                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1220                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1221                                 SV_PushEntity (check, move2, true);
1222                                 pusher->fields.server->solid = savesolid;
1223                                 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1224                                 if (trace.startsolid)
1225                                 {
1226                                         // still inside pusher, so it's really blocked
1227
1228                                         // fail the move
1229                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1230                                                 continue;
1231                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1232                                         {
1233                                                 // corpse
1234                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1235                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1236                                                 continue;
1237                                         }
1238
1239                                         VectorCopy (pushorig, pusher->fields.server->origin);
1240                                         VectorCopy (pushang, pusher->fields.server->angles);
1241                                         pusher->fields.server->ltime = pushltime;
1242                                         SV_LinkEdict (pusher, false);
1243
1244                                         // move back any entities we already moved
1245                                         for (i = 0;i < num_moved;i++)
1246                                         {
1247                                                 prvm_edict_t *ed = sv.moved_edicts[i];
1248                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1249                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1250                                                 SV_LinkEdict (ed, false);
1251                                         }
1252
1253                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1254                                         if (pusher->fields.server->blocked)
1255                                         {
1256                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1257                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1258                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1259                                         }
1260                                         break;
1261                                 }
1262                         }
1263                 }
1264         }
1265         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1266         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1267         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1268 }
1269
1270 /*
1271 ================
1272 SV_Physics_Pusher
1273
1274 ================
1275 */
1276 void SV_Physics_Pusher (prvm_edict_t *ent)
1277 {
1278         float thinktime, oldltime, movetime;
1279
1280         oldltime = ent->fields.server->ltime;
1281
1282         thinktime = ent->fields.server->nextthink;
1283         if (thinktime < ent->fields.server->ltime + sv.frametime)
1284         {
1285                 movetime = thinktime - ent->fields.server->ltime;
1286                 if (movetime < 0)
1287                         movetime = 0;
1288         }
1289         else
1290                 movetime = sv.frametime;
1291
1292         if (movetime)
1293                 // advances ent->fields.server->ltime if not blocked
1294                 SV_PushMove (ent, movetime);
1295
1296         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1297         {
1298                 ent->fields.server->nextthink = 0;
1299                 prog->globals.server->time = sv.time;
1300                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1301                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1302                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1303         }
1304 }
1305
1306
1307 /*
1308 ===============================================================================
1309
1310 CLIENT MOVEMENT
1311
1312 ===============================================================================
1313 */
1314
1315 static float unstickoffsets[] =
1316 {
1317         -1,  0,  0,
1318          1,  0,  0,
1319          0, -1,  0,
1320          0,  1,  0,
1321         -1, -1,  0,
1322          1, -1,  0,
1323         -1,  1,  0,
1324          1,  1,  0,
1325          0,  0,  -1,
1326          0,  0,  1,
1327          0,  0,  2,
1328          0,  0,  3,
1329          0,  0,  4,
1330          0,  0,  5,
1331          0,  0,  6,
1332          0,  0,  7,
1333          0,  0,  8,
1334          0,  0,  9,
1335          0,  0,  10,
1336          0,  0,  11,
1337          0,  0,  12,
1338          0,  0,  13,
1339          0,  0,  14,
1340          0,  0,  15,
1341          0,  0,  16,
1342          0,  0,  17,
1343 };
1344
1345 /*
1346 =============
1347 SV_CheckStuck
1348
1349 This is a big hack to try and fix the rare case of getting stuck in the world
1350 clipping hull.
1351 =============
1352 */
1353 void SV_CheckStuck (prvm_edict_t *ent)
1354 {
1355         int i;
1356         vec3_t offset;
1357
1358         if (!SV_TestEntityPosition(ent, vec3_origin))
1359         {
1360                 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1361                 return;
1362         }
1363
1364         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1365         {
1366                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1367                 {
1368                         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]);
1369                         SV_LinkEdict (ent, true);
1370                         return;
1371                 }
1372         }
1373
1374         VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1375         if (!SV_TestEntityPosition(ent, offset))
1376         {
1377                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1378                 SV_LinkEdict (ent, true);
1379                 return;
1380         }
1381
1382         Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1383 }
1384
1385 static void SV_UnstickEntity (prvm_edict_t *ent)
1386 {
1387         int i;
1388
1389         // if not stuck in a bmodel, just return
1390         if (!SV_TestEntityPosition(ent, vec3_origin))
1391                 return;
1392
1393         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1394         {
1395                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1396                 {
1397                         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]);
1398                         SV_LinkEdict (ent, true);
1399                         return;
1400                 }
1401         }
1402
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 }