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