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