change MAX_CLIP_PLANES from 32 to 5 (back to old value)
[divverent/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23
24 /*
25
26
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
28
29 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
30
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
37
38 solid_edge items only clip against bsp models.
39
40 */
41
42 #define MOVE_EPSILON    0.01
43
44 void SV_Physics_Toss (prvm_edict_t *ent);
45
46 /*
47 ===============================================================================
48
49 LINE TESTING IN HULLS
50
51 ===============================================================================
52 */
53
54 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
55 {
56         prvm_eval_t *val;
57         if (passedict)
58         {
59                 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
60                 if (val && val->_float)
61                         return (int)val->_float;
62                 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
63                 {
64                         if ((int)passedict->fields.server->flags & FL_MONSTER)
65                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
66                         else
67                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
68                 }
69                 else if (passedict->fields.server->solid == SOLID_CORPSE)
70                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
71                 else if (passedict->fields.server->solid == SOLID_TRIGGER)
72                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
73                 else
74                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
75         }
76         else
77                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
78 }
79
80 /*
81 ==================
82 SV_Move
83 ==================
84 */
85 #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 = 1;
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 #define MAX_CLIP_PLANES 5
825 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
826 {
827         int blocked, bumpcount;
828         int i, j, numplanes;
829         float d, time_left, gravity;
830         vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
831 #if 0
832         vec3_t end;
833 #endif
834         trace_t trace;
835         if (time <= 0)
836                 return 0;
837         gravity = 0;
838         if (applygravity)
839         {
840                 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
841                 {
842                         gravity = SV_Gravity(ent) * 0.5f;
843                         ent->fields.server->velocity[2] -= gravity;
844                 }
845                 else
846                 {
847                         applygravity = false;
848                         ent->fields.server->velocity[2] -= SV_Gravity(ent);
849                 }
850         }
851         blocked = 0;
852         VectorCopy(ent->fields.server->velocity, original_velocity);
853         VectorCopy(ent->fields.server->velocity, primal_velocity);
854         numplanes = 0;
855         time_left = time;
856         for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
857         {
858                 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
859                         break;
860
861                 VectorScale(ent->fields.server->velocity, time_left, push);
862 #if 0
863                 VectorAdd(ent->fields.server->origin, push, end);
864 #endif
865                 if(!SV_PushEntity(&trace, ent, push, false, false))
866                 {
867                         // we got teleported by a touch function
868                         // let's abort the move
869                         blocked |= 8;
870                         break;
871                 }
872
873 #if 0
874                 //if (trace.fraction < 0.002)
875                 {
876 #if 1
877                         vec3_t start;
878                         trace_t testtrace;
879                         VectorCopy(ent->fields.server->origin, start);
880                         start[2] += 3;//0.03125;
881                         VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
882                         end[2] += 3;//0.03125;
883                         testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
884                         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)))
885                         {
886                                 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
887                                 trace = testtrace;
888                         }
889 #endif
890 #if 0
891                         //j = -1;
892                         for (i = 0;i < numplanes;i++)
893                         {
894                                 VectorCopy(ent->fields.server->origin, start);
895                                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
896                                 VectorMA(start, 3, planes[i], start);
897                                 VectorMA(end, 3, planes[i], end);
898                                 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
899                                 if (trace.fraction < testtrace.fraction)
900                                 {
901                                         trace = testtrace;
902                                         VectorCopy(start, ent->fields.server->origin);
903                                         //j = i;
904                                 }
905                         }
906                         //if (j >= 0)
907                         //      VectorAdd(ent->fields.server->origin, planes[j], start);
908 #endif
909                 }
910 #endif
911
912 #if 0
913                 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);
914                 if (trace.fraction < 1)
915                         Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
916                 Con_Print("\n");
917 #endif
918
919 #if 0
920                 if (trace.bmodelstartsolid)
921                 {
922                         // LordHavoc: note: this code is what makes entities stick in place
923                         // if embedded in world only (you can walk through other objects if
924                         // stuck)
925                         // entity is trapped in another solid
926                         VectorClear(ent->fields.server->velocity);
927                         return 3;
928                 }
929 #endif
930
931                 if (trace.fraction == 1)
932                         break;
933                 if (trace.plane.normal[2])
934                 {
935                         if (trace.plane.normal[2] > 0.7)
936                         {
937                                 // floor
938                                 blocked |= 1;
939
940                                 if (!trace.ent)
941                                 {
942                                         Con_Printf ("SV_FlyMove: !trace.ent");
943                                         trace.ent = prog->edicts;
944                                 }
945
946                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
947                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
948                         }
949                 }
950                 else
951                 {
952                         // step
953                         blocked |= 2;
954                         // save the trace for player extrafriction
955                         if (stepnormal)
956                                 VectorCopy(trace.plane.normal, stepnormal);
957                 }
958                 if (trace.fraction >= 0.001)
959                 {
960                         // actually covered some distance
961                         VectorCopy(ent->fields.server->velocity, original_velocity);
962                         numplanes = 0;
963                 }
964
965                 time_left *= 1 - trace.fraction;
966
967                 // clipped to another plane
968                 if (numplanes >= MAX_CLIP_PLANES)
969                 {
970                         // this shouldn't really happen
971                         VectorClear(ent->fields.server->velocity);
972                         blocked = 3;
973                         break;
974                 }
975
976                 /*
977                 for (i = 0;i < numplanes;i++)
978                         if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
979                                 break;
980                 if (i < numplanes)
981                 {
982                         VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
983                         continue;
984                 }
985                 */
986
987                 VectorCopy(trace.plane.normal, planes[numplanes]);
988                 numplanes++;
989
990                 if (sv_newflymove.integer)
991                         ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
992                 else
993                 {
994                         // modify original_velocity so it parallels all of the clip planes
995                         for (i = 0;i < numplanes;i++)
996                         {
997                                 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
998                                 for (j = 0;j < numplanes;j++)
999                                 {
1000                                         if (j != i)
1001                                         {
1002                                                 // not ok
1003                                                 if (DotProduct(new_velocity, planes[j]) < 0)
1004                                                         break;
1005                                         }
1006                                 }
1007                                 if (j == numplanes)
1008                                         break;
1009                         }
1010
1011                         if (i != numplanes)
1012                         {
1013                                 // go along this plane
1014                                 VectorCopy(new_velocity, ent->fields.server->velocity);
1015                         }
1016                         else
1017                         {
1018                                 // go along the crease
1019                                 if (numplanes != 2)
1020                                 {
1021                                         VectorClear(ent->fields.server->velocity);
1022                                         blocked = 7;
1023                                         break;
1024                                 }
1025                                 CrossProduct(planes[0], planes[1], dir);
1026                                 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1027                                 VectorNormalize(dir);
1028                                 d = DotProduct(dir, ent->fields.server->velocity);
1029                                 VectorScale(dir, d, ent->fields.server->velocity);
1030                         }
1031                 }
1032
1033                 // if current velocity is against the original velocity,
1034                 // stop dead to avoid tiny occilations in sloping corners
1035                 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1036                 {
1037                         VectorClear(ent->fields.server->velocity);
1038                         break;
1039                 }
1040         }
1041
1042         //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]);
1043
1044         /*
1045         if ((blocked & 1) == 0 && bumpcount > 1)
1046         {
1047                 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1048                 // flag ONGROUND if there's ground under it
1049                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1050         }
1051         */
1052
1053         // LordHavoc: this came from QW and allows you to get out of water more easily
1054         if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1055                 VectorCopy(primal_velocity, ent->fields.server->velocity);
1056         if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1057                 ent->fields.server->velocity[2] -= gravity;
1058         return blocked;
1059 }
1060
1061 /*
1062 ============
1063 SV_Gravity
1064
1065 ============
1066 */
1067 static float SV_Gravity (prvm_edict_t *ent)
1068 {
1069         float ent_gravity;
1070         prvm_eval_t *val;
1071
1072         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1073         if (val!=0 && val->_float)
1074                 ent_gravity = val->_float;
1075         else
1076                 ent_gravity = 1.0;
1077         return ent_gravity * sv_gravity.value * sv.frametime;
1078 }
1079
1080
1081 /*
1082 ===============================================================================
1083
1084 PUSHMOVE
1085
1086 ===============================================================================
1087 */
1088
1089 /*
1090 ============
1091 SV_PushEntity
1092
1093 Does not change the entities velocity at all
1094 The trace struct is filled with the trace that has been done.
1095 Returns true if the push did not result in the entity being teleported by QC code.
1096 ============
1097 */
1098 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1099 {
1100         int type;
1101         vec3_t end;
1102         qboolean impact;
1103
1104         VectorAdd (ent->fields.server->origin, push, end);
1105
1106         if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1107                 type = MOVE_MISSILE;
1108         else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1109                 type = MOVE_NOMONSTERS; // only clip against bmodels
1110         else
1111                 type = MOVE_NORMAL;
1112
1113         *trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1114         if (trace->bmodelstartsolid && failonbmodelstartsolid)
1115                 return true;
1116
1117         VectorCopy (trace->endpos, ent->fields.server->origin);
1118
1119 #if 0
1120         if(!trace->startsolid)
1121         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)
1122         {
1123                 Con_Printf("something eeeeevil happened\n");
1124         }
1125 #endif
1126
1127         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)));
1128
1129         if(impact)
1130         {
1131                 SV_LinkEdict (ent, dolink);
1132                 return SV_Impact (ent, trace);
1133         }
1134         else if(dolink)
1135                 SV_LinkEdict (ent, true);
1136
1137         return true;
1138 }
1139
1140
1141 /*
1142 ============
1143 SV_PushMove
1144
1145 ============
1146 */
1147 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1148 {
1149         int i, e, index;
1150         int pusherowner, pusherprog;
1151         int checkcontents;
1152         qboolean rotated;
1153         float savesolid, movetime2, pushltime;
1154         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1155         int num_moved;
1156         int numcheckentities;
1157         static prvm_edict_t *checkentities[MAX_EDICTS];
1158         dp_model_t *pushermodel;
1159         trace_t trace, trace2;
1160         matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1161         unsigned short moved_edicts[MAX_EDICTS];
1162
1163         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])
1164         {
1165                 pusher->fields.server->ltime += movetime;
1166                 return;
1167         }
1168
1169         switch ((int) pusher->fields.server->solid)
1170         {
1171         // LordHavoc: valid pusher types
1172         case SOLID_BSP:
1173         case SOLID_BBOX:
1174         case SOLID_SLIDEBOX:
1175         case SOLID_CORPSE: // LordHavoc: this would be weird...
1176                 break;
1177         // LordHavoc: no collisions
1178         case SOLID_NOT:
1179         case SOLID_TRIGGER:
1180                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1181                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1182                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1183                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1184                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1185                 pusher->fields.server->ltime += movetime;
1186                 SV_LinkEdict (pusher, false);
1187                 return;
1188         default:
1189                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1190                 return;
1191         }
1192         index = (int) pusher->fields.server->modelindex;
1193         if (index < 1 || index >= MAX_MODELS)
1194         {
1195                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1196                 return;
1197         }
1198         pushermodel = sv.models[index];
1199         pusherowner = pusher->fields.server->owner;
1200         pusherprog = PRVM_EDICT_TO_PROG(pusher);
1201
1202         rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1203
1204         movetime2 = movetime;
1205         VectorScale(pusher->fields.server->velocity, movetime2, move1);
1206         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1207         if (moveangle[0] || moveangle[2])
1208         {
1209                 for (i = 0;i < 3;i++)
1210                 {
1211                         if (move1[i] > 0)
1212                         {
1213                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1214                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1215                         }
1216                         else
1217                         {
1218                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1219                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1220                         }
1221                 }
1222         }
1223         else if (moveangle[1])
1224         {
1225                 for (i = 0;i < 3;i++)
1226                 {
1227                         if (move1[i] > 0)
1228                         {
1229                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1230                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1231                         }
1232                         else
1233                         {
1234                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1235                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1236                         }
1237                 }
1238         }
1239         else
1240         {
1241                 for (i = 0;i < 3;i++)
1242                 {
1243                         if (move1[i] > 0)
1244                         {
1245                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1246                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1247                         }
1248                         else
1249                         {
1250                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1251                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1252                         }
1253                 }
1254         }
1255
1256         VectorNegate (moveangle, a);
1257         AngleVectorsFLU (a, forward, left, up);
1258
1259         VectorCopy (pusher->fields.server->origin, pushorig);
1260         VectorCopy (pusher->fields.server->angles, pushang);
1261         pushltime = pusher->fields.server->ltime;
1262
1263 // move the pusher to its final position
1264
1265         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1266         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1267         pusher->fields.server->ltime += movetime;
1268         SV_LinkEdict (pusher, false);
1269
1270         pushermodel = NULL;
1271         if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1272                 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1273         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);
1274         Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1275
1276         savesolid = pusher->fields.server->solid;
1277
1278 // see if any solid entities are inside the final position
1279         num_moved = 0;
1280
1281         numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1282         for (e = 0;e < numcheckentities;e++)
1283         {
1284                 prvm_edict_t *check = checkentities[e];
1285                 if (check->fields.server->movetype == MOVETYPE_NONE
1286                  || check->fields.server->movetype == MOVETYPE_PUSH
1287                  || check->fields.server->movetype == MOVETYPE_FOLLOW
1288                  || check->fields.server->movetype == MOVETYPE_NOCLIP
1289                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1290                         continue;
1291
1292                 if (check->fields.server->owner == pusherprog)
1293                         continue;
1294
1295                 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1296                         continue;
1297
1298                 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1299
1300                 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1301                 check->priv.server->waterposition_forceupdate = true;
1302
1303                 checkcontents = SV_GenericHitSuperContentsMask(check);
1304
1305                 // if the entity is standing on the pusher, it will definitely be moved
1306                 // if the entity is not standing on the pusher, but is in the pusher's
1307                 // final position, move it
1308                 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1309                 {
1310                         Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1311                         //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1312                         if (!trace.startsolid)
1313                         {
1314                                 //Con_Printf("- not in solid\n");
1315                                 continue;
1316                         }
1317                 }
1318
1319                 if (rotated)
1320                 {
1321                         vec3_t org2;
1322                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1323                         org2[0] = DotProduct (org, forward);
1324                         org2[1] = DotProduct (org, left);
1325                         org2[2] = DotProduct (org, up);
1326                         VectorSubtract (org2, org, move);
1327                         VectorAdd (move, move1, move);
1328                 }
1329                 else
1330                         VectorCopy (move1, move);
1331
1332                 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1333
1334                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1335                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1336                 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1337
1338                 // try moving the contacted entity
1339                 pusher->fields.server->solid = SOLID_NOT;
1340                 if(!SV_PushEntity (&trace, check, move, true, true))
1341                 {
1342                         // entity "check" got teleported
1343                         check->fields.server->angles[1] += trace.fraction * moveangle[1];
1344                         pusher->fields.server->solid = savesolid; // was SOLID_BSP
1345                         continue; // pushed enough
1346                 }
1347                 // FIXME: turn players specially
1348                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1349                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1350                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1351
1352                 // this trace.fraction < 1 check causes items to fall off of pushers
1353                 // if they pass under or through a wall
1354                 // the groundentity check causes items to fall off of ledges
1355                 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1356                         check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1357
1358                 // if it is still inside the pusher, block
1359                 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);
1360                 if (trace.startsolid)
1361                 {
1362                         // try moving the contacted entity a tiny bit further to account for precision errors
1363                         vec3_t move2;
1364                         pusher->fields.server->solid = SOLID_NOT;
1365                         VectorScale(move, 1.1, move2);
1366                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1367                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1368                         if(!SV_PushEntity (&trace2, check, move2, true, true))
1369                         {
1370                                 // entity "check" got teleported
1371                                 continue;
1372                         }
1373                         pusher->fields.server->solid = savesolid;
1374                         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);
1375                         if (trace.startsolid)
1376                         {
1377                                 // try moving the contacted entity a tiny bit less to account for precision errors
1378                                 pusher->fields.server->solid = SOLID_NOT;
1379                                 VectorScale(move, 0.9, move2);
1380                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1381                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1382                                 if(!SV_PushEntity (&trace2, check, move2, true, true))
1383                                 {
1384                                         // entity "check" got teleported
1385                                         continue;
1386                                 }
1387                                 pusher->fields.server->solid = savesolid;
1388                                 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);
1389                                 if (trace.startsolid)
1390                                 {
1391                                         // still inside pusher, so it's really blocked
1392
1393                                         // fail the move
1394                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1395                                                 continue;
1396                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1397                                         {
1398                                                 // corpse
1399                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1400                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1401                                                 continue;
1402                                         }
1403
1404                                         VectorCopy (pushorig, pusher->fields.server->origin);
1405                                         VectorCopy (pushang, pusher->fields.server->angles);
1406                                         pusher->fields.server->ltime = pushltime;
1407                                         SV_LinkEdict (pusher, false);
1408
1409                                         // move back any entities we already moved
1410                                         for (i = 0;i < num_moved;i++)
1411                                         {
1412                                                 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1413                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1414                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1415                                                 SV_LinkEdict (ed, false);
1416                                         }
1417
1418                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1419                                         if (pusher->fields.server->blocked)
1420                                         {
1421                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1422                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1423                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1424                                         }
1425                                         break;
1426                                 }
1427                         }
1428                 }
1429         }
1430         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1431         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1432         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1433 }
1434
1435 /*
1436 ================
1437 SV_Physics_Pusher
1438
1439 ================
1440 */
1441 void SV_Physics_Pusher (prvm_edict_t *ent)
1442 {
1443         float thinktime, oldltime, movetime;
1444
1445         oldltime = ent->fields.server->ltime;
1446
1447         thinktime = ent->fields.server->nextthink;
1448         if (thinktime < ent->fields.server->ltime + sv.frametime)
1449         {
1450                 movetime = thinktime - ent->fields.server->ltime;
1451                 if (movetime < 0)
1452                         movetime = 0;
1453         }
1454         else
1455                 movetime = sv.frametime;
1456
1457         if (movetime)
1458                 // advances ent->fields.server->ltime if not blocked
1459                 SV_PushMove (ent, movetime);
1460
1461         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1462         {
1463                 ent->fields.server->nextthink = 0;
1464                 prog->globals.server->time = sv.time;
1465                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1466                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1467                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1468         }
1469 }
1470
1471
1472 /*
1473 ===============================================================================
1474
1475 CLIENT MOVEMENT
1476
1477 ===============================================================================
1478 */
1479
1480 static float unstickoffsets[] =
1481 {
1482         // poutting -/+z changes first as they are least weird
1483          0,  0,  -1,
1484          0,  0,  1,
1485          // x or y changes
1486         -1,  0,  0,
1487          1,  0,  0,
1488          0, -1,  0,
1489          0,  1,  0,
1490          // x and y changes
1491         -1, -1,  0,
1492          1, -1,  0,
1493         -1,  1,  0,
1494          1,  1,  0,
1495 };
1496
1497 typedef enum unstickresult_e
1498 {
1499         UNSTICK_STUCK = 0,
1500         UNSTICK_GOOD = 1,
1501         UNSTICK_UNSTUCK = 2
1502 }
1503 unstickresult_t;
1504
1505 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1506 {
1507         int i, maxunstick;
1508
1509         // if not stuck in a bmodel, just return
1510         if (!SV_TestEntityPosition(ent, vec3_origin))
1511                 return UNSTICK_GOOD;
1512
1513         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1514         {
1515                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1516                 {
1517                         VectorCopy(unstickoffsets + i, offset);
1518                         SV_LinkEdict (ent, true);
1519                         return UNSTICK_UNSTUCK;
1520                 }
1521         }
1522
1523         maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1524         // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1525
1526         for(i = 2; i <= maxunstick; ++i)
1527         {
1528                 VectorClear(offset);
1529                 offset[2] = -i;
1530                 if (!SV_TestEntityPosition(ent, offset))
1531                 {
1532                         SV_LinkEdict (ent, true);
1533                         return UNSTICK_UNSTUCK;
1534                 }
1535                 offset[2] = i;
1536                 if (!SV_TestEntityPosition(ent, offset))
1537                 {
1538                         SV_LinkEdict (ent, true);
1539                         return UNSTICK_UNSTUCK;
1540                 }
1541         }
1542
1543         return UNSTICK_STUCK;
1544 }
1545
1546 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1547 {
1548         vec3_t offset;
1549         switch(SV_UnstickEntityReturnOffset(ent, offset))
1550         {
1551                 case UNSTICK_GOOD:
1552                         return true;
1553                 case UNSTICK_UNSTUCK:
1554                         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]);
1555                         return true;
1556                 case UNSTICK_STUCK:
1557                         if (developer.integer >= 100)
1558                                 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1559                         return false;
1560                 default:
1561                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1562                         return false;
1563         }
1564 }
1565
1566 /*
1567 =============
1568 SV_CheckStuck
1569
1570 This is a big hack to try and fix the rare case of getting stuck in the world
1571 clipping hull.
1572 =============
1573 */
1574 void SV_CheckStuck (prvm_edict_t *ent)
1575 {
1576         vec3_t offset;
1577
1578         switch(SV_UnstickEntityReturnOffset(ent, offset))
1579         {
1580                 case UNSTICK_GOOD:
1581                         VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1582                         break;
1583                 case UNSTICK_UNSTUCK:
1584                         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]);
1585                         break;
1586                 case UNSTICK_STUCK:
1587                         VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1588                         if (!SV_TestEntityPosition(ent, offset))
1589                         {
1590                                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1591                                 SV_LinkEdict (ent, true);
1592                         }
1593                         else
1594                                 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1595                         break;
1596                 default:
1597                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1598         }
1599 }
1600
1601
1602 /*
1603 =============
1604 SV_CheckWater
1605 =============
1606 */
1607 qboolean SV_CheckWater (prvm_edict_t *ent)
1608 {
1609         int cont;
1610         int nNativeContents;
1611         vec3_t point;
1612
1613         point[0] = ent->fields.server->origin[0];
1614         point[1] = ent->fields.server->origin[1];
1615         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1616
1617         // DRESK - Support for Entity Contents Transition Event
1618         // NOTE: Some logic needed to be slightly re-ordered
1619         // to not affect performance and allow for the feature.
1620
1621         // Acquire Super Contents Prior to Resets
1622         cont = SV_PointSuperContents(point);
1623         // Acquire Native Contents Here
1624         nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1625
1626         // DRESK - Support for Entity Contents Transition Event
1627         if(ent->fields.server->watertype)
1628                 // Entity did NOT Spawn; Check
1629                 SV_CheckContentsTransition(ent, nNativeContents);
1630
1631
1632         ent->fields.server->waterlevel = 0;
1633         ent->fields.server->watertype = CONTENTS_EMPTY;
1634         cont = SV_PointSuperContents(point);
1635         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1636         {
1637                 ent->fields.server->watertype = nNativeContents;
1638                 ent->fields.server->waterlevel = 1;
1639                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1640                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1641                 {
1642                         ent->fields.server->waterlevel = 2;
1643                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1644                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1645                                 ent->fields.server->waterlevel = 3;
1646                 }
1647         }
1648
1649         return ent->fields.server->waterlevel > 1;
1650 }
1651
1652 /*
1653 ============
1654 SV_WallFriction
1655
1656 ============
1657 */
1658 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1659 {
1660         float d, i;
1661         vec3_t forward, into, side;
1662
1663         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1664         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1665         {
1666                 // cut the tangential velocity
1667                 i = DotProduct (stepnormal, ent->fields.server->velocity);
1668                 VectorScale (stepnormal, i, into);
1669                 VectorSubtract (ent->fields.server->velocity, into, side);
1670                 ent->fields.server->velocity[0] = side[0] * (1 + d);
1671                 ent->fields.server->velocity[1] = side[1] * (1 + d);
1672         }
1673 }
1674
1675 #if 0
1676 /*
1677 =====================
1678 SV_TryUnstick
1679
1680 Player has come to a dead stop, possibly due to the problem with limited
1681 float precision at some angle joins in the BSP hull.
1682
1683 Try fixing by pushing one pixel in each direction.
1684
1685 This is a hack, but in the interest of good gameplay...
1686 ======================
1687 */
1688 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1689 {
1690         int i, clip;
1691         vec3_t oldorg, dir;
1692
1693         VectorCopy (ent->fields.server->origin, oldorg);
1694         VectorClear (dir);
1695
1696         for (i=0 ; i<8 ; i++)
1697         {
1698                 // try pushing a little in an axial direction
1699                 switch (i)
1700                 {
1701                         case 0: dir[0] = 2; dir[1] = 0; break;
1702                         case 1: dir[0] = 0; dir[1] = 2; break;
1703                         case 2: dir[0] = -2; dir[1] = 0; break;
1704                         case 3: dir[0] = 0; dir[1] = -2; break;
1705                         case 4: dir[0] = 2; dir[1] = 2; break;
1706                         case 5: dir[0] = -2; dir[1] = 2; break;
1707                         case 6: dir[0] = 2; dir[1] = -2; break;
1708                         case 7: dir[0] = -2; dir[1] = -2; break;
1709                 }
1710
1711                 SV_PushEntity (&trace, ent, dir, false, true);
1712
1713                 // retry the original move
1714                 ent->fields.server->velocity[0] = oldvel[0];
1715                 ent->fields.server->velocity[1] = oldvel[1];
1716                 ent->fields.server->velocity[2] = 0;
1717                 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1718
1719                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1720                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1721                 {
1722                         Con_DPrint("TryUnstick - success.\n");
1723                         return clip;
1724                 }
1725
1726                 // go back to the original pos and try again
1727                 VectorCopy (oldorg, ent->fields.server->origin);
1728         }
1729
1730         // still not moving
1731         VectorClear (ent->fields.server->velocity);
1732         Con_DPrint("TryUnstick - failure.\n");
1733         return 7;
1734 }
1735 #endif
1736
1737 /*
1738 =====================
1739 SV_WalkMove
1740
1741 Only used by players
1742 ======================
1743 */
1744 void SV_WalkMove (prvm_edict_t *ent)
1745 {
1746         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1747         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1748         trace_t downtrace, trace;
1749         qboolean applygravity;
1750
1751         // if frametime is 0 (due to client sending the same timestamp twice),
1752         // don't move
1753         if (sv.frametime <= 0)
1754                 return;
1755
1756         SV_CheckStuck (ent);
1757
1758         applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
1759
1760         hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1761
1762         SV_CheckVelocity(ent);
1763
1764         // do a regular slide move unless it looks like you ran into a step
1765         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1766
1767         VectorCopy (ent->fields.server->origin, start_origin);
1768         VectorCopy (ent->fields.server->velocity, start_velocity);
1769
1770         clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
1771
1772         // if the move did not hit the ground at any point, we're not on ground
1773         if (!(clip & 1))
1774                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1775
1776         SV_CheckVelocity(ent);
1777         SV_LinkEdict (ent, true);
1778
1779         if(clip & 8) // teleport
1780                 return;
1781
1782         if ((int)ent->fields.server->flags & FL_WATERJUMP)
1783                 return;
1784
1785         if (sv_nostep.integer)
1786                 return;
1787
1788         VectorCopy(ent->fields.server->origin, originalmove_origin);
1789         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1790         originalmove_clip = clip;
1791         originalmove_flags = (int)ent->fields.server->flags;
1792         originalmove_groundentity = ent->fields.server->groundentity;
1793
1794         // if move didn't block on a step, return
1795         if (clip & 2)
1796         {
1797                 // if move was not trying to move into the step, return
1798                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1799                         return;
1800
1801                 if (ent->fields.server->movetype != MOVETYPE_FLY)
1802                 {
1803                         // return if gibbed by a trigger
1804                         if (ent->fields.server->movetype != MOVETYPE_WALK)
1805                                 return;
1806
1807                         // only step up while jumping if that is enabled
1808                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1809                                 if (!oldonground && ent->fields.server->waterlevel == 0)
1810                                         return;
1811                 }
1812
1813                 // try moving up and forward to go up a step
1814                 // back to start pos
1815                 VectorCopy (start_origin, ent->fields.server->origin);
1816                 VectorCopy (start_velocity, ent->fields.server->velocity);
1817
1818                 // move up
1819                 VectorClear (upmove);
1820                 upmove[2] = sv_stepheight.value;
1821                 if(!SV_PushEntity(&trace, ent, upmove, false, true))
1822                 {
1823                         // we got teleported when upstepping... must abort the move
1824                         return;
1825                 }
1826
1827                 // move forward
1828                 ent->fields.server->velocity[2] = 0;
1829                 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
1830                 ent->fields.server->velocity[2] += start_velocity[2];
1831                 if(clip & 8)
1832                 {
1833                         // we got teleported when upstepping... must abort the move
1834                         // note that z velocity handling may not be what QC expects here, but we cannot help it
1835                         return;
1836                 }
1837
1838                 SV_CheckVelocity(ent);
1839                 SV_LinkEdict (ent, true);
1840
1841                 // check for stuckness, possibly due to the limited precision of floats
1842                 // in the clipping hulls
1843                 if (clip
1844                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1845                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1846                 {
1847                         //Con_Printf("wall\n");
1848                         // stepping up didn't make any progress, revert to original move
1849                         VectorCopy(originalmove_origin, ent->fields.server->origin);
1850                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1851                         //clip = originalmove_clip;
1852                         ent->fields.server->flags = originalmove_flags;
1853                         ent->fields.server->groundentity = originalmove_groundentity;
1854                         // now try to unstick if needed
1855                         //clip = SV_TryUnstick (ent, oldvel);
1856                         return;
1857                 }
1858
1859                 //Con_Printf("step - ");
1860
1861                 // extra friction based on view angle
1862                 if (clip & 2 && sv_wallfriction.integer)
1863                         SV_WallFriction (ent, stepnormal);
1864         }
1865         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1866         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))
1867                 return;
1868
1869         // move down
1870         VectorClear (downmove);
1871         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1872         if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
1873         {
1874                 // we got teleported when downstepping... must abort the move
1875                 return;
1876         }
1877
1878         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1879         {
1880                 // this has been disabled so that you can't jump when you are stepping
1881                 // up while already jumping (also known as the Quake2 double jump bug)
1882 #if 0
1883                 // LordHavoc: disabled this check so you can walk on monsters/players
1884                 //if (ent->fields.server->solid == SOLID_BSP)
1885                 {
1886                         //Con_Printf("onground\n");
1887                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
1888                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1889                 }
1890 #endif
1891         }
1892         else
1893         {
1894                 //Con_Printf("slope\n");
1895                 // if the push down didn't end up on good ground, use the move without
1896                 // the step up.  This happens near wall / slope combinations, and can
1897                 // cause the player to hop up higher on a slope too steep to climb
1898                 VectorCopy(originalmove_origin, ent->fields.server->origin);
1899                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1900                 //clip = originalmove_clip;
1901                 ent->fields.server->flags = originalmove_flags;
1902                 ent->fields.server->groundentity = originalmove_groundentity;
1903         }
1904
1905         SV_CheckVelocity(ent);
1906         SV_LinkEdict (ent, true);
1907 }
1908
1909 //============================================================================
1910
1911 /*
1912 =============
1913 SV_Physics_Follow
1914
1915 Entities that are "stuck" to another entity
1916 =============
1917 */
1918 void SV_Physics_Follow (prvm_edict_t *ent)
1919 {
1920         vec3_t vf, vr, vu, angles, v;
1921         prvm_edict_t *e;
1922
1923         // regular thinking
1924         if (!SV_RunThink (ent))
1925                 return;
1926
1927         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1928         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1929         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])
1930         {
1931                 // quick case for no rotation
1932                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1933         }
1934         else
1935         {
1936                 angles[0] = -ent->fields.server->punchangle[0];
1937                 angles[1] =  ent->fields.server->punchangle[1];
1938                 angles[2] =  ent->fields.server->punchangle[2];
1939                 AngleVectors (angles, vf, vr, vu);
1940                 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];
1941                 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];
1942                 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];
1943                 angles[0] = -e->fields.server->angles[0];
1944                 angles[1] =  e->fields.server->angles[1];
1945                 angles[2] =  e->fields.server->angles[2];
1946                 AngleVectors (angles, vf, vr, vu);
1947                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1948                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1949                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1950         }
1951         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1952         SV_LinkEdict (ent, true);
1953 }
1954
1955 /*
1956 ==============================================================================
1957
1958 TOSS / BOUNCE
1959
1960 ==============================================================================
1961 */
1962
1963 /*
1964 =============
1965 SV_CheckWaterTransition
1966
1967 =============
1968 */
1969 void SV_CheckWaterTransition (prvm_edict_t *ent)
1970 {
1971         int cont;
1972         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1973         if (!ent->fields.server->watertype)
1974         {
1975                 // just spawned here
1976                 ent->fields.server->watertype = cont;
1977                 ent->fields.server->waterlevel = 1;
1978                 return;
1979         }
1980
1981         // DRESK - Support for Entity Contents Transition Event
1982         // NOTE: Call here BEFORE updating the watertype below,
1983         // and suppress watersplash sound if a valid function
1984         // call was made to allow for custom "splash" sounds.
1985         if( !SV_CheckContentsTransition(ent, cont) )
1986         { // Contents Transition Function Invalid; Potentially Play Water Sound
1987                 // check if the entity crossed into or out of water
1988                 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1989                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1990         }
1991
1992         if (cont <= CONTENTS_WATER)
1993         {
1994                 ent->fields.server->watertype = cont;
1995                 ent->fields.server->waterlevel = 1;
1996         }
1997         else
1998         {
1999                 ent->fields.server->watertype = CONTENTS_EMPTY;
2000                 ent->fields.server->waterlevel = 0;
2001         }
2002 }
2003
2004 /*
2005 =============
2006 SV_Physics_Toss
2007
2008 Toss, bounce, and fly movement.  When onground, do nothing.
2009 =============
2010 */
2011 void SV_Physics_Toss (prvm_edict_t *ent)
2012 {
2013         trace_t trace;
2014         vec3_t move;
2015         vec_t movetime;
2016         int bump;
2017
2018 // if onground, return without moving
2019         if ((int)ent->fields.server->flags & FL_ONGROUND)
2020         {
2021                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2022                 {
2023                         // don't stick to ground if onground and moving upward
2024                         ent->fields.server->flags -= FL_ONGROUND;
2025                 }
2026                 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2027                 {
2028                         // we can trust FL_ONGROUND if groundentity is world because it never moves
2029                         return;
2030                 }
2031                 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2032                 {
2033                         // if ent was supported by a brush model on previous frame,
2034                         // and groundentity is now freed, set groundentity to 0 (world)
2035                         // which leaves it suspended in the air
2036                         ent->fields.server->groundentity = 0;
2037                         if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
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 > 0)
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         {
2491                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2492                         if (!ent->priv.server->free)
2493                                 SV_Physics_Entity(ent);
2494                 // make a second pass to see if any ents spawned this frame and make
2495                 // sure they run their move/think
2496                 if (sv_gameplayfix_delayprojectiles.integer < 0)
2497                         for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2498                                 if (!ent->priv.server->move && !ent->priv.server->free)
2499                                         SV_Physics_Entity(ent);
2500         }
2501
2502         if (prog->globals.server->force_retouch > 0)
2503                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2504
2505         // LordHavoc: endframe support
2506         if (prog->funcoffsets.EndFrame)
2507         {
2508                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2509                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2510                 prog->globals.server->time = sv.time;
2511                 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2512         }
2513
2514         // decrement prog->num_edicts if the highest number entities died
2515         for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2516
2517         if (!sv_freezenonclients.integer)
2518                 sv.time += sv.frametime;
2519 }