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