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