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