]> icculus.org git repositories - divverent/darkplaces.git/blob - sv_phys.c
implemented sv_gameplayfix_nudgeoutofsolid
[divverent/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23
24 /*
25
26
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
28
29 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
30
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
37
38 solid_edge items only clip against bsp models.
39
40 */
41
42 #define MOVE_EPSILON    0.01
43
44 void SV_Physics_Toss (prvm_edict_t *ent);
45
46 int SV_GetPitchSign(prvm_edict_t *ent)
47 {
48         dp_model_t *model;
49         int modelindex;
50         if (
51                         ((modelindex = (int)ent->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[modelindex]))
52                         ?
53                         model->type == mod_alias
54                         :
55                         (
56                          (((unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
57                          ||
58                          ((gamemode == GAME_TENEBRAE) && ((unsigned int)ent->fields.server->effects & (16 | 32)))
59                         )
60            )
61                 return -1;
62         return 1;
63 }
64
65 /*
66 ===============================================================================
67
68 LINE TESTING IN HULLS
69
70 ===============================================================================
71 */
72
73 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
74 {
75         prvm_eval_t *val;
76         if (passedict)
77         {
78                 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
79                 if (val && val->_float)
80                         return (int)val->_float;
81                 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
82                 {
83                         if ((int)passedict->fields.server->flags & FL_MONSTER)
84                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
85                         else
86                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
87                 }
88                 else if (passedict->fields.server->solid == SOLID_CORPSE)
89                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
90                 else if (passedict->fields.server->solid == SOLID_TRIGGER)
91                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
92                 else
93                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
94         }
95         else
96                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
97 }
98
99 /*
100 ==================
101 SV_TracePoint
102 ==================
103 */
104 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
105 {
106         int i, bodysupercontents;
107         int passedictprog;
108         float pitchsign = 1;
109         prvm_edict_t *traceowner, *touch;
110         trace_t trace;
111         // bounding box of entire move area
112         vec3_t clipboxmins, clipboxmaxs;
113         // size when clipping against monsters
114         vec3_t clipmins2, clipmaxs2;
115         // start and end origin of move
116         vec3_t clipstart;
117         // trace results
118         trace_t cliptrace;
119         // matrices to transform into/out of other entity's space
120         matrix4x4_t matrix, imatrix;
121         // model of other entity
122         dp_model_t *model;
123         // list of entities to test for collisions
124         int numtouchedicts;
125         prvm_edict_t *touchedicts[MAX_EDICTS];
126
127         //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
128
129         VectorCopy(start, clipstart);
130         VectorClear(clipmins2);
131         VectorClear(clipmaxs2);
132 #if COLLISIONPARANOID >= 3
133         Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
134 #endif
135
136         // clip to world
137         Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
138         cliptrace.bmodelstartsolid = cliptrace.startsolid;
139         if (cliptrace.startsolid || cliptrace.fraction < 1)
140                 cliptrace.ent = prog->edicts;
141         if (type == MOVE_WORLDONLY)
142                 goto finished;
143
144         if (type == MOVE_MISSILE)
145         {
146                 // LordHavoc: modified this, was = -15, now -= 15
147                 for (i = 0;i < 3;i++)
148                 {
149                         clipmins2[i] -= 15;
150                         clipmaxs2[i] += 15;
151                 }
152         }
153
154         // create the bounding box of the entire move
155         for (i = 0;i < 3;i++)
156         {
157                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
158                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
159         }
160
161         // debug override to test against everything
162         if (sv_debugmove.integer)
163         {
164                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
165                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
166         }
167
168         // if the passedict is world, make it NULL (to avoid two checks each time)
169         if (passedict == prog->edicts)
170                 passedict = NULL;
171         // precalculate prog value for passedict for comparisons
172         passedictprog = PRVM_EDICT_TO_PROG(passedict);
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 (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                         pitchsign = SV_GetPitchSign(touch);
222                 }
223                 if (model)
224                         Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
225                 else
226                         Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
227                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
228                 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
229                         Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
230                 else
231                         Collision_ClipPointToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
232
233                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
234         }
235
236 finished:
237         return cliptrace;
238 }
239
240 /*
241 ==================
242 SV_TraceLine
243 ==================
244 */
245 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
246 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
247 #else
248 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
249 #endif
250 {
251         int i, bodysupercontents;
252         int passedictprog;
253         float pitchsign = 1;
254         prvm_edict_t *traceowner, *touch;
255         trace_t trace;
256         // bounding box of entire move area
257         vec3_t clipboxmins, clipboxmaxs;
258         // size when clipping against monsters
259         vec3_t clipmins2, clipmaxs2;
260         // start and end origin of move
261         vec3_t clipstart, clipend;
262         // trace results
263         trace_t cliptrace;
264         // matrices to transform into/out of other entity's space
265         matrix4x4_t matrix, imatrix;
266         // model of other entity
267         dp_model_t *model;
268         // list of entities to test for collisions
269         int numtouchedicts;
270         prvm_edict_t *touchedicts[MAX_EDICTS];
271 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
272         vec3_t end;
273         vec_t len = 0;
274
275         if(!VectorCompare(start, pEnd))
276         {
277                 // TRICK: make the trace 1 qu longer!
278                 VectorSubtract(pEnd, start, end);
279                 len = VectorNormalizeLength(end);
280                 VectorAdd(pEnd, end, end);
281         }
282         else
283                 VectorCopy(pEnd, end);
284 #endif
285
286         //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
287
288         if (VectorCompare(start, end))
289                 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
290
291         VectorCopy(start, clipstart);
292         VectorCopy(end, clipend);
293         VectorClear(clipmins2);
294         VectorClear(clipmaxs2);
295 #if COLLISIONPARANOID >= 3
296         Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
297 #endif
298
299         // clip to world
300         Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask);
301         cliptrace.bmodelstartsolid = cliptrace.startsolid;
302         if (cliptrace.startsolid || cliptrace.fraction < 1)
303                 cliptrace.ent = prog->edicts;
304         if (type == MOVE_WORLDONLY)
305                 goto finished;
306
307         if (type == MOVE_MISSILE)
308         {
309                 // LordHavoc: modified this, was = -15, now -= 15
310                 for (i = 0;i < 3;i++)
311                 {
312                         clipmins2[i] -= 15;
313                         clipmaxs2[i] += 15;
314                 }
315         }
316
317         // create the bounding box of the entire move
318         for (i = 0;i < 3;i++)
319         {
320                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
321                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
322         }
323
324         // debug override to test against everything
325         if (sv_debugmove.integer)
326         {
327                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
328                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
329         }
330
331         // if the passedict is world, make it NULL (to avoid two checks each time)
332         if (passedict == prog->edicts)
333                 passedict = NULL;
334         // precalculate prog value for passedict for comparisons
335         passedictprog = PRVM_EDICT_TO_PROG(passedict);
336         // precalculate passedict's owner edict pointer for comparisons
337         traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
338
339         // clip to entities
340         // because this uses World_EntitiestoBox, we know all entity boxes overlap
341         // the clip region, so we can skip culling checks in the loop below
342         numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
343         if (numtouchedicts > MAX_EDICTS)
344         {
345                 // this never happens
346                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
347                 numtouchedicts = MAX_EDICTS;
348         }
349         for (i = 0;i < numtouchedicts;i++)
350         {
351                 touch = touchedicts[i];
352
353                 if (touch->fields.server->solid < SOLID_BBOX)
354                         continue;
355                 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
356                         continue;
357
358                 if (passedict)
359                 {
360                         // don't clip against self
361                         if (passedict == touch)
362                                 continue;
363                         // don't clip owned entities against owner
364                         if (traceowner == touch)
365                                 continue;
366                         // don't clip owner against owned entities
367                         if (passedictprog == touch->fields.server->owner)
368                                 continue;
369                         // don't clip points against points (they can't collide)
370                         if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
371                                 continue;
372                 }
373
374                 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
375
376                 // might interact, so do an exact clip
377                 model = NULL;
378                 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
379                 {
380                         unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
381                         // if the modelindex is 0, it shouldn't be SOLID_BSP!
382                         if (modelindex > 0 && modelindex < MAX_MODELS)
383                                 model = sv.models[(int)touch->fields.server->modelindex];
384                         pitchsign = SV_GetPitchSign(touch);
385                 }
386                 if (model)
387                         Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
388                 else
389                         Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
390                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
391                 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
392                         Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
393                 else
394                         Collision_ClipLineToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
395
396                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
397         }
398
399 finished:
400 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
401         if(!VectorCompare(start, pEnd))
402                 Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd);
403 #endif
404         return cliptrace;
405 }
406
407 /*
408 ==================
409 SV_Move
410 ==================
411 */
412 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
413 #if COLLISIONPARANOID >= 1
414 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
415 #else
416 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
417 #endif
418 #else
419 #if COLLISIONPARANOID >= 1
420 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
421 #else
422 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
423 #endif
424 #endif
425 {
426         vec3_t hullmins, hullmaxs;
427         int i, bodysupercontents;
428         int passedictprog;
429         float pitchsign = 1;
430         qboolean pointtrace;
431         prvm_edict_t *traceowner, *touch;
432         trace_t trace;
433         // bounding box of entire move area
434         vec3_t clipboxmins, clipboxmaxs;
435         // size of the moving object
436         vec3_t clipmins, clipmaxs;
437         // size when clipping against monsters
438         vec3_t clipmins2, clipmaxs2;
439         // start and end origin of move
440         vec3_t clipstart, clipend;
441         // trace results
442         trace_t cliptrace;
443         // matrices to transform into/out of other entity's space
444         matrix4x4_t matrix, imatrix;
445         // model of other entity
446         dp_model_t *model;
447         // list of entities to test for collisions
448         int numtouchedicts;
449         prvm_edict_t *touchedicts[MAX_EDICTS];
450 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
451         vec3_t end;
452         vec_t len = 0;
453
454         if(!VectorCompare(start, pEnd))
455         {
456                 // TRICK: make the trace 1 qu longer!
457                 VectorSubtract(pEnd, start, end);
458                 len = VectorNormalizeLength(end);
459                 VectorAdd(pEnd, end, end);
460         }
461         else
462                 VectorCopy(pEnd, end);
463 #endif
464
465         if (VectorCompare(mins, maxs))
466         {
467                 vec3_t shiftstart, shiftend;
468                 VectorAdd(start, mins, shiftstart);
469                 VectorAdd(end, mins, shiftend);
470                 if (VectorCompare(start, end))
471                         trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
472                 else
473                         trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
474                 VectorSubtract(trace.endpos, mins, trace.endpos);
475                 return trace;
476         }
477
478         VectorCopy(start, clipstart);
479         VectorCopy(end, clipend);
480         VectorCopy(mins, clipmins);
481         VectorCopy(maxs, clipmaxs);
482         VectorCopy(mins, clipmins2);
483         VectorCopy(maxs, clipmaxs2);
484 #if COLLISIONPARANOID >= 3
485         Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
486 #endif
487
488         // clip to world
489         Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
490         cliptrace.bmodelstartsolid = cliptrace.startsolid;
491         if (cliptrace.startsolid || cliptrace.fraction < 1)
492                 cliptrace.ent = prog->edicts;
493         if (type == MOVE_WORLDONLY)
494                 goto finished;
495
496         if (type == MOVE_MISSILE)
497         {
498                 // LordHavoc: modified this, was = -15, now -= 15
499                 for (i = 0;i < 3;i++)
500                 {
501                         clipmins2[i] -= 15;
502                         clipmaxs2[i] += 15;
503                 }
504         }
505
506         // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
507         if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
508                 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
509         else
510         {
511                 VectorCopy(clipmins, hullmins);
512                 VectorCopy(clipmaxs, hullmaxs);
513         }
514
515         // create the bounding box of the entire move
516         for (i = 0;i < 3;i++)
517         {
518                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
519                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
520         }
521
522         // debug override to test against everything
523         if (sv_debugmove.integer)
524         {
525                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
526                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
527         }
528
529         // if the passedict is world, make it NULL (to avoid two checks each time)
530         if (passedict == prog->edicts)
531                 passedict = NULL;
532         // precalculate prog value for passedict for comparisons
533         passedictprog = PRVM_EDICT_TO_PROG(passedict);
534         // figure out whether this is a point trace for comparisons
535         pointtrace = VectorCompare(clipmins, clipmaxs);
536         // precalculate passedict's owner edict pointer for comparisons
537         traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
538
539         // clip to entities
540         // because this uses World_EntitiestoBox, we know all entity boxes overlap
541         // the clip region, so we can skip culling checks in the loop below
542         numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
543         if (numtouchedicts > MAX_EDICTS)
544         {
545                 // this never happens
546                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
547                 numtouchedicts = MAX_EDICTS;
548         }
549         for (i = 0;i < numtouchedicts;i++)
550         {
551                 touch = touchedicts[i];
552
553                 if (touch->fields.server->solid < SOLID_BBOX)
554                         continue;
555                 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
556                         continue;
557
558                 if (passedict)
559                 {
560                         // don't clip against self
561                         if (passedict == touch)
562                                 continue;
563                         // don't clip owned entities against owner
564                         if (traceowner == touch)
565                                 continue;
566                         // don't clip owner against owned entities
567                         if (passedictprog == touch->fields.server->owner)
568                                 continue;
569                         // don't clip points against points (they can't collide)
570                         if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
571                                 continue;
572                 }
573
574                 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
575
576                 // might interact, so do an exact clip
577                 model = NULL;
578                 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
579                 {
580                         unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
581                         // if the modelindex is 0, it shouldn't be SOLID_BSP!
582                         if (modelindex > 0 && modelindex < MAX_MODELS)
583                                 model = sv.models[(int)touch->fields.server->modelindex];
584                         //pitchsign = 1;
585                         pitchsign = SV_GetPitchSign(touch);
586                 }
587                 if (model)
588                         Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
589                 else
590                         Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
591                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
592                 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
593                         Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
594                 else
595                         Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
596
597                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
598         }
599
600 finished:
601 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
602         if(!VectorCompare(start, pEnd))
603                 Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd);
604 #endif
605         return cliptrace;
606 }
607
608 #if COLLISIONPARANOID >= 1
609 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
610 {
611         int endstuck;
612         trace_t trace;
613         vec3_t temp;
614         trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
615         if (passedict)
616         {
617                 VectorCopy(trace.endpos, temp);
618                 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
619 #if COLLISIONPARANOID < 3
620                 if (trace.startsolid || endstuck)
621 #endif
622                         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" : "");
623         }
624         return trace;
625 }
626 #endif
627
628 int SV_PointSuperContents(const vec3_t point)
629 {
630         int supercontents = 0;
631         int i;
632         prvm_edict_t *touch;
633         vec3_t transformed;
634         // matrices to transform into/out of other entity's space
635         matrix4x4_t matrix, imatrix;
636         // model of other entity
637         dp_model_t *model;
638         unsigned int modelindex;
639         int frame;
640         // list of entities to test for collisions
641         int numtouchedicts;
642         prvm_edict_t *touchedicts[MAX_EDICTS];
643
644         // get world supercontents at this point
645         if (sv.worldmodel && sv.worldmodel->PointSuperContents)
646                 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
647
648         // if sv_gameplayfix_swiminbmodels is off we're done
649         if (!sv_gameplayfix_swiminbmodels.integer)
650                 return supercontents;
651
652         // get list of entities at this point
653         numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
654         if (numtouchedicts > MAX_EDICTS)
655         {
656                 // this never happens
657                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
658                 numtouchedicts = MAX_EDICTS;
659         }
660         for (i = 0;i < numtouchedicts;i++)
661         {
662                 touch = touchedicts[i];
663
664                 // we only care about SOLID_BSP for pointcontents
665                 if (touch->fields.server->solid != SOLID_BSP)
666                         continue;
667
668                 // might interact, so do an exact clip
669                 modelindex = (unsigned int)touch->fields.server->modelindex;
670                 if (modelindex >= MAX_MODELS)
671                         continue;
672                 model = sv.models[(int)touch->fields.server->modelindex];
673                 if (!model || !model->PointSuperContents)
674                         continue;
675                 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);
676                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
677                 Matrix4x4_Transform(&imatrix, point, transformed);
678                 frame = (int)touch->fields.server->frame;
679                 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
680         }
681
682         return supercontents;
683 }
684
685 /*
686 ===============================================================================
687
688 Linking entities into the world culling system
689
690 ===============================================================================
691 */
692
693 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
694 {
695         prvm_eval_t *val;
696         prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
697         prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
698         prog->globals.server->time = sv.time;
699         prog->globals.server->trace_allsolid = false;
700         prog->globals.server->trace_startsolid = false;
701         prog->globals.server->trace_fraction = 1;
702         prog->globals.server->trace_inwater = false;
703         prog->globals.server->trace_inopen = true;
704         VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
705         VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
706         prog->globals.server->trace_plane_dist = 0;
707         prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
708         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
709                 val->_float = 0;
710         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
711                 val->_float = 0;
712         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
713                 val->_float = 0;
714         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
715                 val->string = 0;
716         PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
717 }
718
719 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
720 {
721         int i, numtouchedicts, old_self, old_other;
722         prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
723
724         if (ent == prog->edicts)
725                 return;         // don't add the world
726
727         if (ent->priv.server->free)
728                 return;
729
730         if (ent->fields.server->solid == SOLID_NOT)
731                 return;
732
733         // build a list of edicts to touch, because the link loop can be corrupted
734         // by IncreaseEdicts called during touch functions
735         numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
736         if (numtouchedicts > MAX_EDICTS)
737         {
738                 // this never happens
739                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
740                 numtouchedicts = MAX_EDICTS;
741         }
742
743         old_self = prog->globals.server->self;
744         old_other = prog->globals.server->other;
745         for (i = 0;i < numtouchedicts;i++)
746         {
747                 touch = touchedicts[i];
748                 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
749                 {
750                         SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
751                 }
752         }
753         prog->globals.server->self = old_self;
754         prog->globals.server->other = old_other;
755 }
756
757 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
758 {
759         vec3_t v, u;
760         matrix4x4_t m;
761         Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
762
763         v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
764                 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
765         v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
766                 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
767                 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
768         v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
769                 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
770                 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
771         v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
772                 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
773                 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
774         v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
775                 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
776                 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
777         v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
778                 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
779                 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
780         v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
781                 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
782                 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
783         v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
784                 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
785                 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
786 }
787
788 /*
789 ===============
790 SV_LinkEdict
791
792 ===============
793 */
794 void SV_LinkEdict (prvm_edict_t *ent)
795 {
796         dp_model_t *model;
797         vec3_t mins, maxs;
798
799         if (ent == prog->edicts)
800                 return;         // don't add the world
801
802         if (ent->priv.server->free)
803                 return;
804
805 // set the abs box
806
807         if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
808         {
809                 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
810                 // TODO special handling for spheres?
811                 RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs);
812                 VectorAdd(ent->fields.server->origin, mins, mins);
813                 VectorAdd(ent->fields.server->origin, maxs, maxs);
814         }
815         else if (ent->fields.server->solid == SOLID_BSP)
816         {
817                 int modelindex = (int)ent->fields.server->modelindex;
818                 if (modelindex < 0 || modelindex >= MAX_MODELS)
819                 {
820                         Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
821                         modelindex = 0;
822                 }
823                 model = sv.models[modelindex];
824                 if (model != NULL)
825                 {
826                         if (!model->TraceBox && developer.integer >= 1)
827                                 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
828
829                         if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
830                         {
831                                 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
832                                 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
833                         }
834                         else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
835                         {
836                                 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
837                                 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
838                         }
839                         else
840                         {
841                                 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
842                                 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
843                         }
844                 }
845                 else
846                 {
847                         // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
848                         VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
849                         VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
850                 }
851         }
852         else
853         {
854                 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
855                 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
856         }
857
858 //
859 // to make items easier to pick up and allow them to be grabbed off
860 // of shelves, the abs sizes are expanded
861 //
862         if ((int)ent->fields.server->flags & FL_ITEM)
863         {
864                 mins[0] -= 15;
865                 mins[1] -= 15;
866                 mins[2] -= 1;
867                 maxs[0] += 15;
868                 maxs[1] += 15;
869                 maxs[2] += 1;
870         }
871         else
872         {
873                 // because movement is clipped an epsilon away from an actual edge,
874                 // we must fully check even when bounding boxes don't quite touch
875                 mins[0] -= 1;
876                 mins[1] -= 1;
877                 mins[2] -= 1;
878                 maxs[0] += 1;
879                 maxs[1] += 1;
880                 maxs[2] += 1;
881         }
882
883         VectorCopy(mins, ent->fields.server->absmin);
884         VectorCopy(maxs, ent->fields.server->absmax);
885
886         World_LinkEdict(&sv.world, ent, mins, maxs);
887 }
888
889 /*
890 ===============================================================================
891
892 Utility functions
893
894 ===============================================================================
895 */
896
897 /*
898 ============
899 SV_TestEntityPosition
900
901 returns true if the entity is in solid currently
902 ============
903 */
904 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
905 {
906         int contents;
907         vec3_t org;
908         trace_t trace;
909         contents = SV_GenericHitSuperContentsMask(ent);
910         VectorAdd(ent->fields.server->origin, offset, org);
911         trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
912         if (trace.startsupercontents & contents)
913                 return true;
914         else
915         {
916                 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
917                 {
918                         // q1bsp/hlbsp use hulls and if the entity does not exactly match
919                         // a hull size it is incorrectly tested, so this code tries to
920                         // 'fix' it slightly...
921                         // FIXME: this breaks entities larger than the hull size
922                         int i;
923                         vec3_t v, m1, m2, s;
924                         VectorAdd(org, ent->fields.server->mins, m1);
925                         VectorAdd(org, ent->fields.server->maxs, m2);
926                         VectorSubtract(m2, m1, s);
927 #define EPSILON (1.0f / 32.0f)
928                         if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
929                         if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
930                         if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
931                         for (i = 0;i < 8;i++)
932                         {
933                                 v[0] = (i & 1) ? m2[0] : m1[0];
934                                 v[1] = (i & 2) ? m2[1] : m1[1];
935                                 v[2] = (i & 4) ? m2[2] : m1[2];
936                                 if (SV_PointSuperContents(v) & contents)
937                                         return true;
938                         }
939                 }
940         }
941         // if the trace found a better position for the entity, move it there
942         if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
943         {
944 #if 0
945                 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
946                 VectorCopy(trace.endpos, ent->fields.server->origin);
947 #else
948                 // verify if the endpos is REALLY outside solid
949                 VectorCopy(trace.endpos, org);
950                 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
951                 if(trace.startsolid)
952                         Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
953                 else
954                         VectorCopy(org, ent->fields.server->origin);
955 #endif
956         }
957         return false;
958 }
959
960 /*
961 ================
962 SV_CheckAllEnts
963 ================
964 */
965 void SV_CheckAllEnts (void)
966 {
967         int e;
968         prvm_edict_t *check;
969
970         // see if any solid entities are inside the final position
971         check = PRVM_NEXT_EDICT(prog->edicts);
972         for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
973         {
974                 if (check->priv.server->free)
975                         continue;
976                 if (check->fields.server->movetype == MOVETYPE_PUSH
977                  || check->fields.server->movetype == MOVETYPE_NONE
978                  || check->fields.server->movetype == MOVETYPE_FOLLOW
979                  || check->fields.server->movetype == MOVETYPE_NOCLIP)
980                         continue;
981
982                 if (SV_TestEntityPosition (check, vec3_origin))
983                         Con_Print("entity in invalid position\n");
984         }
985 }
986
987 // DRESK - Support for Entity Contents Transition Event
988 /*
989 ================
990 SV_CheckContentsTransition
991
992 returns true if entity had a valid contentstransition function call
993 ================
994 */
995 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
996 {
997         int bValidFunctionCall;
998         prvm_eval_t *contentstransition;
999
1000         // Default Valid Function Call to False
1001         bValidFunctionCall = false;
1002
1003         if(ent->fields.server->watertype != nContents)
1004         { // Changed Contents
1005                 // Acquire Contents Transition Function from QC
1006                 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
1007
1008                 if(contentstransition->function)
1009                 { // Valid Function; Execute
1010                         // Assign Valid Function
1011                         bValidFunctionCall = true;
1012                         // Prepare Parameters (Original Contents, New Contents)
1013                                 // Original Contents
1014                                 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
1015                                 // New Contents
1016                                 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1017                                 // Assign Self
1018                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1019                         // Execute VM Function
1020                         PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
1021                 }
1022         }
1023
1024         // Return if Function Call was Valid
1025         return bValidFunctionCall;
1026 }
1027
1028
1029 /*
1030 ================
1031 SV_CheckVelocity
1032 ================
1033 */
1034 void SV_CheckVelocity (prvm_edict_t *ent)
1035 {
1036         int i;
1037         float wishspeed;
1038
1039 //
1040 // bound velocity
1041 //
1042         for (i=0 ; i<3 ; i++)
1043         {
1044                 if (IS_NAN(ent->fields.server->velocity[i]))
1045                 {
1046                         Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1047                         ent->fields.server->velocity[i] = 0;
1048                 }
1049                 if (IS_NAN(ent->fields.server->origin[i]))
1050                 {
1051                         Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1052                         ent->fields.server->origin[i] = 0;
1053                 }
1054         }
1055
1056         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1057         // player_run/player_stand1 does not horribly malfunction if the
1058         // velocity becomes a denormalized float
1059         if (VectorLength2(ent->fields.server->velocity) < 0.0001)
1060                 VectorClear(ent->fields.server->velocity);
1061
1062         // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1063         wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1064         if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1065         {
1066                 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1067                 ent->fields.server->velocity[0] *= wishspeed;
1068                 ent->fields.server->velocity[1] *= wishspeed;
1069                 ent->fields.server->velocity[2] *= wishspeed;
1070         }
1071 }
1072
1073 /*
1074 =============
1075 SV_RunThink
1076
1077 Runs thinking code if time.  There is some play in the exact time the think
1078 function will be called, because it is called before any movement is done
1079 in a frame.  Not used for pushmove objects, because they must be exact.
1080 Returns false if the entity removed itself.
1081 =============
1082 */
1083 qboolean SV_RunThink (prvm_edict_t *ent)
1084 {
1085         int iterations;
1086
1087         // don't let things stay in the past.
1088         // it is possible to start that way by a trigger with a local time.
1089         if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1090                 return true;
1091
1092         for (iterations = 0;iterations < 128  && !ent->priv.server->free;iterations++)
1093         {
1094                 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1095                 ent->fields.server->nextthink = 0;
1096                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1097                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1098                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1099                 // mods often set nextthink to time to cause a think every frame,
1100                 // we don't want to loop in that case, so exit if the new nextthink is
1101                 // <= the time the qc was told, also exit if it is past the end of the
1102                 // frame
1103                 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1104                         break;
1105         }
1106         return !ent->priv.server->free;
1107 }
1108
1109 /*
1110 ==================
1111 SV_Impact
1112
1113 Two entities have touched, so run their touch functions
1114 returns true if the impact kept the origin of the touching entity intact
1115 ==================
1116 */
1117 extern void VM_SetTraceGlobals(const trace_t *trace);
1118 extern sizebuf_t vm_tempstringsbuf;
1119 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1120 {
1121         int restorevm_tempstringsbuf_cursize;
1122         int old_self, old_other;
1123         vec3_t org;
1124         prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1125         prvm_eval_t *val;
1126
1127         old_self = prog->globals.server->self;
1128         old_other = prog->globals.server->other;
1129         restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1130
1131         VectorCopy(e1->fields.server->origin, org);
1132
1133         VM_SetTraceGlobals(trace);
1134
1135         prog->globals.server->time = sv.time;
1136         if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1137         {
1138                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1139                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1140                 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1141         }
1142
1143         if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1144         {
1145                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1146                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1147                 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1148                 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1149                 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1150                 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1151                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1152                         val->_float = 0;
1153                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1154                         val->_float = 0;
1155                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1156                         val->_float = 0;
1157                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1158                         val->string = 0;
1159                 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1160         }
1161
1162         prog->globals.server->self = old_self;
1163         prog->globals.server->other = old_other;
1164         vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1165
1166         return VectorCompare(e1->fields.server->origin, org);
1167 }
1168
1169
1170 /*
1171 ==================
1172 ClipVelocity
1173
1174 Slide off of the impacting object
1175 returns the blocked flags (1 = floor, 2 = step / wall)
1176 ==================
1177 */
1178 #define STOP_EPSILON 0.1
1179 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1180 {
1181         int i;
1182         float backoff;
1183
1184         backoff = -DotProduct (in, normal) * overbounce;
1185         VectorMA(in, backoff, normal, out);
1186
1187         for (i = 0;i < 3;i++)
1188                 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1189                         out[i] = 0;
1190 }
1191
1192
1193 /*
1194 ============
1195 SV_FlyMove
1196
1197 The basic solid body movement clip that slides along multiple planes
1198 Returns the clipflags if the velocity was modified (hit something solid)
1199 1 = floor
1200 2 = wall / step
1201 4 = dead stop
1202 8 = teleported by touch method
1203 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1204 ============
1205 */
1206 static float SV_Gravity (prvm_edict_t *ent);
1207 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1208 #define MAX_CLIP_PLANES 5
1209 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
1210 {
1211         int blocked, bumpcount;
1212         int i, j, numplanes;
1213         float d, time_left, gravity;
1214         vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1215 #if 0
1216         vec3_t end;
1217 #endif
1218         trace_t trace;
1219         if (time <= 0)
1220                 return 0;
1221         gravity = 0;
1222         if (applygravity)
1223         {
1224                 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1225                 {
1226                         gravity = SV_Gravity(ent) * 0.5f;
1227                         ent->fields.server->velocity[2] -= gravity;
1228                 }
1229                 else
1230                 {
1231                         applygravity = false;
1232                         ent->fields.server->velocity[2] -= SV_Gravity(ent);
1233                 }
1234         }
1235         blocked = 0;
1236         VectorCopy(ent->fields.server->velocity, original_velocity);
1237         VectorCopy(ent->fields.server->velocity, primal_velocity);
1238         numplanes = 0;
1239         time_left = time;
1240         for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1241         {
1242                 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1243                         break;
1244
1245                 VectorScale(ent->fields.server->velocity, time_left, push);
1246 #if 0
1247                 VectorAdd(ent->fields.server->origin, push, end);
1248 #endif
1249                 if(!SV_PushEntity(&trace, ent, push, false, false))
1250                 {
1251                         // we got teleported by a touch function
1252                         // let's abort the move
1253                         blocked |= 8;
1254                         break;
1255                 }
1256
1257 #if 0
1258                 //if (trace.fraction < 0.002)
1259                 {
1260 #if 1
1261                         vec3_t start;
1262                         trace_t testtrace;
1263                         VectorCopy(ent->fields.server->origin, start);
1264                         start[2] += 3;//0.03125;
1265                         VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1266                         end[2] += 3;//0.03125;
1267                         testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1268                         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)))
1269                         {
1270                                 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
1271                                 trace = testtrace;
1272                         }
1273 #endif
1274 #if 0
1275                         //j = -1;
1276                         for (i = 0;i < numplanes;i++)
1277                         {
1278                                 VectorCopy(ent->fields.server->origin, start);
1279                                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1280                                 VectorMA(start, 3, planes[i], start);
1281                                 VectorMA(end, 3, planes[i], end);
1282                                 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1283                                 if (trace.fraction < testtrace.fraction)
1284                                 {
1285                                         trace = testtrace;
1286                                         VectorCopy(start, ent->fields.server->origin);
1287                                         //j = i;
1288                                 }
1289                         }
1290                         //if (j >= 0)
1291                         //      VectorAdd(ent->fields.server->origin, planes[j], start);
1292 #endif
1293                 }
1294 #endif
1295
1296 #if 0
1297                 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);
1298                 if (trace.fraction < 1)
1299                         Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
1300                 Con_Print("\n");
1301 #endif
1302
1303 #if 0
1304                 if (trace.bmodelstartsolid)
1305                 {
1306                         // LordHavoc: note: this code is what makes entities stick in place
1307                         // if embedded in world only (you can walk through other objects if
1308                         // stuck)
1309                         // entity is trapped in another solid
1310                         VectorClear(ent->fields.server->velocity);
1311                         return 3;
1312                 }
1313 #endif
1314
1315                 if (trace.fraction == 1)
1316                         break;
1317                 if (trace.plane.normal[2])
1318                 {
1319                         if (trace.plane.normal[2] > 0.7)
1320                         {
1321                                 // floor
1322                                 blocked |= 1;
1323
1324                                 if (!trace.ent)
1325                                 {
1326                                         Con_Printf ("SV_FlyMove: !trace.ent");
1327                                         trace.ent = prog->edicts;
1328                                 }
1329
1330                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1331                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1332                         }
1333                 }
1334                 else
1335                 {
1336                         // step
1337                         blocked |= 2;
1338                         // save the trace for player extrafriction
1339                         if (stepnormal)
1340                                 VectorCopy(trace.plane.normal, stepnormal);
1341                 }
1342                 if (trace.fraction >= 0.001)
1343                 {
1344                         // actually covered some distance
1345                         VectorCopy(ent->fields.server->velocity, original_velocity);
1346                         numplanes = 0;
1347                 }
1348
1349                 time_left *= 1 - trace.fraction;
1350
1351                 // clipped to another plane
1352                 if (numplanes >= MAX_CLIP_PLANES)
1353                 {
1354                         // this shouldn't really happen
1355                         VectorClear(ent->fields.server->velocity);
1356                         blocked = 3;
1357                         break;
1358                 }
1359
1360                 /*
1361                 for (i = 0;i < numplanes;i++)
1362                         if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1363                                 break;
1364                 if (i < numplanes)
1365                 {
1366                         VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1367                         continue;
1368                 }
1369                 */
1370
1371                 VectorCopy(trace.plane.normal, planes[numplanes]);
1372                 numplanes++;
1373
1374                 if (sv_newflymove.integer)
1375                         ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1376                 else
1377                 {
1378                         // modify original_velocity so it parallels all of the clip planes
1379                         for (i = 0;i < numplanes;i++)
1380                         {
1381                                 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1382                                 for (j = 0;j < numplanes;j++)
1383                                 {
1384                                         if (j != i)
1385                                         {
1386                                                 // not ok
1387                                                 if (DotProduct(new_velocity, planes[j]) < 0)
1388                                                         break;
1389                                         }
1390                                 }
1391                                 if (j == numplanes)
1392                                         break;
1393                         }
1394
1395                         if (i != numplanes)
1396                         {
1397                                 // go along this plane
1398                                 VectorCopy(new_velocity, ent->fields.server->velocity);
1399                         }
1400                         else
1401                         {
1402                                 // go along the crease
1403                                 if (numplanes != 2)
1404                                 {
1405                                         VectorClear(ent->fields.server->velocity);
1406                                         blocked = 7;
1407                                         break;
1408                                 }
1409                                 CrossProduct(planes[0], planes[1], dir);
1410                                 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1411                                 VectorNormalize(dir);
1412                                 d = DotProduct(dir, ent->fields.server->velocity);
1413                                 VectorScale(dir, d, ent->fields.server->velocity);
1414                         }
1415                 }
1416
1417                 // if current velocity is against the original velocity,
1418                 // stop dead to avoid tiny occilations in sloping corners
1419                 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1420                 {
1421                         VectorClear(ent->fields.server->velocity);
1422                         break;
1423                 }
1424         }
1425
1426         //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]);
1427
1428         /*
1429         if ((blocked & 1) == 0 && bumpcount > 1)
1430         {
1431                 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1432                 // flag ONGROUND if there's ground under it
1433                 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1434         }
1435         */
1436
1437         // LordHavoc: this came from QW and allows you to get out of water more easily
1438         if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1439                 VectorCopy(primal_velocity, ent->fields.server->velocity);
1440         if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1441                 ent->fields.server->velocity[2] -= gravity;
1442         return blocked;
1443 }
1444
1445 /*
1446 ============
1447 SV_Gravity
1448
1449 ============
1450 */
1451 static float SV_Gravity (prvm_edict_t *ent)
1452 {
1453         float ent_gravity;
1454         prvm_eval_t *val;
1455
1456         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1457         if (val!=0 && val->_float)
1458                 ent_gravity = val->_float;
1459         else
1460                 ent_gravity = 1.0;
1461         return ent_gravity * sv_gravity.value * sv.frametime;
1462 }
1463
1464
1465 /*
1466 ===============================================================================
1467
1468 PUSHMOVE
1469
1470 ===============================================================================
1471 */
1472
1473 /*
1474 ============
1475 SV_PushEntity
1476
1477 Does not change the entities velocity at all
1478 The trace struct is filled with the trace that has been done.
1479 Returns true if the push did not result in the entity being teleported by QC code.
1480 ============
1481 */
1482 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1483 {
1484         int type;
1485         int bump;
1486         vec3_t original;
1487         vec3_t end;
1488
1489         VectorCopy(ent->fields.server->origin, original);
1490         VectorAdd (ent->fields.server->origin, push, end);
1491
1492         if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1493                 type = MOVE_MISSILE;
1494         else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1495                 type = MOVE_NOMONSTERS; // only clip against bmodels
1496         else
1497                 type = MOVE_NORMAL;
1498
1499         *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1500         bump = 0;
1501         while (trace->startsolid && sv_gameplayfix_nudgeoutofsolid.integer)
1502         {
1503                 VectorMA(ent->fields.server->origin, -trace->startdepth, trace->startdepthnormal, ent->fields.server->origin);
1504                 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1505                 bump++;
1506                 if (bump > 10)
1507                 {
1508                         VectorCopy(original, ent->fields.server->origin);
1509                         break;
1510                 }
1511         }
1512         if (trace->bmodelstartsolid && failonbmodelstartsolid)
1513                 return true;
1514
1515
1516         VectorCopy (trace->endpos, ent->fields.server->origin);
1517         SV_LinkEdict(ent);
1518
1519 #if 0
1520         if(!trace->startsolid)
1521         if(SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, type, ent, SV_GenericHitSuperContentsMask(ent)).startsolid)
1522         {
1523                 Con_Printf("something eeeeevil happened\n");
1524         }
1525 #endif
1526
1527         if (dolink)
1528                 SV_LinkEdict_TouchAreaGrid(ent);
1529
1530         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))))
1531                 return SV_Impact (ent, trace);
1532
1533         return true;
1534 }
1535
1536
1537 /*
1538 ============
1539 SV_PushMove
1540
1541 ============
1542 */
1543 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1544 {
1545         int i, e, index;
1546         int pusherowner, pusherprog;
1547         int checkcontents;
1548         qboolean rotated;
1549         float savesolid, movetime2, pushltime;
1550         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1551         int num_moved;
1552         int numcheckentities;
1553         static prvm_edict_t *checkentities[MAX_EDICTS];
1554         dp_model_t *pushermodel;
1555         trace_t trace, trace2;
1556         matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1557         unsigned short moved_edicts[MAX_EDICTS];
1558
1559         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])
1560         {
1561                 pusher->fields.server->ltime += movetime;
1562                 return;
1563         }
1564
1565         switch ((int) pusher->fields.server->solid)
1566         {
1567         // LordHavoc: valid pusher types
1568         case SOLID_BSP:
1569         case SOLID_BBOX:
1570         case SOLID_SLIDEBOX:
1571         case SOLID_CORPSE: // LordHavoc: this would be weird...
1572                 break;
1573         // LordHavoc: no collisions
1574         case SOLID_NOT:
1575         case SOLID_TRIGGER:
1576                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1577                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1578                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1579                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1580                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1581                 pusher->fields.server->ltime += movetime;
1582                 SV_LinkEdict(pusher);
1583                 return;
1584         default:
1585                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1586                 return;
1587         }
1588         index = (int) pusher->fields.server->modelindex;
1589         if (index < 1 || index >= MAX_MODELS)
1590         {
1591                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1592                 return;
1593         }
1594         pushermodel = sv.models[index];
1595         pusherowner = pusher->fields.server->owner;
1596         pusherprog = PRVM_EDICT_TO_PROG(pusher);
1597
1598         rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1599
1600         movetime2 = movetime;
1601         VectorScale(pusher->fields.server->velocity, movetime2, move1);
1602         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1603         if (moveangle[0] || moveangle[2])
1604         {
1605                 for (i = 0;i < 3;i++)
1606                 {
1607                         if (move1[i] > 0)
1608                         {
1609                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1610                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1611                         }
1612                         else
1613                         {
1614                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1615                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1616                         }
1617                 }
1618         }
1619         else if (moveangle[1])
1620         {
1621                 for (i = 0;i < 3;i++)
1622                 {
1623                         if (move1[i] > 0)
1624                         {
1625                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1626                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1627                         }
1628                         else
1629                         {
1630                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1631                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1632                         }
1633                 }
1634         }
1635         else
1636         {
1637                 for (i = 0;i < 3;i++)
1638                 {
1639                         if (move1[i] > 0)
1640                         {
1641                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1642                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1643                         }
1644                         else
1645                         {
1646                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1647                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1648                         }
1649                 }
1650         }
1651
1652         VectorNegate (moveangle, a);
1653         AngleVectorsFLU (a, forward, left, up);
1654
1655         VectorCopy (pusher->fields.server->origin, pushorig);
1656         VectorCopy (pusher->fields.server->angles, pushang);
1657         pushltime = pusher->fields.server->ltime;
1658
1659 // move the pusher to its final position
1660
1661         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1662         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1663         pusher->fields.server->ltime += movetime;
1664         SV_LinkEdict(pusher);
1665
1666         pushermodel = NULL;
1667         if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1668                 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1669         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);
1670         Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1671
1672         savesolid = pusher->fields.server->solid;
1673
1674 // see if any solid entities are inside the final position
1675         num_moved = 0;
1676
1677         numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1678         for (e = 0;e < numcheckentities;e++)
1679         {
1680                 prvm_edict_t *check = checkentities[e];
1681                 int movetype = (int)check->fields.server->movetype;
1682                 switch(movetype)
1683                 {
1684                 case MOVETYPE_NONE:
1685                 case MOVETYPE_PUSH:
1686                 case MOVETYPE_FOLLOW:
1687                 case MOVETYPE_NOCLIP:
1688                 case MOVETYPE_FAKEPUSH:
1689                         continue;
1690                 default:
1691                         break;
1692                 }
1693
1694                 if (check->fields.server->owner == pusherprog)
1695                         continue;
1696
1697                 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1698                         continue;
1699
1700                 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1701
1702                 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1703                 check->priv.server->waterposition_forceupdate = true;
1704
1705                 checkcontents = SV_GenericHitSuperContentsMask(check);
1706
1707                 // if the entity is standing on the pusher, it will definitely be moved
1708                 // if the entity is not standing on the pusher, but is in the pusher's
1709                 // final position, move it
1710                 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1711                 {
1712                         Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1713                         //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1714                         if (!trace.startsolid)
1715                         {
1716                                 //Con_Printf("- not in solid\n");
1717                                 continue;
1718                         }
1719                 }
1720
1721                 if (rotated)
1722                 {
1723                         vec3_t org2;
1724                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1725                         org2[0] = DotProduct (org, forward);
1726                         org2[1] = DotProduct (org, left);
1727                         org2[2] = DotProduct (org, up);
1728                         VectorSubtract (org2, org, move);
1729                         VectorAdd (move, move1, move);
1730                 }
1731                 else
1732                         VectorCopy (move1, move);
1733
1734                 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1735
1736                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1737                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1738                 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1739
1740                 // physics objects need better collisions than this code can do
1741                 if (movetype == MOVETYPE_PHYSICS)
1742                 {
1743                         VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1744                         SV_LinkEdict(check);
1745                         SV_LinkEdict_TouchAreaGrid(check);
1746                         continue;
1747                 }
1748
1749                 // try moving the contacted entity
1750                 pusher->fields.server->solid = SOLID_NOT;
1751                 if(!SV_PushEntity (&trace, check, move, true, true))
1752                 {
1753                         // entity "check" got teleported
1754                         check->fields.server->angles[1] += trace.fraction * moveangle[1];
1755                         pusher->fields.server->solid = savesolid; // was SOLID_BSP
1756                         continue; // pushed enough
1757                 }
1758                 // FIXME: turn players specially
1759                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1760                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1761                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1762
1763                 // this trace.fraction < 1 check causes items to fall off of pushers
1764                 // if they pass under or through a wall
1765                 // the groundentity check causes items to fall off of ledges
1766                 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1767                         check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1768
1769                 // if it is still inside the pusher, block
1770                 Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1771                 if (trace.startsolid)
1772                 {
1773                         // try moving the contacted entity a tiny bit further to account for precision errors
1774                         vec3_t move2;
1775                         pusher->fields.server->solid = SOLID_NOT;
1776                         VectorScale(move, 1.1, move2);
1777                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1778                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1779                         if(!SV_PushEntity (&trace2, check, move2, true, true))
1780                         {
1781                                 // entity "check" got teleported
1782                                 continue;
1783                         }
1784                         pusher->fields.server->solid = savesolid;
1785                         Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1786                         if (trace.startsolid)
1787                         {
1788                                 // try moving the contacted entity a tiny bit less to account for precision errors
1789                                 pusher->fields.server->solid = SOLID_NOT;
1790                                 VectorScale(move, 0.9, move2);
1791                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1792                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1793                                 if(!SV_PushEntity (&trace2, check, move2, true, true))
1794                                 {
1795                                         // entity "check" got teleported
1796                                         continue;
1797                                 }
1798                                 pusher->fields.server->solid = savesolid;
1799                                 Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1800                                 if (trace.startsolid)
1801                                 {
1802                                         // still inside pusher, so it's really blocked
1803
1804                                         // fail the move
1805                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1806                                                 continue;
1807                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1808                                         {
1809                                                 // corpse
1810                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1811                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1812                                                 continue;
1813                                         }
1814
1815                                         VectorCopy (pushorig, pusher->fields.server->origin);
1816                                         VectorCopy (pushang, pusher->fields.server->angles);
1817                                         pusher->fields.server->ltime = pushltime;
1818                                         SV_LinkEdict(pusher);
1819
1820                                         // move back any entities we already moved
1821                                         for (i = 0;i < num_moved;i++)
1822                                         {
1823                                                 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1824                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1825                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1826                                                 SV_LinkEdict(ed);
1827                                         }
1828
1829                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1830                                         if (pusher->fields.server->blocked)
1831                                         {
1832                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1833                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1834                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1835                                         }
1836                                         break;
1837                                 }
1838                         }
1839                 }
1840         }
1841         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1842         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1843         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1844 }
1845
1846 /*
1847 ================
1848 SV_Physics_Pusher
1849
1850 ================
1851 */
1852 void SV_Physics_Pusher (prvm_edict_t *ent)
1853 {
1854         float thinktime, oldltime, movetime;
1855
1856         oldltime = ent->fields.server->ltime;
1857
1858         thinktime = ent->fields.server->nextthink;
1859         if (thinktime < ent->fields.server->ltime + sv.frametime)
1860         {
1861                 movetime = thinktime - ent->fields.server->ltime;
1862                 if (movetime < 0)
1863                         movetime = 0;
1864         }
1865         else
1866                 movetime = sv.frametime;
1867
1868         if (movetime)
1869                 // advances ent->fields.server->ltime if not blocked
1870                 SV_PushMove (ent, movetime);
1871
1872         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1873         {
1874                 ent->fields.server->nextthink = 0;
1875                 prog->globals.server->time = sv.time;
1876                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1877                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1878                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1879         }
1880 }
1881
1882
1883 /*
1884 ===============================================================================
1885
1886 CLIENT MOVEMENT
1887
1888 ===============================================================================
1889 */
1890
1891 static float unstickoffsets[] =
1892 {
1893         // poutting -/+z changes first as they are least weird
1894          0,  0,  -1,
1895          0,  0,  1,
1896          // x or y changes
1897         -1,  0,  0,
1898          1,  0,  0,
1899          0, -1,  0,
1900          0,  1,  0,
1901          // x and y changes
1902         -1, -1,  0,
1903          1, -1,  0,
1904         -1,  1,  0,
1905          1,  1,  0,
1906 };
1907
1908 typedef enum unstickresult_e
1909 {
1910         UNSTICK_STUCK = 0,
1911         UNSTICK_GOOD = 1,
1912         UNSTICK_UNSTUCK = 2
1913 }
1914 unstickresult_t;
1915
1916 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1917 {
1918         int i, maxunstick;
1919
1920         // if not stuck in a bmodel, just return
1921         if (!SV_TestEntityPosition(ent, vec3_origin))
1922                 return UNSTICK_GOOD;
1923
1924         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1925         {
1926                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1927                 {
1928                         VectorCopy(unstickoffsets + i, offset);
1929                         SV_LinkEdict(ent);
1930                         //SV_LinkEdict_TouchAreaGrid(ent);
1931                         return UNSTICK_UNSTUCK;
1932                 }
1933         }
1934
1935         maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1936         // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1937
1938         for(i = 2; i <= maxunstick; ++i)
1939         {
1940                 VectorClear(offset);
1941                 offset[2] = -i;
1942                 if (!SV_TestEntityPosition(ent, offset))
1943                 {
1944                         SV_LinkEdict(ent);
1945                         //SV_LinkEdict_TouchAreaGrid(ent);
1946                         return UNSTICK_UNSTUCK;
1947                 }
1948                 offset[2] = i;
1949                 if (!SV_TestEntityPosition(ent, offset))
1950                 {
1951                         SV_LinkEdict(ent);
1952                         //SV_LinkEdict_TouchAreaGrid(ent);
1953                         return UNSTICK_UNSTUCK;
1954                 }
1955         }
1956
1957         return UNSTICK_STUCK;
1958 }
1959
1960 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1961 {
1962         vec3_t offset;
1963         switch(SV_UnstickEntityReturnOffset(ent, offset))
1964         {
1965                 case UNSTICK_GOOD:
1966                         return true;
1967                 case UNSTICK_UNSTUCK:
1968                         Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), offset[0], offset[1], offset[2]);
1969                         return true;
1970                 case UNSTICK_STUCK:
1971                         if (developer.integer >= 100)
1972                                 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1973                         return false;
1974                 default:
1975                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1976                         return false;
1977         }
1978 }
1979
1980 /*
1981 =============
1982 SV_CheckStuck
1983
1984 This is a big hack to try and fix the rare case of getting stuck in the world
1985 clipping hull.
1986 =============
1987 */
1988 void SV_CheckStuck (prvm_edict_t *ent)
1989 {
1990         vec3_t offset;
1991
1992         switch(SV_UnstickEntityReturnOffset(ent, offset))
1993         {
1994                 case UNSTICK_GOOD:
1995                         VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1996                         break;
1997                 case UNSTICK_UNSTUCK:
1998                         Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), offset[0], offset[1], offset[2]);
1999                         break;
2000                 case UNSTICK_STUCK:
2001                         VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
2002                         if (!SV_TestEntityPosition(ent, offset))
2003                         {
2004                                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2005                                 SV_LinkEdict(ent);
2006                                 //SV_LinkEdict_TouchAreaGrid(ent);
2007                         }
2008                         else
2009                                 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2010                         break;
2011                 default:
2012                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2013         }
2014 }
2015
2016
2017 /*
2018 =============
2019 SV_CheckWater
2020 =============
2021 */
2022 qboolean SV_CheckWater (prvm_edict_t *ent)
2023 {
2024         int cont;
2025         int nNativeContents;
2026         vec3_t point;
2027
2028         point[0] = ent->fields.server->origin[0];
2029         point[1] = ent->fields.server->origin[1];
2030         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2031
2032         // DRESK - Support for Entity Contents Transition Event
2033         // NOTE: Some logic needed to be slightly re-ordered
2034         // to not affect performance and allow for the feature.
2035
2036         // Acquire Super Contents Prior to Resets
2037         cont = SV_PointSuperContents(point);
2038         // Acquire Native Contents Here
2039         nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2040
2041         // DRESK - Support for Entity Contents Transition Event
2042         if(ent->fields.server->watertype)
2043                 // Entity did NOT Spawn; Check
2044                 SV_CheckContentsTransition(ent, nNativeContents);
2045
2046
2047         ent->fields.server->waterlevel = 0;
2048         ent->fields.server->watertype = CONTENTS_EMPTY;
2049         cont = SV_PointSuperContents(point);
2050         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2051         {
2052                 ent->fields.server->watertype = nNativeContents;
2053                 ent->fields.server->waterlevel = 1;
2054                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2055                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2056                 {
2057                         ent->fields.server->waterlevel = 2;
2058                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2059                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2060                                 ent->fields.server->waterlevel = 3;
2061                 }
2062         }
2063
2064         return ent->fields.server->waterlevel > 1;
2065 }
2066
2067 /*
2068 ============
2069 SV_WallFriction
2070
2071 ============
2072 */
2073 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2074 {
2075         float d, i;
2076         vec3_t forward, into, side;
2077
2078         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2079         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2080         {
2081                 // cut the tangential velocity
2082                 i = DotProduct (stepnormal, ent->fields.server->velocity);
2083                 VectorScale (stepnormal, i, into);
2084                 VectorSubtract (ent->fields.server->velocity, into, side);
2085                 ent->fields.server->velocity[0] = side[0] * (1 + d);
2086                 ent->fields.server->velocity[1] = side[1] * (1 + d);
2087         }
2088 }
2089
2090 #if 0
2091 /*
2092 =====================
2093 SV_TryUnstick
2094
2095 Player has come to a dead stop, possibly due to the problem with limited
2096 float precision at some angle joins in the BSP hull.
2097
2098 Try fixing by pushing one pixel in each direction.
2099
2100 This is a hack, but in the interest of good gameplay...
2101 ======================
2102 */
2103 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2104 {
2105         int i, clip;
2106         vec3_t oldorg, dir;
2107
2108         VectorCopy (ent->fields.server->origin, oldorg);
2109         VectorClear (dir);
2110
2111         for (i=0 ; i<8 ; i++)
2112         {
2113                 // try pushing a little in an axial direction
2114                 switch (i)
2115                 {
2116                         case 0: dir[0] = 2; dir[1] = 0; break;
2117                         case 1: dir[0] = 0; dir[1] = 2; break;
2118                         case 2: dir[0] = -2; dir[1] = 0; break;
2119                         case 3: dir[0] = 0; dir[1] = -2; break;
2120                         case 4: dir[0] = 2; dir[1] = 2; break;
2121                         case 5: dir[0] = -2; dir[1] = 2; break;
2122                         case 6: dir[0] = 2; dir[1] = -2; break;
2123                         case 7: dir[0] = -2; dir[1] = -2; break;
2124                 }
2125
2126                 SV_PushEntity (&trace, ent, dir, false, true);
2127
2128                 // retry the original move
2129                 ent->fields.server->velocity[0] = oldvel[0];
2130                 ent->fields.server->velocity[1] = oldvel[1];
2131                 ent->fields.server->velocity[2] = 0;
2132                 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2133
2134                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2135                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2136                 {
2137                         Con_DPrint("TryUnstick - success.\n");
2138                         return clip;
2139                 }
2140
2141                 // go back to the original pos and try again
2142                 VectorCopy (oldorg, ent->fields.server->origin);
2143         }
2144
2145         // still not moving
2146         VectorClear (ent->fields.server->velocity);
2147         Con_DPrint("TryUnstick - failure.\n");
2148         return 7;
2149 }
2150 #endif
2151
2152 /*
2153 =====================
2154 SV_WalkMove
2155
2156 Only used by players
2157 ======================
2158 */
2159 void SV_WalkMove (prvm_edict_t *ent)
2160 {
2161         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2162         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2163         trace_t downtrace, trace;
2164         qboolean applygravity;
2165
2166         // if frametime is 0 (due to client sending the same timestamp twice),
2167         // don't move
2168         if (sv.frametime <= 0)
2169                 return;
2170
2171         SV_CheckStuck (ent);
2172
2173         applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2174
2175         hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2176
2177         SV_CheckVelocity(ent);
2178
2179         // do a regular slide move unless it looks like you ran into a step
2180         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2181
2182         VectorCopy (ent->fields.server->origin, start_origin);
2183         VectorCopy (ent->fields.server->velocity, start_velocity);
2184
2185         clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2186
2187         // if the move did not hit the ground at any point, we're not on ground
2188         if (!(clip & 1))
2189                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2190
2191         SV_CheckVelocity(ent);
2192         SV_LinkEdict(ent);
2193         SV_LinkEdict_TouchAreaGrid(ent);
2194
2195         if(clip & 8) // teleport
2196                 return;
2197
2198         if ((int)ent->fields.server->flags & FL_WATERJUMP)
2199                 return;
2200
2201         if (sv_nostep.integer)
2202                 return;
2203
2204         VectorCopy(ent->fields.server->origin, originalmove_origin);
2205         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2206         originalmove_clip = clip;
2207         originalmove_flags = (int)ent->fields.server->flags;
2208         originalmove_groundentity = ent->fields.server->groundentity;
2209
2210         // if move didn't block on a step, return
2211         if (clip & 2)
2212         {
2213                 // if move was not trying to move into the step, return
2214                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2215                         return;
2216
2217                 if (ent->fields.server->movetype != MOVETYPE_FLY)
2218                 {
2219                         // return if gibbed by a trigger
2220                         if (ent->fields.server->movetype != MOVETYPE_WALK)
2221                                 return;
2222
2223                         // only step up while jumping if that is enabled
2224                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2225                                 if (!oldonground && ent->fields.server->waterlevel == 0)
2226                                         return;
2227                 }
2228
2229                 // try moving up and forward to go up a step
2230                 // back to start pos
2231                 VectorCopy (start_origin, ent->fields.server->origin);
2232                 VectorCopy (start_velocity, ent->fields.server->velocity);
2233
2234                 // move up
2235                 VectorClear (upmove);
2236                 upmove[2] = sv_stepheight.value;
2237                 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2238                 {
2239                         // we got teleported when upstepping... must abort the move
2240                         return;
2241                 }
2242
2243                 // move forward
2244                 ent->fields.server->velocity[2] = 0;
2245                 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2246                 ent->fields.server->velocity[2] += start_velocity[2];
2247                 if(clip & 8)
2248                 {
2249                         // we got teleported when upstepping... must abort the move
2250                         // note that z velocity handling may not be what QC expects here, but we cannot help it
2251                         return;
2252                 }
2253
2254                 SV_CheckVelocity(ent);
2255                 SV_LinkEdict(ent);
2256                 SV_LinkEdict_TouchAreaGrid(ent);
2257
2258                 // check for stuckness, possibly due to the limited precision of floats
2259                 // in the clipping hulls
2260                 if (clip
2261                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2262                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2263                 {
2264                         //Con_Printf("wall\n");
2265                         // stepping up didn't make any progress, revert to original move
2266                         VectorCopy(originalmove_origin, ent->fields.server->origin);
2267                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2268                         //clip = originalmove_clip;
2269                         ent->fields.server->flags = originalmove_flags;
2270                         ent->fields.server->groundentity = originalmove_groundentity;
2271                         // now try to unstick if needed
2272                         //clip = SV_TryUnstick (ent, oldvel);
2273                         return;
2274                 }
2275
2276                 //Con_Printf("step - ");
2277
2278                 // extra friction based on view angle
2279                 if (clip & 2 && sv_wallfriction.integer)
2280                         SV_WallFriction (ent, stepnormal);
2281         }
2282         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2283         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))
2284                 return;
2285
2286         // move down
2287         VectorClear (downmove);
2288         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2289         if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2290         {
2291                 // we got teleported when downstepping... must abort the move
2292                 return;
2293         }
2294
2295         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2296         {
2297                 // this has been disabled so that you can't jump when you are stepping
2298                 // up while already jumping (also known as the Quake2 double jump bug)
2299 #if 0
2300                 // LordHavoc: disabled this check so you can walk on monsters/players
2301                 //if (ent->fields.server->solid == SOLID_BSP)
2302                 {
2303                         //Con_Printf("onground\n");
2304                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
2305                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2306                 }
2307 #endif
2308         }
2309         else
2310         {
2311                 //Con_Printf("slope\n");
2312                 // if the push down didn't end up on good ground, use the move without
2313                 // the step up.  This happens near wall / slope combinations, and can
2314                 // cause the player to hop up higher on a slope too steep to climb
2315                 VectorCopy(originalmove_origin, ent->fields.server->origin);
2316                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2317                 //clip = originalmove_clip;
2318                 ent->fields.server->flags = originalmove_flags;
2319                 ent->fields.server->groundentity = originalmove_groundentity;
2320         }
2321
2322         SV_CheckVelocity(ent);
2323         SV_LinkEdict(ent);
2324         SV_LinkEdict_TouchAreaGrid(ent);
2325 }
2326
2327 //============================================================================
2328
2329 /*
2330 =============
2331 SV_Physics_Follow
2332
2333 Entities that are "stuck" to another entity
2334 =============
2335 */
2336 void SV_Physics_Follow (prvm_edict_t *ent)
2337 {
2338         vec3_t vf, vr, vu, angles, v;
2339         prvm_edict_t *e;
2340
2341         // regular thinking
2342         if (!SV_RunThink (ent))
2343                 return;
2344
2345         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2346         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2347         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])
2348         {
2349                 // quick case for no rotation
2350                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2351         }
2352         else
2353         {
2354                 angles[0] = -ent->fields.server->punchangle[0];
2355                 angles[1] =  ent->fields.server->punchangle[1];
2356                 angles[2] =  ent->fields.server->punchangle[2];
2357                 AngleVectors (angles, vf, vr, vu);
2358                 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];
2359                 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];
2360                 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];
2361                 angles[0] = -e->fields.server->angles[0];
2362                 angles[1] =  e->fields.server->angles[1];
2363                 angles[2] =  e->fields.server->angles[2];
2364                 AngleVectors (angles, vf, vr, vu);
2365                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2366                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2367                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2368         }
2369         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2370         SV_LinkEdict(ent);
2371         //SV_LinkEdict_TouchAreaGrid(ent);
2372 }
2373
2374 /*
2375 ==============================================================================
2376
2377 TOSS / BOUNCE
2378
2379 ==============================================================================
2380 */
2381
2382 /*
2383 =============
2384 SV_CheckWaterTransition
2385
2386 =============
2387 */
2388 void SV_CheckWaterTransition (prvm_edict_t *ent)
2389 {
2390         int cont;
2391         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2392         if (!ent->fields.server->watertype)
2393         {
2394                 // just spawned here
2395                 ent->fields.server->watertype = cont;
2396                 ent->fields.server->waterlevel = 1;
2397                 return;
2398         }
2399
2400         // DRESK - Support for Entity Contents Transition Event
2401         // NOTE: Call here BEFORE updating the watertype below,
2402         // and suppress watersplash sound if a valid function
2403         // call was made to allow for custom "splash" sounds.
2404         if( !SV_CheckContentsTransition(ent, cont) )
2405         { // Contents Transition Function Invalid; Potentially Play Water Sound
2406                 // check if the entity crossed into or out of water
2407                 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2408                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2409         }
2410
2411         if (cont <= CONTENTS_WATER)
2412         {
2413                 ent->fields.server->watertype = cont;
2414                 ent->fields.server->waterlevel = 1;
2415         }
2416         else
2417         {
2418                 ent->fields.server->watertype = CONTENTS_EMPTY;
2419                 ent->fields.server->waterlevel = 0;
2420         }
2421 }
2422
2423 /*
2424 =============
2425 SV_Physics_Toss
2426
2427 Toss, bounce, and fly movement.  When onground, do nothing.
2428 =============
2429 */
2430 void SV_Physics_Toss (prvm_edict_t *ent)
2431 {
2432         trace_t trace;
2433         vec3_t move;
2434         vec_t movetime;
2435         int bump;
2436
2437 // if onground, return without moving
2438         if ((int)ent->fields.server->flags & FL_ONGROUND)
2439         {
2440                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2441                 {
2442                         // don't stick to ground if onground and moving upward
2443                         ent->fields.server->flags -= FL_ONGROUND;
2444                 }
2445                 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2446                 {
2447                         // we can trust FL_ONGROUND if groundentity is world because it never moves
2448                         return;
2449                 }
2450                 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2451                 {
2452                         // if ent was supported by a brush model on previous frame,
2453                         // and groundentity is now freed, set groundentity to 0 (world)
2454                         // which leaves it suspended in the air
2455                         ent->fields.server->groundentity = 0;
2456                         if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2457                                 return;
2458                 }
2459         }
2460         ent->priv.server->suspendedinairflag = false;
2461
2462         SV_CheckVelocity (ent);
2463
2464 // add gravity
2465         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2466                 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2467
2468 // move angles
2469         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2470
2471         movetime = sv.frametime;
2472         for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2473         {
2474         // move origin
2475                 VectorScale (ent->fields.server->velocity, movetime, move);
2476                 if(!SV_PushEntity (&trace, ent, move, true, true))
2477                         return; // teleported
2478                 if (ent->priv.server->free)
2479                         return;
2480                 if (trace.bmodelstartsolid)
2481                 {
2482                         // try to unstick the entity
2483                         SV_UnstickEntity(ent);
2484                         if(!SV_PushEntity (&trace, ent, move, false, true))
2485                                 return; // teleported
2486                         if (ent->priv.server->free)
2487                                 return;
2488                 }
2489                 if (trace.fraction == 1)
2490                         break;
2491                 movetime *= 1 - min(1, trace.fraction);
2492                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2493                 {
2494                         prvm_eval_t *val;
2495                         float bouncefactor = 1.0f;
2496                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2497                         if (val!=0 && val->_float)
2498                                 bouncefactor = val->_float;
2499
2500                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2501                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2502                 }
2503                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2504                 {
2505                         float d, ent_gravity;
2506                         prvm_eval_t *val;
2507                         float bouncefactor = 0.5f;
2508                         float bouncestop = 60.0f / 800.0f;
2509
2510                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2511                         if (val!=0 && val->_float)
2512                                 bouncefactor = val->_float;
2513
2514                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2515                         if (val!=0 && val->_float)
2516                                 bouncestop = val->_float;
2517
2518                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2519                         // LordHavoc: fixed grenades not bouncing when fired down a slope
2520                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2521                         if (val!=0 && val->_float)
2522                                 ent_gravity = val->_float;
2523                         else
2524                                 ent_gravity = 1.0;
2525                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
2526                         {
2527                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2528                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2529                                 {
2530                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2531                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2532                                         VectorClear (ent->fields.server->velocity);
2533                                         VectorClear (ent->fields.server->avelocity);
2534                                 }
2535                                 else
2536                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2537                         }
2538                         else
2539                         {
2540                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2541                                 {
2542                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2543                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2544                                         VectorClear (ent->fields.server->velocity);
2545                                         VectorClear (ent->fields.server->avelocity);
2546                                 }
2547                                 else
2548                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2549                         }
2550                 }
2551                 else
2552                 {
2553                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2554                         if (trace.plane.normal[2] > 0.7)
2555                         {
2556                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2557                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2558                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2559                                         ent->priv.server->suspendedinairflag = true;
2560                                 VectorClear (ent->fields.server->velocity);
2561                                 VectorClear (ent->fields.server->avelocity);
2562                         }
2563                         else
2564                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2565                 }
2566                 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2567                         break;
2568         }
2569
2570 // check for in water
2571         SV_CheckWaterTransition (ent);
2572 }
2573
2574 /*
2575 ===============================================================================
2576
2577 STEPPING MOVEMENT
2578
2579 ===============================================================================
2580 */
2581
2582 /*
2583 =============
2584 SV_Physics_Step
2585
2586 Monsters freefall when they don't have a ground entity, otherwise
2587 all movement is done with discrete steps.
2588
2589 This is also used for objects that have become still on the ground, but
2590 will fall if the floor is pulled out from under them.
2591 =============
2592 */
2593 void SV_Physics_Step (prvm_edict_t *ent)
2594 {
2595         int flags = (int)ent->fields.server->flags;
2596
2597         // DRESK
2598         // Backup Velocity in the event that movetypesteplandevent is called,
2599         // to provide a parameter with the entity's velocity at impact.
2600         prvm_eval_t *movetypesteplandevent;
2601         vec3_t backupVelocity;
2602         VectorCopy(ent->fields.server->velocity, backupVelocity);
2603         // don't fall at all if fly/swim
2604         if (!(flags & (FL_FLY | FL_SWIM)))
2605         {
2606                 if (flags & FL_ONGROUND)
2607                 {
2608                         // freefall if onground and moving upward
2609                         // freefall if not standing on a world surface (it may be a lift or trap door)
2610                         if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2611                         {
2612                                 ent->fields.server->flags -= FL_ONGROUND;
2613                                 SV_CheckVelocity(ent);
2614                                 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2615                                 SV_LinkEdict(ent);
2616                                 SV_LinkEdict_TouchAreaGrid(ent);
2617                                 ent->priv.server->waterposition_forceupdate = true;
2618                         }
2619                 }
2620                 else
2621                 {
2622                         // freefall if not onground
2623                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2624
2625                         SV_CheckVelocity(ent);
2626                         SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2627                         SV_LinkEdict(ent);
2628                         SV_LinkEdict_TouchAreaGrid(ent);
2629
2630                         // just hit ground
2631                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2632                         {
2633                                 // DRESK - Check for Entity Land Event Function
2634                                 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2635
2636                                 if(movetypesteplandevent->function)
2637                                 { // Valid Function; Execute
2638                                         // Prepare Parameters
2639                                                 // Assign Velocity at Impact
2640                                                 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2641                                                 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2642                                                 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2643                                                 // Assign Self
2644                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2645                                         // Execute VM Function
2646                                         PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2647                                 }
2648                                 else
2649                                 // Check for Engine Landing Sound
2650                                 if(sv_sound_land.string)
2651                                         SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2652                         }
2653                         ent->priv.server->waterposition_forceupdate = true;
2654                 }
2655         }
2656
2657 // regular thinking
2658         if (!SV_RunThink(ent))
2659                 return;
2660
2661         if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2662         {
2663                 ent->priv.server->waterposition_forceupdate = false;
2664                 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2665                 SV_CheckWaterTransition(ent);
2666         }
2667 }
2668
2669 //============================================================================
2670
2671 static void SV_Physics_Entity (prvm_edict_t *ent)
2672 {
2673         // don't run think/move on newly spawned projectiles as it messes up
2674         // movement interpolation and rocket trails, and is inconsistent with
2675         // respect to entities spawned in the same frame
2676         // (if an ent spawns a higher numbered ent, it moves in the same frame,
2677         //  but if it spawns a lower numbered ent, it doesn't - this never moves
2678         //  ents in the first frame regardless)
2679         qboolean runmove = ent->priv.server->move;
2680         ent->priv.server->move = true;
2681         if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2682                 return;
2683         switch ((int) ent->fields.server->movetype)
2684         {
2685         case MOVETYPE_PUSH:
2686         case MOVETYPE_FAKEPUSH:
2687                 SV_Physics_Pusher (ent);
2688                 break;
2689         case MOVETYPE_NONE:
2690                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2691                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2692                         SV_RunThink (ent);
2693                 break;
2694         case MOVETYPE_FOLLOW:
2695                 SV_Physics_Follow (ent);
2696                 break;
2697         case MOVETYPE_NOCLIP:
2698                 if (SV_RunThink(ent))
2699                 {
2700                         SV_CheckWater(ent);
2701                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2702                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2703                 }
2704                 SV_LinkEdict(ent);
2705                 break;
2706         case MOVETYPE_STEP:
2707                 SV_Physics_Step (ent);
2708                 break;
2709         case MOVETYPE_WALK:
2710                 if (SV_RunThink (ent))
2711                         SV_WalkMove (ent);
2712                 break;
2713         case MOVETYPE_TOSS:
2714         case MOVETYPE_BOUNCE:
2715         case MOVETYPE_BOUNCEMISSILE:
2716         case MOVETYPE_FLYMISSILE:
2717         case MOVETYPE_FLY:
2718                 // regular thinking
2719                 if (SV_RunThink (ent))
2720                         SV_Physics_Toss (ent);
2721                 break;
2722         case MOVETYPE_PHYSICS:
2723                 if (SV_RunThink(ent))
2724                 {
2725                         SV_LinkEdict(ent);
2726                         SV_LinkEdict_TouchAreaGrid(ent);
2727                 }
2728                 break;
2729         default:
2730                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2731                 break;
2732         }
2733 }
2734
2735 void SV_Physics_ClientMove(void)
2736 {
2737         prvm_edict_t *ent;
2738         ent = host_client->edict;
2739
2740         // call player physics, this needs the proper frametime
2741         prog->globals.server->frametime = sv.frametime;
2742         SV_ClientThink();
2743
2744         // call standard client pre-think, with frametime = 0
2745         prog->globals.server->time = sv.time;
2746         prog->globals.server->frametime = 0;
2747         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2748         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2749         prog->globals.server->frametime = sv.frametime;
2750
2751         // make sure the velocity is sane (not a NaN)
2752         SV_CheckVelocity(ent);
2753
2754         // perform MOVETYPE_WALK behavior
2755         SV_WalkMove (ent);
2756
2757         // call standard player post-think, with frametime = 0
2758         prog->globals.server->time = sv.time;
2759         prog->globals.server->frametime = 0;
2760         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2761         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2762         prog->globals.server->frametime = sv.frametime;
2763
2764         if(ent->fields.server->fixangle)
2765         {
2766                 // angle fixing was requested by physics code...
2767                 // so store the current angles for later use
2768                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2769                 host_client->fixangle_angles_set = TRUE;
2770
2771                 // and clear fixangle for the next frame
2772                 ent->fields.server->fixangle = 0;
2773         }
2774 }
2775
2776 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2777 {
2778         // don't do physics on disconnected clients, FrikBot relies on this
2779         if (!host_client->spawned)
2780                 return;
2781
2782         // make sure the velocity is sane (not a NaN)
2783         SV_CheckVelocity(ent);
2784
2785         // don't run physics here if running asynchronously
2786         if (host_client->clmovement_inputtimeout <= 0)
2787         {
2788                 SV_ClientThink();
2789                 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2790         }
2791
2792         // make sure the velocity is still sane (not a NaN)
2793         SV_CheckVelocity(ent);
2794
2795         // call standard client pre-think
2796         prog->globals.server->time = sv.time;
2797         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2798         PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2799
2800         // make sure the velocity is still sane (not a NaN)
2801         SV_CheckVelocity(ent);
2802 }
2803
2804 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2805 {
2806         // don't do physics on disconnected clients, FrikBot relies on this
2807         if (!host_client->spawned)
2808                 return;
2809
2810         // make sure the velocity is sane (not a NaN)
2811         SV_CheckVelocity(ent);
2812
2813         // call standard player post-think
2814         prog->globals.server->time = sv.time;
2815         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2816         PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2817
2818         // make sure the velocity is still sane (not a NaN)
2819         SV_CheckVelocity(ent);
2820
2821         if(ent->fields.server->fixangle)
2822         {
2823                 // angle fixing was requested by physics code...
2824                 // so store the current angles for later use
2825                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2826                 host_client->fixangle_angles_set = TRUE;
2827
2828                 // and clear fixangle for the next frame
2829                 ent->fields.server->fixangle = 0;
2830         }
2831
2832         // decrement the countdown variable used to decide when to go back to
2833         // synchronous physics
2834         if (host_client->clmovement_inputtimeout > sv.frametime)
2835                 host_client->clmovement_inputtimeout -= sv.frametime;
2836         else
2837                 host_client->clmovement_inputtimeout = 0;
2838 }
2839
2840 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2841 {
2842         // don't do physics on disconnected clients, FrikBot relies on this
2843         if (!host_client->spawned)
2844         {
2845                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2846                 return;
2847         }
2848
2849         // make sure the velocity is sane (not a NaN)
2850         SV_CheckVelocity(ent);
2851
2852         switch ((int) ent->fields.server->movetype)
2853         {
2854         case MOVETYPE_PUSH:
2855         case MOVETYPE_FAKEPUSH:
2856                 SV_Physics_Pusher (ent);
2857                 break;
2858         case MOVETYPE_NONE:
2859                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2860                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2861                         SV_RunThink (ent);
2862                 break;
2863         case MOVETYPE_FOLLOW:
2864                 SV_Physics_Follow (ent);
2865                 break;
2866         case MOVETYPE_NOCLIP:
2867                 SV_RunThink(ent);
2868                 SV_CheckWater(ent);
2869                 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2870                 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2871                 break;
2872         case MOVETYPE_STEP:
2873                 SV_Physics_Step (ent);
2874                 break;
2875         case MOVETYPE_WALK:
2876                 SV_RunThink (ent);
2877                 // don't run physics here if running asynchronously
2878                 if (host_client->clmovement_inputtimeout <= 0)
2879                         SV_WalkMove (ent);
2880                 break;
2881         case MOVETYPE_TOSS:
2882         case MOVETYPE_BOUNCE:
2883         case MOVETYPE_BOUNCEMISSILE:
2884         case MOVETYPE_FLYMISSILE:
2885                 // regular thinking
2886                 SV_RunThink (ent);
2887                 SV_Physics_Toss (ent);
2888                 break;
2889         case MOVETYPE_FLY:
2890                 SV_RunThink (ent);
2891                 SV_WalkMove (ent);
2892                 break;
2893         case MOVETYPE_PHYSICS:
2894                 SV_RunThink (ent);
2895                 break;
2896         default:
2897                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2898                 break;
2899         }
2900
2901         SV_CheckVelocity (ent);
2902
2903         SV_LinkEdict(ent);
2904         SV_LinkEdict_TouchAreaGrid(ent);
2905
2906         SV_CheckVelocity (ent);
2907 }
2908
2909 /*
2910 ================
2911 SV_Physics
2912
2913 ================
2914 */
2915 void SV_Physics (void)
2916 {
2917         int i;
2918         prvm_edict_t *ent;
2919
2920 // let the progs know that a new frame has started
2921         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2922         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2923         prog->globals.server->time = sv.time;
2924         prog->globals.server->frametime = sv.frametime;
2925         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2926
2927         // run physics engine
2928         World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2929
2930 //
2931 // treat each object in turn
2932 //
2933
2934         // if force_retouch, relink all the entities
2935         if (prog->globals.server->force_retouch > 0)
2936                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2937                         if (!ent->priv.server->free)
2938                                 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2939
2940         if (sv_gameplayfix_consistentplayerprethink.integer)
2941         {
2942                 // run physics on the client entities in 3 stages
2943                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2944                         if (!ent->priv.server->free)
2945                                 SV_Physics_ClientEntity_PreThink(ent);
2946
2947                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2948                         if (!ent->priv.server->free)
2949                                 SV_Physics_ClientEntity(ent);
2950
2951                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2952                         if (!ent->priv.server->free)
2953                                 SV_Physics_ClientEntity_PostThink(ent);
2954         }
2955         else
2956         {
2957                 // run physics on the client entities
2958                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2959                 {
2960                         if (!ent->priv.server->free)
2961                         {
2962                                 SV_Physics_ClientEntity_PreThink(ent);
2963                                 SV_Physics_ClientEntity(ent);
2964                                 SV_Physics_ClientEntity_PostThink(ent);
2965                         }
2966                 }
2967         }
2968
2969         // run physics on all the non-client entities
2970         if (!sv_freezenonclients.integer)
2971         {
2972                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2973                         if (!ent->priv.server->free)
2974                                 SV_Physics_Entity(ent);
2975                 // make a second pass to see if any ents spawned this frame and make
2976                 // sure they run their move/think
2977                 if (sv_gameplayfix_delayprojectiles.integer < 0)
2978                         for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2979                                 if (!ent->priv.server->move && !ent->priv.server->free)
2980                                         SV_Physics_Entity(ent);
2981         }
2982
2983         if (prog->globals.server->force_retouch > 0)
2984                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2985
2986         // LordHavoc: endframe support
2987         if (prog->funcoffsets.EndFrame)
2988         {
2989                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2990                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2991                 prog->globals.server->time = sv.time;
2992                 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2993         }
2994
2995         // decrement prog->num_edicts if the highest number entities died
2996         for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2997
2998         if (!sv_freezenonclients.integer)
2999                 sv.time += sv.frametime;
3000 }