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