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