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