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