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