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