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