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