entity unsticking: verify if the new position REALLY is not in solid (should hide...
[divverent/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23
24 /*
25
26
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
28
29 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
30
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
37
38 solid_edge items only clip against bsp models.
39
40 */
41
42 #define MOVE_EPSILON    0.01
43
44 void SV_Physics_Toss (prvm_edict_t *ent);
45
46 /*
47 ===============================================================================
48
49 LINE TESTING IN HULLS
50
51 ===============================================================================
52 */
53
54 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
55 {
56         prvm_eval_t *val;
57         if (passedict)
58         {
59                 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
60                 if (val && val->_float)
61                         return (int)val->_float;
62                 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
63                 {
64                         if ((int)passedict->fields.server->flags & FL_MONSTER)
65                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
66                         else
67                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
68                 }
69                 else if (passedict->fields.server->solid == SOLID_CORPSE)
70                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
71                 else if (passedict->fields.server->solid == SOLID_TRIGGER)
72                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
73                 else
74                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
75         }
76         else
77                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
78 }
79
80 /*
81 ==================
82 SV_Move
83 ==================
84 */
85 #if COLLISIONPARANOID >= 1
86 trace_t SV_Move_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
87 #else
88 trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
89 #endif
90 {
91         vec3_t hullmins, hullmaxs;
92         int i, bodysupercontents;
93         int passedictprog;
94         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 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
770 ============
771 */
772 static float SV_Gravity (prvm_edict_t *ent);
773 // LordHavoc: increased from 5 to 32
774 #define MAX_CLIP_PLANES 32
775 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
776 {
777         int blocked, bumpcount;
778         int i, j, impact, numplanes;
779         float d, time_left, gravity;
780         vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
781         trace_t trace;
782         if (time <= 0)
783                 return 0;
784         gravity = 0;
785         if (applygravity)
786         {
787                 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
788                 {
789                         gravity = SV_Gravity(ent) * 0.5f;
790                         ent->fields.server->velocity[2] -= gravity;
791                 }
792                 else
793                 {
794                         applygravity = false;
795                         ent->fields.server->velocity[2] -= SV_Gravity(ent);
796                 }
797         }
798         blocked = 0;
799         VectorCopy(ent->fields.server->velocity, original_velocity);
800         VectorCopy(ent->fields.server->velocity, primal_velocity);
801         numplanes = 0;
802         time_left = time;
803         for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
804         {
805                 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
806                         break;
807
808                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
809                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
810 #if 0
811                 //if (trace.fraction < 0.002)
812                 {
813 #if 1
814                         vec3_t start;
815                         trace_t testtrace;
816                         VectorCopy(ent->fields.server->origin, start);
817                         start[2] += 3;//0.03125;
818                         VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
819                         end[2] += 3;//0.03125;
820                         testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
821                         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)))
822                         {
823                                 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
824                                 trace = testtrace;
825                         }
826 #endif
827 #if 0
828                         //j = -1;
829                         for (i = 0;i < numplanes;i++)
830                         {
831                                 VectorCopy(ent->fields.server->origin, start);
832                                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
833                                 VectorMA(start, 3, planes[i], start);
834                                 VectorMA(end, 3, planes[i], end);
835                                 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
836                                 if (trace.fraction < testtrace.fraction)
837                                 {
838                                         trace = testtrace;
839                                         VectorCopy(start, ent->fields.server->origin);
840                                         //j = i;
841                                 }
842                         }
843                         //if (j >= 0)
844                         //      VectorAdd(ent->fields.server->origin, planes[j], start);
845 #endif
846                 }
847 #endif
848
849 #if 0
850                 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);
851                 if (trace.fraction < 1)
852                         Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
853                 Con_Print("\n");
854 #endif
855
856 #if 0
857                 if (trace.bmodelstartsolid)
858                 {
859                         // LordHavoc: note: this code is what makes entities stick in place
860                         // if embedded in world only (you can walk through other objects if
861                         // stuck)
862                         // entity is trapped in another solid
863                         VectorClear(ent->fields.server->velocity);
864                         return 3;
865                 }
866 #endif
867
868                 // break if it moved the entire distance
869                 if (trace.fraction == 1)
870                 {
871                         VectorCopy(trace.endpos, ent->fields.server->origin);
872                         break;
873                 }
874
875                 if (!trace.ent)
876                 {
877                         Con_Printf ("SV_FlyMove: !trace.ent");
878                         trace.ent = prog->edicts;
879                 }
880
881                 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
882
883                 if (trace.plane.normal[2])
884                 {
885                         if (trace.plane.normal[2] > 0.7)
886                         {
887                                 // floor
888                                 blocked |= 1;
889                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
890                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
891                         }
892                 }
893                 else
894                 {
895                         // step
896                         blocked |= 2;
897                         // save the trace for player extrafriction
898                         if (stepnormal)
899                                 VectorCopy(trace.plane.normal, stepnormal);
900                 }
901
902                 if (trace.fraction >= 0.001)
903                 {
904                         // actually covered some distance
905                         VectorCopy(trace.endpos, ent->fields.server->origin);
906                         VectorCopy(ent->fields.server->velocity, original_velocity);
907                         numplanes = 0;
908                 }
909
910                 // run the impact function
911                 if (impact)
912                 {
913                         SV_Impact(ent, &trace);
914
915                         // break if removed by the impact function
916                         if (ent->priv.server->free)
917                                 break;
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))
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)
1052 {
1053         int type;
1054         trace_t trace;
1055         vec3_t end;
1056
1057         VectorAdd (ent->fields.server->origin, push, end);
1058
1059         if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1060                 type = MOVE_MISSILE;
1061         else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1062                 type = MOVE_NOMONSTERS; // only clip against bmodels
1063         else
1064                 type = MOVE_NORMAL;
1065
1066         trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1067         if (trace.bmodelstartsolid && failonbmodelstartsolid)
1068                 return trace;
1069
1070         VectorCopy (trace.endpos, ent->fields.server->origin);
1071         SV_LinkEdict (ent, true);
1072
1073         if (ent->fields.server->solid >= SOLID_TRIGGER && trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
1074                 SV_Impact (ent, &trace);
1075         return trace;
1076 }
1077
1078
1079 /*
1080 ============
1081 SV_PushMove
1082
1083 ============
1084 */
1085 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1086 {
1087         int i, e, index;
1088         int pusherowner, pusherprog;
1089         int checkcontents;
1090         qboolean rotated;
1091         float savesolid, movetime2, pushltime;
1092         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1093         int num_moved;
1094         int numcheckentities;
1095         static prvm_edict_t *checkentities[MAX_EDICTS];
1096         dp_model_t *pushermodel;
1097         trace_t trace;
1098         matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1099         unsigned short moved_edicts[MAX_EDICTS];
1100
1101         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])
1102         {
1103                 pusher->fields.server->ltime += movetime;
1104                 return;
1105         }
1106
1107         switch ((int) pusher->fields.server->solid)
1108         {
1109         // LordHavoc: valid pusher types
1110         case SOLID_BSP:
1111         case SOLID_BBOX:
1112         case SOLID_SLIDEBOX:
1113         case SOLID_CORPSE: // LordHavoc: this would be weird...
1114                 break;
1115         // LordHavoc: no collisions
1116         case SOLID_NOT:
1117         case SOLID_TRIGGER:
1118                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1119                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1120                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1121                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1122                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1123                 pusher->fields.server->ltime += movetime;
1124                 SV_LinkEdict (pusher, false);
1125                 return;
1126         default:
1127                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1128                 return;
1129         }
1130         index = (int) pusher->fields.server->modelindex;
1131         if (index < 1 || index >= MAX_MODELS)
1132         {
1133                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1134                 return;
1135         }
1136         pushermodel = sv.models[index];
1137         pusherowner = pusher->fields.server->owner;
1138         pusherprog = PRVM_EDICT_TO_PROG(pusher);
1139
1140         rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1141
1142         movetime2 = movetime;
1143         VectorScale(pusher->fields.server->velocity, movetime2, move1);
1144         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1145         if (moveangle[0] || moveangle[2])
1146         {
1147                 for (i = 0;i < 3;i++)
1148                 {
1149                         if (move1[i] > 0)
1150                         {
1151                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1152                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1153                         }
1154                         else
1155                         {
1156                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1157                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1158                         }
1159                 }
1160         }
1161         else if (moveangle[1])
1162         {
1163                 for (i = 0;i < 3;i++)
1164                 {
1165                         if (move1[i] > 0)
1166                         {
1167                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1168                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1169                         }
1170                         else
1171                         {
1172                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1173                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1174                         }
1175                 }
1176         }
1177         else
1178         {
1179                 for (i = 0;i < 3;i++)
1180                 {
1181                         if (move1[i] > 0)
1182                         {
1183                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1184                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1185                         }
1186                         else
1187                         {
1188                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1189                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1190                         }
1191                 }
1192         }
1193
1194         VectorNegate (moveangle, a);
1195         AngleVectorsFLU (a, forward, left, up);
1196
1197         VectorCopy (pusher->fields.server->origin, pushorig);
1198         VectorCopy (pusher->fields.server->angles, pushang);
1199         pushltime = pusher->fields.server->ltime;
1200
1201 // move the pusher to its final position
1202
1203         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1204         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1205         pusher->fields.server->ltime += movetime;
1206         SV_LinkEdict (pusher, false);
1207
1208         pushermodel = NULL;
1209         if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1210                 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1211         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);
1212         Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1213
1214         savesolid = pusher->fields.server->solid;
1215
1216 // see if any solid entities are inside the final position
1217         num_moved = 0;
1218
1219         numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1220         for (e = 0;e < numcheckentities;e++)
1221         {
1222                 prvm_edict_t *check = checkentities[e];
1223                 if (check->fields.server->movetype == MOVETYPE_NONE
1224                  || check->fields.server->movetype == MOVETYPE_PUSH
1225                  || check->fields.server->movetype == MOVETYPE_FOLLOW
1226                  || check->fields.server->movetype == MOVETYPE_NOCLIP
1227                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1228                         continue;
1229
1230                 if (check->fields.server->owner == pusherprog)
1231                         continue;
1232
1233                 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1234                         continue;
1235
1236                 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1237
1238                 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1239                 check->priv.server->waterposition_forceupdate = true;
1240
1241                 checkcontents = SV_GenericHitSuperContentsMask(check);
1242
1243                 // if the entity is standing on the pusher, it will definitely be moved
1244                 // if the entity is not standing on the pusher, but is in the pusher's
1245                 // final position, move it
1246                 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1247                 {
1248                         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);
1249                         //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1250                         if (!trace.startsolid)
1251                         {
1252                                 //Con_Printf("- not in solid\n");
1253                                 continue;
1254                         }
1255                 }
1256
1257                 if (rotated)
1258                 {
1259                         vec3_t org2;
1260                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1261                         org2[0] = DotProduct (org, forward);
1262                         org2[1] = DotProduct (org, left);
1263                         org2[2] = DotProduct (org, up);
1264                         VectorSubtract (org2, org, move);
1265                         VectorAdd (move, move1, move);
1266                 }
1267                 else
1268                         VectorCopy (move1, move);
1269
1270                 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1271
1272                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1273                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1274                 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1275
1276                 // try moving the contacted entity
1277                 pusher->fields.server->solid = SOLID_NOT;
1278                 trace = SV_PushEntity (check, move, true);
1279                 // FIXME: turn players specially
1280                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1281                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1282                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1283
1284                 // this trace.fraction < 1 check causes items to fall off of pushers
1285                 // if they pass under or through a wall
1286                 // the groundentity check causes items to fall off of ledges
1287                 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1288                         check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1289
1290                 // if it is still inside the pusher, block
1291                 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);
1292                 if (trace.startsolid)
1293                 {
1294                         // try moving the contacted entity a tiny bit further to account for precision errors
1295                         vec3_t move2;
1296                         pusher->fields.server->solid = SOLID_NOT;
1297                         VectorScale(move, 1.1, move2);
1298                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1299                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1300                         SV_PushEntity (check, move2, true);
1301                         pusher->fields.server->solid = savesolid;
1302                         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);
1303                         if (trace.startsolid)
1304                         {
1305                                 // try moving the contacted entity a tiny bit less to account for precision errors
1306                                 pusher->fields.server->solid = SOLID_NOT;
1307                                 VectorScale(move, 0.9, move2);
1308                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1309                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1310                                 SV_PushEntity (check, move2, true);
1311                                 pusher->fields.server->solid = savesolid;
1312                                 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);
1313                                 if (trace.startsolid)
1314                                 {
1315                                         // still inside pusher, so it's really blocked
1316
1317                                         // fail the move
1318                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1319                                                 continue;
1320                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1321                                         {
1322                                                 // corpse
1323                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1324                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1325                                                 continue;
1326                                         }
1327
1328                                         VectorCopy (pushorig, pusher->fields.server->origin);
1329                                         VectorCopy (pushang, pusher->fields.server->angles);
1330                                         pusher->fields.server->ltime = pushltime;
1331                                         SV_LinkEdict (pusher, false);
1332
1333                                         // move back any entities we already moved
1334                                         for (i = 0;i < num_moved;i++)
1335                                         {
1336                                                 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1337                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1338                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1339                                                 SV_LinkEdict (ed, false);
1340                                         }
1341
1342                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1343                                         if (pusher->fields.server->blocked)
1344                                         {
1345                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1346                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1347                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1348                                         }
1349                                         break;
1350                                 }
1351                         }
1352                 }
1353         }
1354         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1355         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1356         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1357 }
1358
1359 /*
1360 ================
1361 SV_Physics_Pusher
1362
1363 ================
1364 */
1365 void SV_Physics_Pusher (prvm_edict_t *ent)
1366 {
1367         float thinktime, oldltime, movetime;
1368
1369         oldltime = ent->fields.server->ltime;
1370
1371         thinktime = ent->fields.server->nextthink;
1372         if (thinktime < ent->fields.server->ltime + sv.frametime)
1373         {
1374                 movetime = thinktime - ent->fields.server->ltime;
1375                 if (movetime < 0)
1376                         movetime = 0;
1377         }
1378         else
1379                 movetime = sv.frametime;
1380
1381         if (movetime)
1382                 // advances ent->fields.server->ltime if not blocked
1383                 SV_PushMove (ent, movetime);
1384
1385         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1386         {
1387                 ent->fields.server->nextthink = 0;
1388                 prog->globals.server->time = sv.time;
1389                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1390                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1391                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1392         }
1393 }
1394
1395
1396 /*
1397 ===============================================================================
1398
1399 CLIENT MOVEMENT
1400
1401 ===============================================================================
1402 */
1403
1404 static float unstickoffsets[] =
1405 {
1406         -1,  0,  0,
1407          1,  0,  0,
1408          0, -1,  0,
1409          0,  1,  0,
1410         -1, -1,  0,
1411          1, -1,  0,
1412         -1,  1,  0,
1413          1,  1,  0,
1414          0,  0,  -1,
1415          0,  0,  1,
1416          0,  0,  -2,
1417          0,  0,  2,
1418          0,  0,  -3,
1419          0,  0,  3,
1420          0,  0,  -4,
1421          0,  0,  4,
1422          0,  0,  -5,
1423          0,  0,  5,
1424          0,  0,  -6,
1425          0,  0,  6,
1426          0,  0,  -7,
1427          0,  0,  7,
1428          0,  0,  -8,
1429          0,  0,  8,
1430          0,  0,  -9,
1431          0,  0,  9,
1432          0,  0,  -10,
1433          0,  0,  10,
1434          0,  0,  -11,
1435          0,  0,  11,
1436          0,  0,  -12,
1437          0,  0,  12,
1438          0,  0,  -13,
1439          0,  0,  13,
1440          0,  0,  -14,
1441          0,  0,  14,
1442          0,  0,  -15,
1443          0,  0,  15,
1444          0,  0,  -16,
1445          0,  0,  16,
1446          0,  0,  -17,
1447          0,  0,  17,
1448 };
1449
1450 /*
1451 =============
1452 SV_CheckStuck
1453
1454 This is a big hack to try and fix the rare case of getting stuck in the world
1455 clipping hull.
1456 =============
1457 */
1458 void SV_CheckStuck (prvm_edict_t *ent)
1459 {
1460         int i;
1461         vec3_t offset;
1462
1463         if (!SV_TestEntityPosition(ent, vec3_origin))
1464         {
1465                 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1466                 return;
1467         }
1468
1469         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1470         {
1471                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1472                 {
1473                         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), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
1474                         SV_LinkEdict (ent, true);
1475                         return;
1476                 }
1477         }
1478
1479         VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1480         if (!SV_TestEntityPosition(ent, offset))
1481         {
1482                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1483                 SV_LinkEdict (ent, true);
1484                 return;
1485         }
1486
1487         Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1488 }
1489
1490 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1491 {
1492         int i;
1493
1494         // if not stuck in a bmodel, just return
1495         if (!SV_TestEntityPosition(ent, vec3_origin))
1496                 return true;
1497
1498         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1499         {
1500                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1501                 {
1502                         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), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
1503                         SV_LinkEdict (ent, true);
1504                         return true;
1505                 }
1506         }
1507
1508         if (developer.integer >= 100)
1509                 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1510         return false;
1511 }
1512
1513
1514 /*
1515 =============
1516 SV_CheckWater
1517 =============
1518 */
1519 qboolean SV_CheckWater (prvm_edict_t *ent)
1520 {
1521         int cont;
1522         int nNativeContents;
1523         vec3_t point;
1524
1525         point[0] = ent->fields.server->origin[0];
1526         point[1] = ent->fields.server->origin[1];
1527         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1528
1529         // DRESK - Support for Entity Contents Transition Event
1530         // NOTE: Some logic needed to be slightly re-ordered
1531         // to not affect performance and allow for the feature.
1532
1533         // Acquire Super Contents Prior to Resets
1534         cont = SV_PointSuperContents(point);
1535         // Acquire Native Contents Here
1536         nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1537
1538         // DRESK - Support for Entity Contents Transition Event
1539         if(ent->fields.server->watertype)
1540                 // Entity did NOT Spawn; Check
1541                 SV_CheckContentsTransition(ent, nNativeContents);
1542
1543
1544         ent->fields.server->waterlevel = 0;
1545         ent->fields.server->watertype = CONTENTS_EMPTY;
1546         cont = SV_PointSuperContents(point);
1547         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1548         {
1549                 ent->fields.server->watertype = nNativeContents;
1550                 ent->fields.server->waterlevel = 1;
1551                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1552                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1553                 {
1554                         ent->fields.server->waterlevel = 2;
1555                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1556                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1557                                 ent->fields.server->waterlevel = 3;
1558                 }
1559         }
1560
1561         return ent->fields.server->waterlevel > 1;
1562 }
1563
1564 /*
1565 ============
1566 SV_WallFriction
1567
1568 ============
1569 */
1570 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1571 {
1572         float d, i;
1573         vec3_t forward, into, side;
1574
1575         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1576         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1577         {
1578                 // cut the tangential velocity
1579                 i = DotProduct (stepnormal, ent->fields.server->velocity);
1580                 VectorScale (stepnormal, i, into);
1581                 VectorSubtract (ent->fields.server->velocity, into, side);
1582                 ent->fields.server->velocity[0] = side[0] * (1 + d);
1583                 ent->fields.server->velocity[1] = side[1] * (1 + d);
1584         }
1585 }
1586
1587 #if 0
1588 /*
1589 =====================
1590 SV_TryUnstick
1591
1592 Player has come to a dead stop, possibly due to the problem with limited
1593 float precision at some angle joins in the BSP hull.
1594
1595 Try fixing by pushing one pixel in each direction.
1596
1597 This is a hack, but in the interest of good gameplay...
1598 ======================
1599 */
1600 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1601 {
1602         int i, clip;
1603         vec3_t oldorg, dir;
1604
1605         VectorCopy (ent->fields.server->origin, oldorg);
1606         VectorClear (dir);
1607
1608         for (i=0 ; i<8 ; i++)
1609         {
1610                 // try pushing a little in an axial direction
1611                 switch (i)
1612                 {
1613                         case 0: dir[0] = 2; dir[1] = 0; break;
1614                         case 1: dir[0] = 0; dir[1] = 2; break;
1615                         case 2: dir[0] = -2; dir[1] = 0; break;
1616                         case 3: dir[0] = 0; dir[1] = -2; break;
1617                         case 4: dir[0] = 2; dir[1] = 2; break;
1618                         case 5: dir[0] = -2; dir[1] = 2; break;
1619                         case 6: dir[0] = 2; dir[1] = -2; break;
1620                         case 7: dir[0] = -2; dir[1] = -2; break;
1621                 }
1622
1623                 SV_PushEntity (ent, dir, false);
1624
1625                 // retry the original move
1626                 ent->fields.server->velocity[0] = oldvel[0];
1627                 ent->fields.server->velocity[1] = oldvel[1];
1628                 ent->fields.server->velocity[2] = 0;
1629                 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1630
1631                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1632                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1633                 {
1634                         Con_DPrint("TryUnstick - success.\n");
1635                         return clip;
1636                 }
1637
1638                 // go back to the original pos and try again
1639                 VectorCopy (oldorg, ent->fields.server->origin);
1640         }
1641
1642         // still not moving
1643         VectorClear (ent->fields.server->velocity);
1644         Con_DPrint("TryUnstick - failure.\n");
1645         return 7;
1646 }
1647 #endif
1648
1649 /*
1650 =====================
1651 SV_WalkMove
1652
1653 Only used by players
1654 ======================
1655 */
1656 void SV_WalkMove (prvm_edict_t *ent)
1657 {
1658         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1659         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1660         trace_t downtrace;
1661         qboolean applygravity;
1662
1663         // if frametime is 0 (due to client sending the same timestamp twice),
1664         // don't move
1665         if (sv.frametime <= 0)
1666                 return;
1667
1668         SV_CheckStuck (ent);
1669
1670         applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
1671
1672         hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1673
1674         SV_CheckVelocity(ent);
1675
1676         // do a regular slide move unless it looks like you ran into a step
1677         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1678
1679         VectorCopy (ent->fields.server->origin, start_origin);
1680         VectorCopy (ent->fields.server->velocity, start_velocity);
1681
1682         clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
1683
1684         // if the move did not hit the ground at any point, we're not on ground
1685         if (!(clip & 1))
1686                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1687
1688         SV_CheckVelocity(ent);
1689         SV_LinkEdict (ent, true);
1690
1691         VectorCopy(ent->fields.server->origin, originalmove_origin);
1692         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1693         originalmove_clip = clip;
1694         originalmove_flags = (int)ent->fields.server->flags;
1695         originalmove_groundentity = ent->fields.server->groundentity;
1696
1697         if ((int)ent->fields.server->flags & FL_WATERJUMP)
1698                 return;
1699
1700         if (sv_nostep.integer)
1701                 return;
1702
1703         // if move didn't block on a step, return
1704         if (clip & 2)
1705         {
1706                 // if move was not trying to move into the step, return
1707                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1708                         return;
1709
1710                 if (ent->fields.server->movetype != MOVETYPE_FLY)
1711                 {
1712                         // return if gibbed by a trigger
1713                         if (ent->fields.server->movetype != MOVETYPE_WALK)
1714                                 return;
1715
1716                         // only step up while jumping if that is enabled
1717                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1718                                 if (!oldonground && ent->fields.server->waterlevel == 0)
1719                                         return;
1720                 }
1721
1722                 // try moving up and forward to go up a step
1723                 // back to start pos
1724                 VectorCopy (start_origin, ent->fields.server->origin);
1725                 VectorCopy (start_velocity, ent->fields.server->velocity);
1726
1727                 // move up
1728                 VectorClear (upmove);
1729                 upmove[2] = sv_stepheight.value;
1730                 // FIXME: don't link?
1731                 SV_PushEntity(ent, upmove, false);
1732
1733                 // move forward
1734                 ent->fields.server->velocity[2] = 0;
1735                 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
1736                 ent->fields.server->velocity[2] += start_velocity[2];
1737
1738                 SV_CheckVelocity(ent);
1739                 SV_LinkEdict (ent, true);
1740
1741                 // check for stuckness, possibly due to the limited precision of floats
1742                 // in the clipping hulls
1743                 if (clip
1744                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1745                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1746                 {
1747                         //Con_Printf("wall\n");
1748                         // stepping up didn't make any progress, revert to original move
1749                         VectorCopy(originalmove_origin, ent->fields.server->origin);
1750                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1751                         //clip = originalmove_clip;
1752                         ent->fields.server->flags = originalmove_flags;
1753                         ent->fields.server->groundentity = originalmove_groundentity;
1754                         // now try to unstick if needed
1755                         //clip = SV_TryUnstick (ent, oldvel);
1756                         return;
1757                 }
1758
1759                 //Con_Printf("step - ");
1760
1761                 // extra friction based on view angle
1762                 if (clip & 2 && sv_wallfriction.integer)
1763                         SV_WallFriction (ent, stepnormal);
1764         }
1765         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1766         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))
1767                 return;
1768
1769         // move down
1770         VectorClear (downmove);
1771         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1772         // FIXME: don't link?
1773         downtrace = SV_PushEntity (ent, downmove, false);
1774
1775         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1776         {
1777                 // this has been disabled so that you can't jump when you are stepping
1778                 // up while already jumping (also known as the Quake2 double jump bug)
1779 #if 0
1780                 // LordHavoc: disabled this check so you can walk on monsters/players
1781                 //if (ent->fields.server->solid == SOLID_BSP)
1782                 {
1783                         //Con_Printf("onground\n");
1784                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
1785                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1786                 }
1787 #endif
1788         }
1789         else
1790         {
1791                 //Con_Printf("slope\n");
1792                 // if the push down didn't end up on good ground, use the move without
1793                 // the step up.  This happens near wall / slope combinations, and can
1794                 // cause the player to hop up higher on a slope too steep to climb
1795                 VectorCopy(originalmove_origin, ent->fields.server->origin);
1796                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1797                 //clip = originalmove_clip;
1798                 ent->fields.server->flags = originalmove_flags;
1799                 ent->fields.server->groundentity = originalmove_groundentity;
1800         }
1801
1802         SV_CheckVelocity(ent);
1803         SV_LinkEdict (ent, true);
1804 }
1805
1806 //============================================================================
1807
1808 /*
1809 =============
1810 SV_Physics_Follow
1811
1812 Entities that are "stuck" to another entity
1813 =============
1814 */
1815 void SV_Physics_Follow (prvm_edict_t *ent)
1816 {
1817         vec3_t vf, vr, vu, angles, v;
1818         prvm_edict_t *e;
1819
1820         // regular thinking
1821         if (!SV_RunThink (ent))
1822                 return;
1823
1824         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1825         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1826         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])
1827         {
1828                 // quick case for no rotation
1829                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1830         }
1831         else
1832         {
1833                 angles[0] = -ent->fields.server->punchangle[0];
1834                 angles[1] =  ent->fields.server->punchangle[1];
1835                 angles[2] =  ent->fields.server->punchangle[2];
1836                 AngleVectors (angles, vf, vr, vu);
1837                 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];
1838                 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];
1839                 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];
1840                 angles[0] = -e->fields.server->angles[0];
1841                 angles[1] =  e->fields.server->angles[1];
1842                 angles[2] =  e->fields.server->angles[2];
1843                 AngleVectors (angles, vf, vr, vu);
1844                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1845                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1846                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1847         }
1848         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1849         SV_LinkEdict (ent, true);
1850 }
1851
1852 /*
1853 ==============================================================================
1854
1855 TOSS / BOUNCE
1856
1857 ==============================================================================
1858 */
1859
1860 /*
1861 =============
1862 SV_CheckWaterTransition
1863
1864 =============
1865 */
1866 void SV_CheckWaterTransition (prvm_edict_t *ent)
1867 {
1868         int cont;
1869         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1870         if (!ent->fields.server->watertype)
1871         {
1872                 // just spawned here
1873                 ent->fields.server->watertype = cont;
1874                 ent->fields.server->waterlevel = 1;
1875                 return;
1876         }
1877
1878         // DRESK - Support for Entity Contents Transition Event
1879         // NOTE: Call here BEFORE updating the watertype below,
1880         // and suppress watersplash sound if a valid function
1881         // call was made to allow for custom "splash" sounds.
1882         if( !SV_CheckContentsTransition(ent, cont) )
1883         { // Contents Transition Function Invalid; Potentially Play Water Sound
1884                 // check if the entity crossed into or out of water
1885                 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1886                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1887         }
1888
1889         if (cont <= CONTENTS_WATER)
1890         {
1891                 ent->fields.server->watertype = cont;
1892                 ent->fields.server->waterlevel = 1;
1893         }
1894         else
1895         {
1896                 ent->fields.server->watertype = CONTENTS_EMPTY;
1897                 ent->fields.server->waterlevel = 0;
1898         }
1899 }
1900
1901 /*
1902 =============
1903 SV_Physics_Toss
1904
1905 Toss, bounce, and fly movement.  When onground, do nothing.
1906 =============
1907 */
1908 void SV_Physics_Toss (prvm_edict_t *ent)
1909 {
1910         trace_t trace;
1911         vec3_t move;
1912         vec_t movetime;
1913         int bump;
1914
1915 // if onground, return without moving
1916         if ((int)ent->fields.server->flags & FL_ONGROUND)
1917         {
1918                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1919                 {
1920                         // don't stick to ground if onground and moving upward
1921                         ent->fields.server->flags -= FL_ONGROUND;
1922                 }
1923                 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1924                 {
1925                         // we can trust FL_ONGROUND if groundentity is world because it never moves
1926                         return;
1927                 }
1928                 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1929                 {
1930                         // if ent was supported by a brush model on previous frame,
1931                         // and groundentity is now freed, set groundentity to 0 (world)
1932                         // which leaves it suspended in the air
1933                         ent->fields.server->groundentity = 0;
1934                         return;
1935                 }
1936         }
1937         ent->priv.server->suspendedinairflag = false;
1938
1939         SV_CheckVelocity (ent);
1940
1941 // add gravity
1942         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1943                 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1944
1945 // move angles
1946         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1947
1948         movetime = sv.frametime;
1949         for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
1950         {
1951         // move origin
1952                 VectorScale (ent->fields.server->velocity, movetime, move);
1953                 trace = SV_PushEntity (ent, move, true);
1954                 if (ent->priv.server->free)
1955                         return;
1956                 if (trace.bmodelstartsolid)
1957                 {
1958                         // try to unstick the entity
1959                         SV_UnstickEntity(ent);
1960                         trace = SV_PushEntity (ent, move, false);
1961                         if (ent->priv.server->free)
1962                                 return;
1963                 }
1964                 if (trace.fraction == 1)
1965                         break;
1966                 movetime *= 1 - min(1, trace.fraction);
1967                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1968                 {
1969                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1970                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1971                 }
1972                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1973                 {
1974                         float d, ent_gravity;
1975                         prvm_eval_t *val;
1976                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1977                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1978                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1979                         if (val!=0 && val->_float)
1980                                 ent_gravity = val->_float;
1981                         else
1982                                 ent_gravity = 1.0;
1983                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
1984                         {
1985                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1986                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * (60.0 / 800.0) * ent_gravity)
1987                                 {
1988                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1989                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1990                                         VectorClear (ent->fields.server->velocity);
1991                                         VectorClear (ent->fields.server->avelocity);
1992                                 }
1993                                 else
1994                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1995                         }
1996                         else
1997                         {
1998                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * (60.0 / 800.0) * ent_gravity)
1999                                 {
2000                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2001                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2002                                         VectorClear (ent->fields.server->velocity);
2003                                         VectorClear (ent->fields.server->avelocity);
2004                                 }
2005                                 else
2006                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2007                         }
2008                 }
2009                 else
2010                 {
2011                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2012                         if (trace.plane.normal[2] > 0.7)
2013                         {
2014                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2015                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2016                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2017                                         ent->priv.server->suspendedinairflag = true;
2018                                 VectorClear (ent->fields.server->velocity);
2019                                 VectorClear (ent->fields.server->avelocity);
2020                         }
2021                         else
2022                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2023                 }
2024                 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2025                         break;
2026         }
2027
2028 // check for in water
2029         SV_CheckWaterTransition (ent);
2030 }
2031
2032 /*
2033 ===============================================================================
2034
2035 STEPPING MOVEMENT
2036
2037 ===============================================================================
2038 */
2039
2040 /*
2041 =============
2042 SV_Physics_Step
2043
2044 Monsters freefall when they don't have a ground entity, otherwise
2045 all movement is done with discrete steps.
2046
2047 This is also used for objects that have become still on the ground, but
2048 will fall if the floor is pulled out from under them.
2049 =============
2050 */
2051 void SV_Physics_Step (prvm_edict_t *ent)
2052 {
2053         int flags = (int)ent->fields.server->flags;
2054
2055         // DRESK
2056         // Backup Velocity in the event that movetypesteplandevent is called,
2057         // to provide a parameter with the entity's velocity at impact.
2058         prvm_eval_t *movetypesteplandevent;
2059         vec3_t backupVelocity;
2060         VectorCopy(ent->fields.server->velocity, backupVelocity);
2061         // don't fall at all if fly/swim
2062         if (!(flags & (FL_FLY | FL_SWIM)))
2063         {
2064                 if (flags & FL_ONGROUND)
2065                 {
2066                         // freefall if onground and moving upward
2067                         // freefall if not standing on a world surface (it may be a lift or trap door)
2068                         if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2069                         {
2070                                 ent->fields.server->flags -= FL_ONGROUND;
2071                                 SV_CheckVelocity(ent);
2072                                 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2073                                 SV_LinkEdict(ent, true);
2074                                 ent->priv.server->waterposition_forceupdate = true;
2075                         }
2076                 }
2077                 else
2078                 {
2079                         // freefall if not onground
2080                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2081
2082                         SV_CheckVelocity(ent);
2083                         SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2084                         SV_LinkEdict(ent, true);
2085
2086                         // just hit ground
2087                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2088                         {
2089                                 // DRESK - Check for Entity Land Event Function
2090                                 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2091
2092                                 if(movetypesteplandevent->function)
2093                                 { // Valid Function; Execute
2094                                         // Prepare Parameters
2095                                                 // Assign Velocity at Impact
2096                                                 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2097                                                 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2098                                                 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2099                                                 // Assign Self
2100                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2101                                         // Execute VM Function
2102                                         PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2103                                 }
2104                                 else
2105                                 // Check for Engine Landing Sound
2106                                 if(sv_sound_land.string)
2107                                         SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2108                         }
2109                         ent->priv.server->waterposition_forceupdate = true;
2110                 }
2111         }
2112
2113 // regular thinking
2114         if (!SV_RunThink(ent))
2115                 return;
2116
2117         if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2118         {
2119                 ent->priv.server->waterposition_forceupdate = false;
2120                 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2121                 SV_CheckWaterTransition(ent);
2122         }
2123 }
2124
2125 //============================================================================
2126
2127 static void SV_Physics_Entity (prvm_edict_t *ent)
2128 {
2129         // don't run think/move on newly spawned projectiles as it messes up
2130         // movement interpolation and rocket trails, and is inconsistent with
2131         // respect to entities spawned in the same frame
2132         // (if an ent spawns a higher numbered ent, it moves in the same frame,
2133         //  but if it spawns a lower numbered ent, it doesn't - this never moves
2134         //  ents in the first frame regardless)
2135         qboolean runmove = ent->priv.server->move;
2136         ent->priv.server->move = true;
2137         if (!runmove && sv_gameplayfix_delayprojectiles.integer)
2138                 return;
2139         switch ((int) ent->fields.server->movetype)
2140         {
2141         case MOVETYPE_PUSH:
2142         case MOVETYPE_FAKEPUSH:
2143                 SV_Physics_Pusher (ent);
2144                 break;
2145         case MOVETYPE_NONE:
2146                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2147                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2148                         SV_RunThink (ent);
2149                 break;
2150         case MOVETYPE_FOLLOW:
2151                 SV_Physics_Follow (ent);
2152                 break;
2153         case MOVETYPE_NOCLIP:
2154                 if (SV_RunThink(ent))
2155                 {
2156                         SV_CheckWater(ent);
2157                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2158                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2159                 }
2160                 SV_LinkEdict(ent, false);
2161                 break;
2162         case MOVETYPE_STEP:
2163                 SV_Physics_Step (ent);
2164                 break;
2165         case MOVETYPE_WALK:
2166                 if (SV_RunThink (ent))
2167                         SV_WalkMove (ent);
2168                 break;
2169         case MOVETYPE_TOSS:
2170         case MOVETYPE_BOUNCE:
2171         case MOVETYPE_BOUNCEMISSILE:
2172         case MOVETYPE_FLYMISSILE:
2173         case MOVETYPE_FLY:
2174                 // regular thinking
2175                 if (SV_RunThink (ent))
2176                         SV_Physics_Toss (ent);
2177                 break;
2178         default:
2179                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2180                 break;
2181         }
2182 }
2183
2184 void SV_Physics_ClientMove(void)
2185 {
2186         prvm_edict_t *ent;
2187         ent = host_client->edict;
2188
2189         // call player physics, this needs the proper frametime
2190         prog->globals.server->frametime = sv.frametime;
2191         SV_ClientThink();
2192
2193         // call standard client pre-think, with frametime = 0
2194         prog->globals.server->time = sv.time;
2195         prog->globals.server->frametime = 0;
2196         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2197         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2198         prog->globals.server->frametime = sv.frametime;
2199
2200         // make sure the velocity is sane (not a NaN)
2201         SV_CheckVelocity(ent);
2202         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2203         // player_run/player_stand1 does not horribly malfunction if the
2204         // velocity becomes a number that is both == 0 and != 0
2205         // (sounds to me like NaN but to be absolutely safe...)
2206         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2207                 VectorClear(ent->fields.server->velocity);
2208
2209         // perform MOVETYPE_WALK behavior
2210         SV_WalkMove (ent);
2211
2212         // call standard player post-think, with frametime = 0
2213         prog->globals.server->time = sv.time;
2214         prog->globals.server->frametime = 0;
2215         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2216         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2217         prog->globals.server->frametime = sv.frametime;
2218
2219         if(ent->fields.server->fixangle)
2220         {
2221                 // angle fixing was requested by physics code...
2222                 // so store the current angles for later use
2223                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2224                 host_client->fixangle_angles_set = TRUE;
2225
2226                 // and clear fixangle for the next frame
2227                 ent->fields.server->fixangle = 0;
2228         }
2229 }
2230
2231 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2232 {
2233         // don't do physics on disconnected clients, FrikBot relies on this
2234         if (!host_client->spawned)
2235         {
2236                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2237                 return;
2238         }
2239
2240         // don't run physics here if running asynchronously
2241         if (host_client->clmovement_inputtimeout <= 0)
2242         {
2243                 SV_ClientThink();
2244                 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2245         }
2246
2247         // make sure the velocity is sane (not a NaN)
2248         SV_CheckVelocity(ent);
2249         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2250         // player_run/player_stand1 does not horribly malfunction if the
2251         // velocity becomes a number that is both == 0 and != 0
2252         // (sounds to me like NaN but to be absolutely safe...)
2253         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2254                 VectorClear(ent->fields.server->velocity);
2255
2256         // call standard client pre-think
2257         prog->globals.server->time = sv.time;
2258         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2259         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2260         SV_CheckVelocity (ent);
2261
2262         switch ((int) ent->fields.server->movetype)
2263         {
2264         case MOVETYPE_PUSH:
2265         case MOVETYPE_FAKEPUSH:
2266                 SV_Physics_Pusher (ent);
2267                 break;
2268         case MOVETYPE_NONE:
2269                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2270                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2271                         SV_RunThink (ent);
2272                 break;
2273         case MOVETYPE_FOLLOW:
2274                 SV_Physics_Follow (ent);
2275                 break;
2276         case MOVETYPE_NOCLIP:
2277                 SV_RunThink(ent);
2278                 SV_CheckWater(ent);
2279                 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2280                 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2281                 break;
2282         case MOVETYPE_STEP:
2283                 SV_Physics_Step (ent);
2284                 break;
2285         case MOVETYPE_WALK:
2286                 SV_RunThink (ent);
2287                 // don't run physics here if running asynchronously
2288                 if (host_client->clmovement_inputtimeout <= 0)
2289                         SV_WalkMove (ent);
2290                 break;
2291         case MOVETYPE_TOSS:
2292         case MOVETYPE_BOUNCE:
2293         case MOVETYPE_BOUNCEMISSILE:
2294         case MOVETYPE_FLYMISSILE:
2295                 // regular thinking
2296                 SV_RunThink (ent);
2297                 SV_Physics_Toss (ent);
2298                 break;
2299         case MOVETYPE_FLY:
2300                 SV_RunThink (ent);
2301                 SV_WalkMove (ent);
2302                 break;
2303         default:
2304                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2305                 break;
2306         }
2307
2308         // decrement the countdown variable used to decide when to go back to
2309         // synchronous physics
2310         if (host_client->clmovement_inputtimeout > sv.frametime)
2311                 host_client->clmovement_inputtimeout -= sv.frametime;
2312         else
2313                 host_client->clmovement_inputtimeout = 0;
2314
2315         SV_CheckVelocity (ent);
2316
2317         SV_LinkEdict (ent, true);
2318
2319         SV_CheckVelocity (ent);
2320
2321         // call standard player post-think
2322         prog->globals.server->time = sv.time;
2323         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2324         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2325
2326         if(ent->fields.server->fixangle)
2327         {
2328                 // angle fixing was requested by physics code...
2329                 // so store the current angles for later use
2330                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2331                 host_client->fixangle_angles_set = TRUE;
2332
2333                 // and clear fixangle for the next frame
2334                 ent->fields.server->fixangle = 0;
2335         }
2336 }
2337
2338 /*
2339 ================
2340 SV_Physics
2341
2342 ================
2343 */
2344 void SV_Physics (void)
2345 {
2346         int i;
2347         prvm_edict_t *ent;
2348
2349 // let the progs know that a new frame has started
2350         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2351         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2352         prog->globals.server->time = sv.time;
2353         prog->globals.server->frametime = sv.frametime;
2354         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2355
2356 //
2357 // treat each object in turn
2358 //
2359
2360         // if force_retouch, relink all the entities
2361         if (prog->globals.server->force_retouch > 0)
2362                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2363                         if (!ent->priv.server->free)
2364                                 SV_LinkEdict (ent, true);       // force retouch even for stationary
2365
2366         // run physics on the client entities
2367         for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2368                 if (!ent->priv.server->free)
2369                                 SV_Physics_ClientEntity(ent);
2370
2371         // run physics on all the non-client entities
2372         if (!sv_freezenonclients.integer)
2373                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2374                         if (!ent->priv.server->free)
2375                                 SV_Physics_Entity(ent);
2376
2377         if (prog->globals.server->force_retouch > 0)
2378                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2379
2380         // LordHavoc: endframe support
2381         if (prog->funcoffsets.EndFrame)
2382         {
2383                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2384                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2385                 prog->globals.server->time = sv.time;
2386                 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2387         }
2388
2389         // decrement prog->num_edicts if the highest number entities died
2390         for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2391
2392         if (!sv_freezenonclients.integer)
2393                 sv.time += sv.frametime;
2394 }