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