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