]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
we cannot skip the next 3 vars after encounting a vector - so we instead have to...
[xonotic/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23
24 /*
25
26
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
28
29 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
30
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
37
38 solid_edge items only clip against bsp models.
39
40 */
41
42 #define MOVE_EPSILON    0.01
43
44 void SV_Physics_Toss (prvm_edict_t *ent);
45
46 int SV_GetPitchSign(prvm_edict_t *ent)
47 {
48         dp_model_t *model;
49         if (
50                         (model = SV_GetModelFromEdict(ent))
51                         ?
52                         model->type == mod_alias
53                         :
54                         (
55                          (((unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
56                          ||
57                          ((gamemode == GAME_TENEBRAE) && ((unsigned int)ent->fields.server->effects & (16 | 32)))
58                         )
59            )
60                 return -1;
61         return 1;
62 }
63
64 /*
65 ===============================================================================
66
67 LINE TESTING IN HULLS
68
69 ===============================================================================
70 */
71
72 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
73 {
74         prvm_eval_t *val;
75         if (passedict)
76         {
77                 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
78                 if (val && val->_float)
79                         return (int)val->_float;
80                 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
81                 {
82                         if ((int)passedict->fields.server->flags & FL_MONSTER)
83                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
84                         else
85                                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
86                 }
87                 else if (passedict->fields.server->solid == SOLID_CORPSE)
88                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
89                 else if (passedict->fields.server->solid == SOLID_TRIGGER)
90                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
91                 else
92                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
93         }
94         else
95                 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
96 }
97
98 /*
99 ==================
100 SV_TracePoint
101 ==================
102 */
103 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
104 {
105         int i, bodysupercontents;
106         int passedictprog;
107         float pitchsign = 1;
108         prvm_edict_t *traceowner, *touch;
109         trace_t trace;
110         // bounding box of entire move area
111         vec3_t clipboxmins, clipboxmaxs;
112         // size when clipping against monsters
113         vec3_t clipmins2, clipmaxs2;
114         // start and end origin of move
115         vec3_t clipstart;
116         // trace results
117         trace_t cliptrace;
118         // matrices to transform into/out of other entity's space
119         matrix4x4_t matrix, imatrix;
120         // model of other entity
121         dp_model_t *model;
122         // list of entities to test for collisions
123         int numtouchedicts;
124         prvm_edict_t *touchedicts[MAX_EDICTS];
125
126         //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
127
128         VectorCopy(start, clipstart);
129         VectorClear(clipmins2);
130         VectorClear(clipmaxs2);
131 #if COLLISIONPARANOID >= 3
132         Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
133 #endif
134
135         // clip to world
136         Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
137         cliptrace.bmodelstartsolid = cliptrace.startsolid;
138         if (cliptrace.startsolid || cliptrace.fraction < 1)
139                 cliptrace.ent = prog->edicts;
140         if (type == MOVE_WORLDONLY)
141                 goto finished;
142
143         if (type == MOVE_MISSILE)
144         {
145                 // LordHavoc: modified this, was = -15, now -= 15
146                 for (i = 0;i < 3;i++)
147                 {
148                         clipmins2[i] -= 15;
149                         clipmaxs2[i] += 15;
150                 }
151         }
152
153         // create the bounding box of the entire move
154         for (i = 0;i < 3;i++)
155         {
156                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
157                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
158         }
159
160         // debug override to test against everything
161         if (sv_debugmove.integer)
162         {
163                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
164                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
165         }
166
167         // if the passedict is world, make it NULL (to avoid two checks each time)
168         if (passedict == prog->edicts)
169                 passedict = NULL;
170         // precalculate prog value for passedict for comparisons
171         passedictprog = PRVM_EDICT_TO_PROG(passedict);
172         // precalculate passedict's owner edict pointer for comparisons
173         traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
174
175         // clip to entities
176         // because this uses World_EntitiestoBox, we know all entity boxes overlap
177         // the clip region, so we can skip culling checks in the loop below
178         numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
179         if (numtouchedicts > MAX_EDICTS)
180         {
181                 // this never happens
182                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
183                 numtouchedicts = MAX_EDICTS;
184         }
185         for (i = 0;i < numtouchedicts;i++)
186         {
187                 touch = touchedicts[i];
188
189                 if (touch->fields.server->solid < SOLID_BBOX)
190                         continue;
191                 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
192                         continue;
193
194                 if (passedict)
195                 {
196                         // don't clip against self
197                         if (passedict == touch)
198                                 continue;
199                         // don't clip owned entities against owner
200                         if (traceowner == touch)
201                                 continue;
202                         // don't clip owner against owned entities
203                         if (passedictprog == touch->fields.server->owner)
204                                 continue;
205                         // don't clip points against points (they can't collide)
206                         if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
207                                 continue;
208                 }
209
210                 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
211
212                 // might interact, so do an exact clip
213                 model = NULL;
214                 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
215                 {
216                         model = SV_GetModelFromEdict(touch);
217                         pitchsign = SV_GetPitchSign(touch);
218                 }
219                 if (model)
220                         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);
221                 else
222                         Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
223                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
224                 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
225                 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
226                 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
227                 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
228                         Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
229                 else
230                         Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
231
232                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
233         }
234
235 finished:
236         return cliptrace;
237 }
238
239 /*
240 ==================
241 SV_TraceLine
242 ==================
243 */
244 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
245 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
246 #else
247 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
248 #endif
249 {
250         int i, bodysupercontents;
251         int passedictprog;
252         float pitchsign = 1;
253         prvm_edict_t *traceowner, *touch;
254         trace_t trace;
255         // bounding box of entire move area
256         vec3_t clipboxmins, clipboxmaxs;
257         // size when clipping against monsters
258         vec3_t clipmins2, clipmaxs2;
259         // start and end origin of move
260         vec3_t clipstart, clipend;
261         // trace results
262         trace_t cliptrace;
263         // matrices to transform into/out of other entity's space
264         matrix4x4_t matrix, imatrix;
265         // model of other entity
266         dp_model_t *model;
267         // list of entities to test for collisions
268         int numtouchedicts;
269         prvm_edict_t *touchedicts[MAX_EDICTS];
270 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
271         vec3_t end;
272         vec_t len = 0;
273
274         if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
275         {
276                 // TRICK: make the trace 1 qu longer!
277                 VectorSubtract(pEnd, start, end);
278                 len = VectorNormalizeLength(end);
279                 VectorMA(pEnd, collision_endposnudge.value, end, end);
280         }
281         else
282                 VectorCopy(pEnd, end);
283 #endif
284
285         //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
286
287         if (VectorCompare(start, end))
288                 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
289
290         VectorCopy(start, clipstart);
291         VectorCopy(end, clipend);
292         VectorClear(clipmins2);
293         VectorClear(clipmaxs2);
294 #if COLLISIONPARANOID >= 3
295         Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
296 #endif
297
298         // clip to world
299         Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask);
300         cliptrace.bmodelstartsolid = cliptrace.startsolid;
301         if (cliptrace.startsolid || cliptrace.fraction < 1)
302                 cliptrace.ent = prog->edicts;
303         if (type == MOVE_WORLDONLY)
304                 goto finished;
305
306         if (type == MOVE_MISSILE)
307         {
308                 // LordHavoc: modified this, was = -15, now -= 15
309                 for (i = 0;i < 3;i++)
310                 {
311                         clipmins2[i] -= 15;
312                         clipmaxs2[i] += 15;
313                 }
314         }
315
316         // create the bounding box of the entire move
317         for (i = 0;i < 3;i++)
318         {
319                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
320                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
321         }
322
323         // debug override to test against everything
324         if (sv_debugmove.integer)
325         {
326                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
327                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
328         }
329
330         // if the passedict is world, make it NULL (to avoid two checks each time)
331         if (passedict == prog->edicts)
332                 passedict = NULL;
333         // precalculate prog value for passedict for comparisons
334         passedictprog = PRVM_EDICT_TO_PROG(passedict);
335         // precalculate passedict's owner edict pointer for comparisons
336         traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
337
338         // clip to entities
339         // because this uses World_EntitiestoBox, we know all entity boxes overlap
340         // the clip region, so we can skip culling checks in the loop below
341         numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
342         if (numtouchedicts > MAX_EDICTS)
343         {
344                 // this never happens
345                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
346                 numtouchedicts = MAX_EDICTS;
347         }
348         for (i = 0;i < numtouchedicts;i++)
349         {
350                 touch = touchedicts[i];
351
352                 if (touch->fields.server->solid < SOLID_BBOX)
353                         continue;
354                 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
355                         continue;
356
357                 if (passedict)
358                 {
359                         // don't clip against self
360                         if (passedict == touch)
361                                 continue;
362                         // don't clip owned entities against owner
363                         if (traceowner == touch)
364                                 continue;
365                         // don't clip owner against owned entities
366                         if (passedictprog == touch->fields.server->owner)
367                                 continue;
368                         // don't clip points against points (they can't collide)
369                         if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
370                                 continue;
371                 }
372
373                 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
374
375                 // might interact, so do an exact clip
376                 model = NULL;
377                 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
378                 {
379                         model = SV_GetModelFromEdict(touch);
380                         pitchsign = SV_GetPitchSign(touch);
381                 }
382                 if (model)
383                         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);
384                 else
385                         Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
386                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
387                 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
388                 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
389                 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
390                 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
391                         Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
392                 else
393                         Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
394
395                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
396         }
397
398 finished:
399 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
400         if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
401                 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
402 #endif
403         return cliptrace;
404 }
405
406 /*
407 ==================
408 SV_Move
409 ==================
410 */
411 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
412 #if COLLISIONPARANOID >= 1
413 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)
414 #else
415 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)
416 #endif
417 #else
418 #if COLLISIONPARANOID >= 1
419 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)
420 #else
421 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)
422 #endif
423 #endif
424 {
425         vec3_t hullmins, hullmaxs;
426         int i, bodysupercontents;
427         int passedictprog;
428         float pitchsign = 1;
429         qboolean pointtrace;
430         prvm_edict_t *traceowner, *touch;
431         trace_t trace;
432         // bounding box of entire move area
433         vec3_t clipboxmins, clipboxmaxs;
434         // size of the moving object
435         vec3_t clipmins, clipmaxs;
436         // size when clipping against monsters
437         vec3_t clipmins2, clipmaxs2;
438         // start and end origin of move
439         vec3_t clipstart, clipend;
440         // trace results
441         trace_t cliptrace;
442         // matrices to transform into/out of other entity's space
443         matrix4x4_t matrix, imatrix;
444         // model of other entity
445         dp_model_t *model;
446         // list of entities to test for collisions
447         int numtouchedicts;
448         prvm_edict_t *touchedicts[MAX_EDICTS];
449 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
450         vec3_t end;
451         vec_t len = 0;
452
453         if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
454         {
455                 // TRICK: make the trace 1 qu longer!
456                 VectorSubtract(pEnd, start, end);
457                 len = VectorNormalizeLength(end);
458                 VectorMA(pEnd, collision_endposnudge.value, end, end);
459         }
460         else
461                 VectorCopy(pEnd, end);
462 #endif
463
464         if (VectorCompare(mins, maxs))
465         {
466                 vec3_t shiftstart, shiftend;
467                 VectorAdd(start, mins, shiftstart);
468                 VectorAdd(end, mins, shiftend);
469                 if (VectorCompare(start, end))
470                         trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
471                 else
472                         trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
473                 VectorSubtract(trace.endpos, mins, trace.endpos);
474                 return trace;
475         }
476
477         VectorCopy(start, clipstart);
478         VectorCopy(end, clipend);
479         VectorCopy(mins, clipmins);
480         VectorCopy(maxs, clipmaxs);
481         VectorCopy(mins, clipmins2);
482         VectorCopy(maxs, clipmaxs2);
483 #if COLLISIONPARANOID >= 3
484         Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
485 #endif
486
487         // clip to world
488         Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
489         cliptrace.bmodelstartsolid = cliptrace.startsolid;
490         if (cliptrace.startsolid || cliptrace.fraction < 1)
491                 cliptrace.ent = prog->edicts;
492         if (type == MOVE_WORLDONLY)
493                 goto finished;
494
495         if (type == MOVE_MISSILE)
496         {
497                 // LordHavoc: modified this, was = -15, now -= 15
498                 for (i = 0;i < 3;i++)
499                 {
500                         clipmins2[i] -= 15;
501                         clipmaxs2[i] += 15;
502                 }
503         }
504
505         // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
506         if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
507                 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
508         else
509         {
510                 VectorCopy(clipmins, hullmins);
511                 VectorCopy(clipmaxs, hullmaxs);
512         }
513
514         // create the bounding box of the entire move
515         for (i = 0;i < 3;i++)
516         {
517                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
518                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
519         }
520
521         // debug override to test against everything
522         if (sv_debugmove.integer)
523         {
524                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
525                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
526         }
527
528         // if the passedict is world, make it NULL (to avoid two checks each time)
529         if (passedict == prog->edicts)
530                 passedict = NULL;
531         // precalculate prog value for passedict for comparisons
532         passedictprog = PRVM_EDICT_TO_PROG(passedict);
533         // figure out whether this is a point trace for comparisons
534         pointtrace = VectorCompare(clipmins, clipmaxs);
535         // precalculate passedict's owner edict pointer for comparisons
536         traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
537
538         // clip to entities
539         // because this uses World_EntitiestoBox, we know all entity boxes overlap
540         // the clip region, so we can skip culling checks in the loop below
541         numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
542         if (numtouchedicts > MAX_EDICTS)
543         {
544                 // this never happens
545                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
546                 numtouchedicts = MAX_EDICTS;
547         }
548         for (i = 0;i < numtouchedicts;i++)
549         {
550                 touch = touchedicts[i];
551
552                 if (touch->fields.server->solid < SOLID_BBOX)
553                         continue;
554                 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
555                         continue;
556
557                 if (passedict)
558                 {
559                         // don't clip against self
560                         if (passedict == touch)
561                                 continue;
562                         // don't clip owned entities against owner
563                         if (traceowner == touch)
564                                 continue;
565                         // don't clip owner against owned entities
566                         if (passedictprog == touch->fields.server->owner)
567                                 continue;
568                         // don't clip points against points (they can't collide)
569                         if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
570                                 continue;
571                 }
572
573                 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
574
575                 // might interact, so do an exact clip
576                 model = NULL;
577                 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
578                 {
579                         model = SV_GetModelFromEdict(touch);
580                         pitchsign = SV_GetPitchSign(touch);
581                 }
582                 if (model)
583                         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);
584                 else
585                         Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
586                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
587                 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
588                 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
589                 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
590                 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
591                         Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
592                 else
593                         Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
594
595                 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
596         }
597
598 finished:
599 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
600         if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
601                 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
602 #endif
603         return cliptrace;
604 }
605
606 #if COLLISIONPARANOID >= 1
607 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)
608 {
609         int endstuck;
610         trace_t trace;
611         vec3_t temp;
612         trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
613         if (passedict)
614         {
615                 VectorCopy(trace.endpos, temp);
616                 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
617 #if COLLISIONPARANOID < 3
618                 if (trace.startsolid || endstuck)
619 #endif
620                         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" : "");
621         }
622         return trace;
623 }
624 #endif
625
626 int SV_PointSuperContents(const vec3_t point)
627 {
628         int supercontents = 0;
629         int i;
630         prvm_edict_t *touch;
631         vec3_t transformed;
632         // matrices to transform into/out of other entity's space
633         matrix4x4_t matrix, imatrix;
634         // model of other entity
635         dp_model_t *model;
636         int frame;
637         // list of entities to test for collisions
638         int numtouchedicts;
639         prvm_edict_t *touchedicts[MAX_EDICTS];
640
641         // get world supercontents at this point
642         if (sv.worldmodel && sv.worldmodel->PointSuperContents)
643                 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
644
645         // if sv_gameplayfix_swiminbmodels is off we're done
646         if (!sv_gameplayfix_swiminbmodels.integer)
647                 return supercontents;
648
649         // get list of entities at this point
650         numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
651         if (numtouchedicts > MAX_EDICTS)
652         {
653                 // this never happens
654                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
655                 numtouchedicts = MAX_EDICTS;
656         }
657         for (i = 0;i < numtouchedicts;i++)
658         {
659                 touch = touchedicts[i];
660
661                 // we only care about SOLID_BSP for pointcontents
662                 if (touch->fields.server->solid != SOLID_BSP)
663                         continue;
664
665                 // might interact, so do an exact clip
666                 model = SV_GetModelFromEdict(touch);
667                 if (!model || !model->PointSuperContents)
668                         continue;
669                 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);
670                 Matrix4x4_Invert_Simple(&imatrix, &matrix);
671                 Matrix4x4_Transform(&imatrix, point, transformed);
672                 frame = (int)touch->fields.server->frame;
673                 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
674         }
675
676         return supercontents;
677 }
678
679 /*
680 ===============================================================================
681
682 Linking entities into the world culling system
683
684 ===============================================================================
685 */
686
687 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
688 {
689         prvm_eval_t *val;
690         prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
691         prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
692         prog->globals.server->time = sv.time;
693         prog->globals.server->trace_allsolid = false;
694         prog->globals.server->trace_startsolid = false;
695         prog->globals.server->trace_fraction = 1;
696         prog->globals.server->trace_inwater = false;
697         prog->globals.server->trace_inopen = true;
698         VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
699         VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
700         prog->globals.server->trace_plane_dist = 0;
701         prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
702         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
703                 val->_float = 0;
704         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
705                 val->_float = 0;
706         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
707                 val->_float = 0;
708         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
709                 val->string = 0;
710         PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
711 }
712
713 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
714 {
715         int i, numtouchedicts, old_self, old_other;
716         prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
717
718         if (ent == prog->edicts)
719                 return;         // don't add the world
720
721         if (ent->priv.server->free)
722                 return;
723
724         if (ent->fields.server->solid == SOLID_NOT)
725                 return;
726
727         // build a list of edicts to touch, because the link loop can be corrupted
728         // by IncreaseEdicts called during touch functions
729         numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
730         if (numtouchedicts > MAX_EDICTS)
731         {
732                 // this never happens
733                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
734                 numtouchedicts = MAX_EDICTS;
735         }
736
737         old_self = prog->globals.server->self;
738         old_other = prog->globals.server->other;
739         for (i = 0;i < numtouchedicts;i++)
740         {
741                 touch = touchedicts[i];
742                 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
743                 {
744                         SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
745                 }
746         }
747         prog->globals.server->self = old_self;
748         prog->globals.server->other = old_other;
749 }
750
751 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
752 {
753         vec3_t v, u;
754         matrix4x4_t m;
755         Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
756
757         v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
758                 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
759         v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
760                 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];
761                 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];
762         v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
763                 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];
764                 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];
765         v[0] = maxs[0]; v[1] = maxs[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] = mins[1]; v[2] = maxs[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] = mins[1]; v[2] = maxs[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] = maxs[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] = maxs[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 }
781
782 /*
783 ===============
784 SV_LinkEdict
785
786 ===============
787 */
788 void SV_LinkEdict (prvm_edict_t *ent)
789 {
790         dp_model_t *model;
791         vec3_t mins, maxs;
792         int modelindex;
793
794         if (ent == prog->edicts)
795                 return;         // don't add the world
796
797         if (ent->priv.server->free)
798                 return;
799
800         modelindex = (int)ent->fields.server->modelindex;
801         if (modelindex < 0 || modelindex >= MAX_MODELS)
802         {
803                 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
804                 modelindex = 0;
805         }
806         model = SV_GetModelByIndex(modelindex);
807
808         VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
809         VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
810         VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
811
812 // set the abs box
813
814         if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
815         {
816                 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
817                 // TODO special handling for spheres?
818                 RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs);
819                 VectorAdd(ent->fields.server->origin, mins, mins);
820                 VectorAdd(ent->fields.server->origin, maxs, maxs);
821         }
822         else if (ent->fields.server->solid == SOLID_BSP)
823         {
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                 vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
1504                 VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
1505                 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1506                 bump++;
1507                 if (bump > 10)
1508                 {
1509                         VectorCopy(original, ent->fields.server->origin);
1510                         break;
1511                 }
1512         }
1513         if (trace->bmodelstartsolid && failonbmodelstartsolid)
1514                 return true;
1515
1516
1517         VectorCopy (trace->endpos, ent->fields.server->origin);
1518         SV_LinkEdict(ent);
1519
1520 #if 0
1521         if(!trace->startsolid)
1522         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)
1523         {
1524                 Con_Printf("something eeeeevil happened\n");
1525         }
1526 #endif
1527
1528         if (dolink)
1529                 SV_LinkEdict_TouchAreaGrid(ent);
1530
1531         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))))
1532                 return SV_Impact (ent, trace);
1533
1534         return true;
1535 }
1536
1537
1538 /*
1539 ============
1540 SV_PushMove
1541
1542 ============
1543 */
1544 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1545 {
1546         int i, e, index;
1547         int pusherowner, pusherprog;
1548         int checkcontents;
1549         qboolean rotated;
1550         float savesolid, movetime2, pushltime;
1551         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1552         int num_moved;
1553         int numcheckentities;
1554         static prvm_edict_t *checkentities[MAX_EDICTS];
1555         dp_model_t *pushermodel;
1556         trace_t trace, trace2;
1557         matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1558         unsigned short moved_edicts[MAX_EDICTS];
1559
1560         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])
1561         {
1562                 pusher->fields.server->ltime += movetime;
1563                 return;
1564         }
1565
1566         switch ((int) pusher->fields.server->solid)
1567         {
1568         // LordHavoc: valid pusher types
1569         case SOLID_BSP:
1570         case SOLID_BBOX:
1571         case SOLID_SLIDEBOX:
1572         case SOLID_CORPSE: // LordHavoc: this would be weird...
1573                 break;
1574         // LordHavoc: no collisions
1575         case SOLID_NOT:
1576         case SOLID_TRIGGER:
1577                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1578                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1579                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1580                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1581                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1582                 pusher->fields.server->ltime += movetime;
1583                 SV_LinkEdict(pusher);
1584                 return;
1585         default:
1586                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1587                 return;
1588         }
1589         index = (int) pusher->fields.server->modelindex;
1590         if (index < 1 || index >= MAX_MODELS)
1591         {
1592                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1593                 return;
1594         }
1595         pushermodel = SV_GetModelByIndex(index);
1596         pusherowner = pusher->fields.server->owner;
1597         pusherprog = PRVM_EDICT_TO_PROG(pusher);
1598
1599         rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1600
1601         movetime2 = movetime;
1602         VectorScale(pusher->fields.server->velocity, movetime2, move1);
1603         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1604         if (moveangle[0] || moveangle[2])
1605         {
1606                 for (i = 0;i < 3;i++)
1607                 {
1608                         if (move1[i] > 0)
1609                         {
1610                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1611                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1612                         }
1613                         else
1614                         {
1615                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1616                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1617                         }
1618                 }
1619         }
1620         else if (moveangle[1])
1621         {
1622                 for (i = 0;i < 3;i++)
1623                 {
1624                         if (move1[i] > 0)
1625                         {
1626                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1627                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1628                         }
1629                         else
1630                         {
1631                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1632                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1633                         }
1634                 }
1635         }
1636         else
1637         {
1638                 for (i = 0;i < 3;i++)
1639                 {
1640                         if (move1[i] > 0)
1641                         {
1642                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1643                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1644                         }
1645                         else
1646                         {
1647                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1648                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1649                         }
1650                 }
1651         }
1652
1653         VectorNegate (moveangle, a);
1654         AngleVectorsFLU (a, forward, left, up);
1655
1656         VectorCopy (pusher->fields.server->origin, pushorig);
1657         VectorCopy (pusher->fields.server->angles, pushang);
1658         pushltime = pusher->fields.server->ltime;
1659
1660 // move the pusher to its final position
1661
1662         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1663         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1664         pusher->fields.server->ltime += movetime;
1665         SV_LinkEdict(pusher);
1666
1667         pushermodel = SV_GetModelFromEdict(pusher);
1668         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);
1669         Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1670
1671         savesolid = pusher->fields.server->solid;
1672
1673 // see if any solid entities are inside the final position
1674         num_moved = 0;
1675
1676         numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1677         for (e = 0;e < numcheckentities;e++)
1678         {
1679                 prvm_edict_t *check = checkentities[e];
1680                 int movetype = (int)check->fields.server->movetype;
1681                 switch(movetype)
1682                 {
1683                 case MOVETYPE_NONE:
1684                 case MOVETYPE_PUSH:
1685                 case MOVETYPE_FOLLOW:
1686                 case MOVETYPE_NOCLIP:
1687                 case MOVETYPE_FAKEPUSH:
1688                         continue;
1689                 default:
1690                         break;
1691                 }
1692
1693                 if (check->fields.server->owner == pusherprog)
1694                         continue;
1695
1696                 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1697                         continue;
1698
1699                 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1700
1701                 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1702                 check->priv.server->waterposition_forceupdate = true;
1703
1704                 checkcontents = SV_GenericHitSuperContentsMask(check);
1705
1706                 // if the entity is standing on the pusher, it will definitely be moved
1707                 // if the entity is not standing on the pusher, but is in the pusher's
1708                 // final position, move it
1709                 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1710                 {
1711                         Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, 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);
1712                         //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1713                         if (!trace.startsolid)
1714                         {
1715                                 //Con_Printf("- not in solid\n");
1716                                 continue;
1717                         }
1718                 }
1719
1720                 if (rotated)
1721                 {
1722                         vec3_t org2;
1723                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1724                         org2[0] = DotProduct (org, forward);
1725                         org2[1] = DotProduct (org, left);
1726                         org2[2] = DotProduct (org, up);
1727                         VectorSubtract (org2, org, move);
1728                         VectorAdd (move, move1, move);
1729                 }
1730                 else
1731                         VectorCopy (move1, move);
1732
1733                 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1734
1735                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1736                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1737                 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1738
1739                 // physics objects need better collisions than this code can do
1740                 if (movetype == MOVETYPE_PHYSICS)
1741                 {
1742                         VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1743                         SV_LinkEdict(check);
1744                         SV_LinkEdict_TouchAreaGrid(check);
1745                         continue;
1746                 }
1747
1748                 // try moving the contacted entity
1749                 pusher->fields.server->solid = SOLID_NOT;
1750                 if(!SV_PushEntity (&trace, check, move, true, true))
1751                 {
1752                         // entity "check" got teleported
1753                         check->fields.server->angles[1] += trace.fraction * moveangle[1];
1754                         pusher->fields.server->solid = savesolid; // was SOLID_BSP
1755                         continue; // pushed enough
1756                 }
1757                 // FIXME: turn players specially
1758                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1759                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1760                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1761
1762                 // this trace.fraction < 1 check causes items to fall off of pushers
1763                 // if they pass under or through a wall
1764                 // the groundentity check causes items to fall off of ledges
1765                 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1766                         check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1767
1768                 // if it is still inside the pusher, block
1769                 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, 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);
1770                 if (trace.startsolid)
1771                 {
1772                         // try moving the contacted entity a tiny bit further to account for precision errors
1773                         vec3_t move2;
1774                         pusher->fields.server->solid = SOLID_NOT;
1775                         VectorScale(move, 1.1, move2);
1776                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1777                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1778                         if(!SV_PushEntity (&trace2, check, move2, true, true))
1779                         {
1780                                 // entity "check" got teleported
1781                                 continue;
1782                         }
1783                         pusher->fields.server->solid = savesolid;
1784                         Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, 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);
1785                         if (trace.startsolid)
1786                         {
1787                                 // try moving the contacted entity a tiny bit less to account for precision errors
1788                                 pusher->fields.server->solid = SOLID_NOT;
1789                                 VectorScale(move, 0.9, move2);
1790                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1791                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1792                                 if(!SV_PushEntity (&trace2, check, move2, true, true))
1793                                 {
1794                                         // entity "check" got teleported
1795                                         continue;
1796                                 }
1797                                 pusher->fields.server->solid = savesolid;
1798                                 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, 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);
1799                                 if (trace.startsolid)
1800                                 {
1801                                         // still inside pusher, so it's really blocked
1802
1803                                         // fail the move
1804                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1805                                                 continue;
1806                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1807                                         {
1808                                                 // corpse
1809                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1810                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1811                                                 continue;
1812                                         }
1813
1814                                         VectorCopy (pushorig, pusher->fields.server->origin);
1815                                         VectorCopy (pushang, pusher->fields.server->angles);
1816                                         pusher->fields.server->ltime = pushltime;
1817                                         SV_LinkEdict(pusher);
1818
1819                                         // move back any entities we already moved
1820                                         for (i = 0;i < num_moved;i++)
1821                                         {
1822                                                 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1823                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1824                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1825                                                 SV_LinkEdict(ed);
1826                                         }
1827
1828                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1829                                         if (pusher->fields.server->blocked)
1830                                         {
1831                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1832                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1833                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1834                                         }
1835                                         break;
1836                                 }
1837                         }
1838                 }
1839         }
1840         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1841         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1842         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1843 }
1844
1845 /*
1846 ================
1847 SV_Physics_Pusher
1848
1849 ================
1850 */
1851 void SV_Physics_Pusher (prvm_edict_t *ent)
1852 {
1853         float thinktime, oldltime, movetime;
1854
1855         oldltime = ent->fields.server->ltime;
1856
1857         thinktime = ent->fields.server->nextthink;
1858         if (thinktime < ent->fields.server->ltime + sv.frametime)
1859         {
1860                 movetime = thinktime - ent->fields.server->ltime;
1861                 if (movetime < 0)
1862                         movetime = 0;
1863         }
1864         else
1865                 movetime = sv.frametime;
1866
1867         if (movetime)
1868                 // advances ent->fields.server->ltime if not blocked
1869                 SV_PushMove (ent, movetime);
1870
1871         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1872         {
1873                 ent->fields.server->nextthink = 0;
1874                 prog->globals.server->time = sv.time;
1875                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1876                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1877                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1878         }
1879 }
1880
1881
1882 /*
1883 ===============================================================================
1884
1885 CLIENT MOVEMENT
1886
1887 ===============================================================================
1888 */
1889
1890 static float unstickoffsets[] =
1891 {
1892         // poutting -/+z changes first as they are least weird
1893          0,  0,  -1,
1894          0,  0,  1,
1895          // x or y changes
1896         -1,  0,  0,
1897          1,  0,  0,
1898          0, -1,  0,
1899          0,  1,  0,
1900          // x and y changes
1901         -1, -1,  0,
1902          1, -1,  0,
1903         -1,  1,  0,
1904          1,  1,  0,
1905 };
1906
1907 typedef enum unstickresult_e
1908 {
1909         UNSTICK_STUCK = 0,
1910         UNSTICK_GOOD = 1,
1911         UNSTICK_UNSTUCK = 2
1912 }
1913 unstickresult_t;
1914
1915 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1916 {
1917         int i, maxunstick;
1918
1919         // if not stuck in a bmodel, just return
1920         if (!SV_TestEntityPosition(ent, vec3_origin))
1921                 return UNSTICK_GOOD;
1922
1923         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1924         {
1925                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1926                 {
1927                         VectorCopy(unstickoffsets + i, offset);
1928                         SV_LinkEdict(ent);
1929                         //SV_LinkEdict_TouchAreaGrid(ent);
1930                         return UNSTICK_UNSTUCK;
1931                 }
1932         }
1933
1934         maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1935         // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1936
1937         for(i = 2; i <= maxunstick; ++i)
1938         {
1939                 VectorClear(offset);
1940                 offset[2] = -i;
1941                 if (!SV_TestEntityPosition(ent, offset))
1942                 {
1943                         SV_LinkEdict(ent);
1944                         //SV_LinkEdict_TouchAreaGrid(ent);
1945                         return UNSTICK_UNSTUCK;
1946                 }
1947                 offset[2] = i;
1948                 if (!SV_TestEntityPosition(ent, offset))
1949                 {
1950                         SV_LinkEdict(ent);
1951                         //SV_LinkEdict_TouchAreaGrid(ent);
1952                         return UNSTICK_UNSTUCK;
1953                 }
1954         }
1955
1956         return UNSTICK_STUCK;
1957 }
1958
1959 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1960 {
1961         vec3_t offset;
1962         switch(SV_UnstickEntityReturnOffset(ent, offset))
1963         {
1964                 case UNSTICK_GOOD:
1965                         return true;
1966                 case UNSTICK_UNSTUCK:
1967                         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]);
1968                         return true;
1969                 case UNSTICK_STUCK:
1970                         if (developer.integer >= 100)
1971                                 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1972                         return false;
1973                 default:
1974                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1975                         return false;
1976         }
1977 }
1978
1979 /*
1980 =============
1981 SV_CheckStuck
1982
1983 This is a big hack to try and fix the rare case of getting stuck in the world
1984 clipping hull.
1985 =============
1986 */
1987 void SV_CheckStuck (prvm_edict_t *ent)
1988 {
1989         vec3_t offset;
1990
1991         switch(SV_UnstickEntityReturnOffset(ent, offset))
1992         {
1993                 case UNSTICK_GOOD:
1994                         VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1995                         break;
1996                 case UNSTICK_UNSTUCK:
1997                         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]);
1998                         break;
1999                 case UNSTICK_STUCK:
2000                         VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
2001                         if (!SV_TestEntityPosition(ent, offset))
2002                         {
2003                                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2004                                 SV_LinkEdict(ent);
2005                                 //SV_LinkEdict_TouchAreaGrid(ent);
2006                         }
2007                         else
2008                                 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2009                         break;
2010                 default:
2011                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2012         }
2013 }
2014
2015
2016 /*
2017 =============
2018 SV_CheckWater
2019 =============
2020 */
2021 qboolean SV_CheckWater (prvm_edict_t *ent)
2022 {
2023         int cont;
2024         int nNativeContents;
2025         vec3_t point;
2026
2027         point[0] = ent->fields.server->origin[0];
2028         point[1] = ent->fields.server->origin[1];
2029         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2030
2031         // DRESK - Support for Entity Contents Transition Event
2032         // NOTE: Some logic needed to be slightly re-ordered
2033         // to not affect performance and allow for the feature.
2034
2035         // Acquire Super Contents Prior to Resets
2036         cont = SV_PointSuperContents(point);
2037         // Acquire Native Contents Here
2038         nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2039
2040         // DRESK - Support for Entity Contents Transition Event
2041         if(ent->fields.server->watertype)
2042                 // Entity did NOT Spawn; Check
2043                 SV_CheckContentsTransition(ent, nNativeContents);
2044
2045
2046         ent->fields.server->waterlevel = 0;
2047         ent->fields.server->watertype = CONTENTS_EMPTY;
2048         cont = SV_PointSuperContents(point);
2049         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2050         {
2051                 ent->fields.server->watertype = nNativeContents;
2052                 ent->fields.server->waterlevel = 1;
2053                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2054                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2055                 {
2056                         ent->fields.server->waterlevel = 2;
2057                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2058                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2059                                 ent->fields.server->waterlevel = 3;
2060                 }
2061         }
2062
2063         return ent->fields.server->waterlevel > 1;
2064 }
2065
2066 /*
2067 ============
2068 SV_WallFriction
2069
2070 ============
2071 */
2072 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2073 {
2074         float d, i;
2075         vec3_t forward, into, side;
2076
2077         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2078         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2079         {
2080                 // cut the tangential velocity
2081                 i = DotProduct (stepnormal, ent->fields.server->velocity);
2082                 VectorScale (stepnormal, i, into);
2083                 VectorSubtract (ent->fields.server->velocity, into, side);
2084                 ent->fields.server->velocity[0] = side[0] * (1 + d);
2085                 ent->fields.server->velocity[1] = side[1] * (1 + d);
2086         }
2087 }
2088
2089 #if 0
2090 /*
2091 =====================
2092 SV_TryUnstick
2093
2094 Player has come to a dead stop, possibly due to the problem with limited
2095 float precision at some angle joins in the BSP hull.
2096
2097 Try fixing by pushing one pixel in each direction.
2098
2099 This is a hack, but in the interest of good gameplay...
2100 ======================
2101 */
2102 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2103 {
2104         int i, clip;
2105         vec3_t oldorg, dir;
2106
2107         VectorCopy (ent->fields.server->origin, oldorg);
2108         VectorClear (dir);
2109
2110         for (i=0 ; i<8 ; i++)
2111         {
2112                 // try pushing a little in an axial direction
2113                 switch (i)
2114                 {
2115                         case 0: dir[0] = 2; dir[1] = 0; break;
2116                         case 1: dir[0] = 0; dir[1] = 2; break;
2117                         case 2: dir[0] = -2; dir[1] = 0; break;
2118                         case 3: dir[0] = 0; dir[1] = -2; break;
2119                         case 4: dir[0] = 2; dir[1] = 2; break;
2120                         case 5: dir[0] = -2; dir[1] = 2; break;
2121                         case 6: dir[0] = 2; dir[1] = -2; break;
2122                         case 7: dir[0] = -2; dir[1] = -2; break;
2123                 }
2124
2125                 SV_PushEntity (&trace, ent, dir, false, true);
2126
2127                 // retry the original move
2128                 ent->fields.server->velocity[0] = oldvel[0];
2129                 ent->fields.server->velocity[1] = oldvel[1];
2130                 ent->fields.server->velocity[2] = 0;
2131                 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2132
2133                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2134                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2135                 {
2136                         Con_DPrint("TryUnstick - success.\n");
2137                         return clip;
2138                 }
2139
2140                 // go back to the original pos and try again
2141                 VectorCopy (oldorg, ent->fields.server->origin);
2142         }
2143
2144         // still not moving
2145         VectorClear (ent->fields.server->velocity);
2146         Con_DPrint("TryUnstick - failure.\n");
2147         return 7;
2148 }
2149 #endif
2150
2151 /*
2152 =====================
2153 SV_WalkMove
2154
2155 Only used by players
2156 ======================
2157 */
2158 void SV_WalkMove (prvm_edict_t *ent)
2159 {
2160         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask, type;
2161         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2162         trace_t downtrace, trace;
2163         qboolean applygravity;
2164
2165         // if frametime is 0 (due to client sending the same timestamp twice),
2166         // don't move
2167         if (sv.frametime <= 0)
2168                 return;
2169
2170         SV_CheckStuck (ent);
2171
2172         applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2173
2174         hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2175
2176         SV_CheckVelocity(ent);
2177
2178         // do a regular slide move unless it looks like you ran into a step
2179         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2180
2181         VectorCopy (ent->fields.server->origin, start_origin);
2182         VectorCopy (ent->fields.server->velocity, start_velocity);
2183
2184         clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2185
2186         if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2187         if(!(clip & 1))
2188         {
2189                 // only try this if there was no floor in the way in the trace (no,
2190                 // this check seems to be not REALLY necessary, because if clip & 1,
2191                 // our trace will hit that thing too)
2192                 VectorSet(upmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + 1);
2193                 VectorSet(downmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
2194                 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
2195                         type = MOVE_MISSILE;
2196                 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
2197                         type = MOVE_NOMONSTERS; // only clip against bmodels
2198                 else
2199                         type = MOVE_NORMAL;
2200                 trace = SV_TraceBox(upmove, ent->fields.server->mins, ent->fields.server->maxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2201                 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2202                         clip |= 1; // but we HAVE found a floor
2203         }
2204
2205         // if the move did not hit the ground at any point, we're not on ground
2206         if(!(clip & 1))
2207                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2208
2209         SV_CheckVelocity(ent);
2210         SV_LinkEdict(ent);
2211         SV_LinkEdict_TouchAreaGrid(ent);
2212
2213         if(clip & 8) // teleport
2214                 return;
2215
2216         if ((int)ent->fields.server->flags & FL_WATERJUMP)
2217                 return;
2218
2219         if (sv_nostep.integer)
2220                 return;
2221
2222         VectorCopy(ent->fields.server->origin, originalmove_origin);
2223         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2224         originalmove_clip = clip;
2225         originalmove_flags = (int)ent->fields.server->flags;
2226         originalmove_groundentity = ent->fields.server->groundentity;
2227
2228         // if move didn't block on a step, return
2229         if (clip & 2)
2230         {
2231                 // if move was not trying to move into the step, return
2232                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2233                         return;
2234
2235                 if (ent->fields.server->movetype != MOVETYPE_FLY)
2236                 {
2237                         // return if gibbed by a trigger
2238                         if (ent->fields.server->movetype != MOVETYPE_WALK)
2239                                 return;
2240
2241                         // only step up while jumping if that is enabled
2242                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2243                                 if (!oldonground && ent->fields.server->waterlevel == 0)
2244                                         return;
2245                 }
2246
2247                 // try moving up and forward to go up a step
2248                 // back to start pos
2249                 VectorCopy (start_origin, ent->fields.server->origin);
2250                 VectorCopy (start_velocity, ent->fields.server->velocity);
2251
2252                 // move up
2253                 VectorClear (upmove);
2254                 upmove[2] = sv_stepheight.value;
2255                 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2256                 {
2257                         // we got teleported when upstepping... must abort the move
2258                         return;
2259                 }
2260
2261                 // move forward
2262                 ent->fields.server->velocity[2] = 0;
2263                 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2264                 ent->fields.server->velocity[2] += start_velocity[2];
2265                 if(clip & 8)
2266                 {
2267                         // we got teleported when upstepping... must abort the move
2268                         // note that z velocity handling may not be what QC expects here, but we cannot help it
2269                         return;
2270                 }
2271
2272                 SV_CheckVelocity(ent);
2273                 SV_LinkEdict(ent);
2274                 SV_LinkEdict_TouchAreaGrid(ent);
2275
2276                 // check for stuckness, possibly due to the limited precision of floats
2277                 // in the clipping hulls
2278                 if (clip
2279                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2280                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2281                 {
2282                         //Con_Printf("wall\n");
2283                         // stepping up didn't make any progress, revert to original move
2284                         VectorCopy(originalmove_origin, ent->fields.server->origin);
2285                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2286                         //clip = originalmove_clip;
2287                         ent->fields.server->flags = originalmove_flags;
2288                         ent->fields.server->groundentity = originalmove_groundentity;
2289                         // now try to unstick if needed
2290                         //clip = SV_TryUnstick (ent, oldvel);
2291                         return;
2292                 }
2293
2294                 //Con_Printf("step - ");
2295
2296                 // extra friction based on view angle
2297                 if (clip & 2 && sv_wallfriction.integer)
2298                         SV_WallFriction (ent, stepnormal);
2299         }
2300         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2301         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))
2302                 return;
2303
2304         // move down
2305         VectorClear (downmove);
2306         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2307         if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2308         {
2309                 // we got teleported when downstepping... must abort the move
2310                 return;
2311         }
2312
2313         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2314         {
2315                 // this has been disabled so that you can't jump when you are stepping
2316                 // up while already jumping (also known as the Quake2 double jump bug)
2317 #if 0
2318                 // LordHavoc: disabled this check so you can walk on monsters/players
2319                 //if (ent->fields.server->solid == SOLID_BSP)
2320                 {
2321                         //Con_Printf("onground\n");
2322                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
2323                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2324                 }
2325 #endif
2326         }
2327         else
2328         {
2329                 //Con_Printf("slope\n");
2330                 // if the push down didn't end up on good ground, use the move without
2331                 // the step up.  This happens near wall / slope combinations, and can
2332                 // cause the player to hop up higher on a slope too steep to climb
2333                 VectorCopy(originalmove_origin, ent->fields.server->origin);
2334                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2335                 //clip = originalmove_clip;
2336                 ent->fields.server->flags = originalmove_flags;
2337                 ent->fields.server->groundentity = originalmove_groundentity;
2338         }
2339
2340         SV_CheckVelocity(ent);
2341         SV_LinkEdict(ent);
2342         SV_LinkEdict_TouchAreaGrid(ent);
2343 }
2344
2345 //============================================================================
2346
2347 /*
2348 =============
2349 SV_Physics_Follow
2350
2351 Entities that are "stuck" to another entity
2352 =============
2353 */
2354 void SV_Physics_Follow (prvm_edict_t *ent)
2355 {
2356         vec3_t vf, vr, vu, angles, v;
2357         prvm_edict_t *e;
2358
2359         // regular thinking
2360         if (!SV_RunThink (ent))
2361                 return;
2362
2363         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2364         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2365         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])
2366         {
2367                 // quick case for no rotation
2368                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2369         }
2370         else
2371         {
2372                 angles[0] = -ent->fields.server->punchangle[0];
2373                 angles[1] =  ent->fields.server->punchangle[1];
2374                 angles[2] =  ent->fields.server->punchangle[2];
2375                 AngleVectors (angles, vf, vr, vu);
2376                 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];
2377                 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];
2378                 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];
2379                 angles[0] = -e->fields.server->angles[0];
2380                 angles[1] =  e->fields.server->angles[1];
2381                 angles[2] =  e->fields.server->angles[2];
2382                 AngleVectors (angles, vf, vr, vu);
2383                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2384                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2385                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2386         }
2387         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2388         SV_LinkEdict(ent);
2389         //SV_LinkEdict_TouchAreaGrid(ent);
2390 }
2391
2392 /*
2393 ==============================================================================
2394
2395 TOSS / BOUNCE
2396
2397 ==============================================================================
2398 */
2399
2400 /*
2401 =============
2402 SV_CheckWaterTransition
2403
2404 =============
2405 */
2406 void SV_CheckWaterTransition (prvm_edict_t *ent)
2407 {
2408         int cont;
2409         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2410         if (!ent->fields.server->watertype)
2411         {
2412                 // just spawned here
2413                 ent->fields.server->watertype = cont;
2414                 ent->fields.server->waterlevel = 1;
2415                 return;
2416         }
2417
2418         // DRESK - Support for Entity Contents Transition Event
2419         // NOTE: Call here BEFORE updating the watertype below,
2420         // and suppress watersplash sound if a valid function
2421         // call was made to allow for custom "splash" sounds.
2422         if( !SV_CheckContentsTransition(ent, cont) )
2423         { // Contents Transition Function Invalid; Potentially Play Water Sound
2424                 // check if the entity crossed into or out of water
2425                 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2426                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2427         }
2428
2429         if (cont <= CONTENTS_WATER)
2430         {
2431                 ent->fields.server->watertype = cont;
2432                 ent->fields.server->waterlevel = 1;
2433         }
2434         else
2435         {
2436                 ent->fields.server->watertype = CONTENTS_EMPTY;
2437                 ent->fields.server->waterlevel = 0;
2438         }
2439 }
2440
2441 /*
2442 =============
2443 SV_Physics_Toss
2444
2445 Toss, bounce, and fly movement.  When onground, do nothing.
2446 =============
2447 */
2448 void SV_Physics_Toss (prvm_edict_t *ent)
2449 {
2450         trace_t trace;
2451         vec3_t move;
2452         vec_t movetime;
2453         int bump;
2454         prvm_edict_t *groundentity;
2455
2456 // if onground, return without moving
2457         if ((int)ent->fields.server->flags & FL_ONGROUND)
2458         {
2459                 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2460                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2461                 {
2462                         // don't stick to ground if onground and moving upward
2463                         ent->fields.server->flags -= FL_ONGROUND;
2464                 }
2465                 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2466                 {
2467                         // we can trust FL_ONGROUND if groundentity is world because it never moves
2468                         return;
2469                 }
2470                 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2471                 {
2472                         // if ent was supported by a brush model on previous frame,
2473                         // and groundentity is now freed, set groundentity to 0 (world)
2474                         // which leaves it suspended in the air
2475                         ent->fields.server->groundentity = 0;
2476                         if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2477                                 return;
2478                 }
2479                 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2480                 {
2481                         // don't slide if still touching the groundentity
2482                         return;
2483                 }
2484         }
2485         ent->priv.server->suspendedinairflag = false;
2486
2487         SV_CheckVelocity (ent);
2488
2489 // add gravity
2490         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2491                 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2492
2493 // move angles
2494         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2495
2496         movetime = sv.frametime;
2497         for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2498         {
2499         // move origin
2500                 VectorScale (ent->fields.server->velocity, movetime, move);
2501                 if(!SV_PushEntity (&trace, ent, move, true, true))
2502                         return; // teleported
2503                 if (ent->priv.server->free)
2504                         return;
2505                 if (trace.bmodelstartsolid)
2506                 {
2507                         // try to unstick the entity
2508                         SV_UnstickEntity(ent);
2509                         if(!SV_PushEntity (&trace, ent, move, false, true))
2510                                 return; // teleported
2511                         if (ent->priv.server->free)
2512                                 return;
2513                 }
2514                 if (trace.fraction == 1)
2515                         break;
2516                 movetime *= 1 - min(1, trace.fraction);
2517                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2518                 {
2519                         prvm_eval_t *val;
2520                         float bouncefactor = 1.0f;
2521                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2522                         if (val!=0 && val->_float)
2523                                 bouncefactor = val->_float;
2524
2525                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2526                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2527                 }
2528                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2529                 {
2530                         float d, ent_gravity;
2531                         prvm_eval_t *val;
2532                         float bouncefactor = 0.5f;
2533                         float bouncestop = 60.0f / 800.0f;
2534
2535                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2536                         if (val!=0 && val->_float)
2537                                 bouncefactor = val->_float;
2538
2539                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2540                         if (val!=0 && val->_float)
2541                                 bouncestop = val->_float;
2542
2543                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2544                         // LordHavoc: fixed grenades not bouncing when fired down a slope
2545                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2546                         if (val!=0 && val->_float)
2547                                 ent_gravity = val->_float;
2548                         else
2549                                 ent_gravity = 1.0;
2550                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
2551                         {
2552                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2553                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2554                                 {
2555                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2556                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2557                                         VectorClear (ent->fields.server->velocity);
2558                                         VectorClear (ent->fields.server->avelocity);
2559                                 }
2560                                 else
2561                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2562                         }
2563                         else
2564                         {
2565                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2566                                 {
2567                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2568                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2569                                         VectorClear (ent->fields.server->velocity);
2570                                         VectorClear (ent->fields.server->avelocity);
2571                                 }
2572                                 else
2573                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2574                         }
2575                 }
2576                 else
2577                 {
2578                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2579                         if (trace.plane.normal[2] > 0.7)
2580                         {
2581                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2582                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2583                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2584                                         ent->priv.server->suspendedinairflag = true;
2585                                 VectorClear (ent->fields.server->velocity);
2586                                 VectorClear (ent->fields.server->avelocity);
2587                         }
2588                         else
2589                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2590                 }
2591                 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2592                         break;
2593         }
2594
2595 // check for in water
2596         SV_CheckWaterTransition (ent);
2597 }
2598
2599 /*
2600 ===============================================================================
2601
2602 STEPPING MOVEMENT
2603
2604 ===============================================================================
2605 */
2606
2607 /*
2608 =============
2609 SV_Physics_Step
2610
2611 Monsters freefall when they don't have a ground entity, otherwise
2612 all movement is done with discrete steps.
2613
2614 This is also used for objects that have become still on the ground, but
2615 will fall if the floor is pulled out from under them.
2616 =============
2617 */
2618 void SV_Physics_Step (prvm_edict_t *ent)
2619 {
2620         int flags = (int)ent->fields.server->flags;
2621
2622         // DRESK
2623         // Backup Velocity in the event that movetypesteplandevent is called,
2624         // to provide a parameter with the entity's velocity at impact.
2625         prvm_eval_t *movetypesteplandevent;
2626         vec3_t backupVelocity;
2627         VectorCopy(ent->fields.server->velocity, backupVelocity);
2628         // don't fall at all if fly/swim
2629         if (!(flags & (FL_FLY | FL_SWIM)))
2630         {
2631                 if (flags & FL_ONGROUND)
2632                 {
2633                         // freefall if onground and moving upward
2634                         // freefall if not standing on a world surface (it may be a lift or trap door)
2635                         if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2636                         {
2637                                 ent->fields.server->flags -= FL_ONGROUND;
2638                                 SV_CheckVelocity(ent);
2639                                 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2640                                 SV_LinkEdict(ent);
2641                                 SV_LinkEdict_TouchAreaGrid(ent);
2642                                 ent->priv.server->waterposition_forceupdate = true;
2643                         }
2644                 }
2645                 else
2646                 {
2647                         // freefall if not onground
2648                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2649
2650                         SV_CheckVelocity(ent);
2651                         SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2652                         SV_LinkEdict(ent);
2653                         SV_LinkEdict_TouchAreaGrid(ent);
2654
2655                         // just hit ground
2656                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2657                         {
2658                                 // DRESK - Check for Entity Land Event Function
2659                                 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2660
2661                                 if(movetypesteplandevent->function)
2662                                 { // Valid Function; Execute
2663                                         // Prepare Parameters
2664                                                 // Assign Velocity at Impact
2665                                                 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2666                                                 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2667                                                 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2668                                                 // Assign Self
2669                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2670                                         // Execute VM Function
2671                                         PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2672                                 }
2673                                 else
2674                                 // Check for Engine Landing Sound
2675                                 if(sv_sound_land.string)
2676                                         SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2677                         }
2678                         ent->priv.server->waterposition_forceupdate = true;
2679                 }
2680         }
2681
2682 // regular thinking
2683         if (!SV_RunThink(ent))
2684                 return;
2685
2686         if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2687         {
2688                 ent->priv.server->waterposition_forceupdate = false;
2689                 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2690                 SV_CheckWaterTransition(ent);
2691         }
2692 }
2693
2694 //============================================================================
2695
2696 static void SV_Physics_Entity (prvm_edict_t *ent)
2697 {
2698         // don't run think/move on newly spawned projectiles as it messes up
2699         // movement interpolation and rocket trails, and is inconsistent with
2700         // respect to entities spawned in the same frame
2701         // (if an ent spawns a higher numbered ent, it moves in the same frame,
2702         //  but if it spawns a lower numbered ent, it doesn't - this never moves
2703         //  ents in the first frame regardless)
2704         qboolean runmove = ent->priv.server->move;
2705         ent->priv.server->move = true;
2706         if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2707                 return;
2708         switch ((int) ent->fields.server->movetype)
2709         {
2710         case MOVETYPE_PUSH:
2711         case MOVETYPE_FAKEPUSH:
2712                 SV_Physics_Pusher (ent);
2713                 break;
2714         case MOVETYPE_NONE:
2715                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2716                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2717                         SV_RunThink (ent);
2718                 break;
2719         case MOVETYPE_FOLLOW:
2720                 SV_Physics_Follow (ent);
2721                 break;
2722         case MOVETYPE_NOCLIP:
2723                 if (SV_RunThink(ent))
2724                 {
2725                         SV_CheckWater(ent);
2726                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2727                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2728                 }
2729                 SV_LinkEdict(ent);
2730                 break;
2731         case MOVETYPE_STEP:
2732                 SV_Physics_Step (ent);
2733                 break;
2734         case MOVETYPE_WALK:
2735                 if (SV_RunThink (ent))
2736                         SV_WalkMove (ent);
2737                 break;
2738         case MOVETYPE_TOSS:
2739         case MOVETYPE_BOUNCE:
2740         case MOVETYPE_BOUNCEMISSILE:
2741         case MOVETYPE_FLYMISSILE:
2742         case MOVETYPE_FLY:
2743                 // regular thinking
2744                 if (SV_RunThink (ent))
2745                         SV_Physics_Toss (ent);
2746                 break;
2747         case MOVETYPE_PHYSICS:
2748                 if (SV_RunThink(ent))
2749                 {
2750                         SV_LinkEdict(ent);
2751                         SV_LinkEdict_TouchAreaGrid(ent);
2752                 }
2753                 break;
2754         default:
2755                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2756                 break;
2757         }
2758 }
2759
2760 void SV_Physics_ClientMove(void)
2761 {
2762         prvm_edict_t *ent;
2763         ent = host_client->edict;
2764
2765         // call player physics, this needs the proper frametime
2766         prog->globals.server->frametime = sv.frametime;
2767         SV_ClientThink();
2768
2769         // call standard client pre-think, with frametime = 0
2770         prog->globals.server->time = sv.time;
2771         prog->globals.server->frametime = 0;
2772         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2773         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2774         prog->globals.server->frametime = sv.frametime;
2775
2776         // make sure the velocity is sane (not a NaN)
2777         SV_CheckVelocity(ent);
2778
2779         // perform MOVETYPE_WALK behavior
2780         SV_WalkMove (ent);
2781
2782         // call standard player post-think, with frametime = 0
2783         prog->globals.server->time = sv.time;
2784         prog->globals.server->frametime = 0;
2785         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2786         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2787         prog->globals.server->frametime = sv.frametime;
2788
2789         if(ent->fields.server->fixangle)
2790         {
2791                 // angle fixing was requested by physics code...
2792                 // so store the current angles for later use
2793                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2794                 host_client->fixangle_angles_set = TRUE;
2795
2796                 // and clear fixangle for the next frame
2797                 ent->fields.server->fixangle = 0;
2798         }
2799 }
2800
2801 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2802 {
2803         // don't do physics on disconnected clients, FrikBot relies on this
2804         if (!host_client->spawned)
2805                 return;
2806
2807         // make sure the velocity is sane (not a NaN)
2808         SV_CheckVelocity(ent);
2809
2810         // don't run physics here if running asynchronously
2811         if (host_client->clmovement_inputtimeout <= 0)
2812         {
2813                 SV_ClientThink();
2814                 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2815         }
2816
2817         // make sure the velocity is still sane (not a NaN)
2818         SV_CheckVelocity(ent);
2819
2820         // call standard client pre-think
2821         prog->globals.server->time = sv.time;
2822         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2823         PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2824
2825         // make sure the velocity is still sane (not a NaN)
2826         SV_CheckVelocity(ent);
2827 }
2828
2829 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2830 {
2831         // don't do physics on disconnected clients, FrikBot relies on this
2832         if (!host_client->spawned)
2833                 return;
2834
2835         // make sure the velocity is sane (not a NaN)
2836         SV_CheckVelocity(ent);
2837
2838         // call standard player post-think
2839         prog->globals.server->time = sv.time;
2840         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2841         PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2842
2843         // make sure the velocity is still sane (not a NaN)
2844         SV_CheckVelocity(ent);
2845
2846         if(ent->fields.server->fixangle)
2847         {
2848                 // angle fixing was requested by physics code...
2849                 // so store the current angles for later use
2850                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2851                 host_client->fixangle_angles_set = TRUE;
2852
2853                 // and clear fixangle for the next frame
2854                 ent->fields.server->fixangle = 0;
2855         }
2856
2857         // decrement the countdown variable used to decide when to go back to
2858         // synchronous physics
2859         if (host_client->clmovement_inputtimeout > sv.frametime)
2860                 host_client->clmovement_inputtimeout -= sv.frametime;
2861         else
2862                 host_client->clmovement_inputtimeout = 0;
2863 }
2864
2865 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2866 {
2867         // don't do physics on disconnected clients, FrikBot relies on this
2868         if (!host_client->spawned)
2869         {
2870                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2871                 return;
2872         }
2873
2874         // make sure the velocity is sane (not a NaN)
2875         SV_CheckVelocity(ent);
2876
2877         switch ((int) ent->fields.server->movetype)
2878         {
2879         case MOVETYPE_PUSH:
2880         case MOVETYPE_FAKEPUSH:
2881                 SV_Physics_Pusher (ent);
2882                 break;
2883         case MOVETYPE_NONE:
2884                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2885                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2886                         SV_RunThink (ent);
2887                 break;
2888         case MOVETYPE_FOLLOW:
2889                 SV_Physics_Follow (ent);
2890                 break;
2891         case MOVETYPE_NOCLIP:
2892                 SV_RunThink(ent);
2893                 SV_CheckWater(ent);
2894                 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2895                 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2896                 break;
2897         case MOVETYPE_STEP:
2898                 SV_Physics_Step (ent);
2899                 break;
2900         case MOVETYPE_WALK:
2901                 SV_RunThink (ent);
2902                 // don't run physics here if running asynchronously
2903                 if (host_client->clmovement_inputtimeout <= 0)
2904                         SV_WalkMove (ent);
2905                 break;
2906         case MOVETYPE_TOSS:
2907         case MOVETYPE_BOUNCE:
2908         case MOVETYPE_BOUNCEMISSILE:
2909         case MOVETYPE_FLYMISSILE:
2910                 // regular thinking
2911                 SV_RunThink (ent);
2912                 SV_Physics_Toss (ent);
2913                 break;
2914         case MOVETYPE_FLY:
2915                 SV_RunThink (ent);
2916                 SV_WalkMove (ent);
2917                 break;
2918         case MOVETYPE_PHYSICS:
2919                 SV_RunThink (ent);
2920                 break;
2921         default:
2922                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2923                 break;
2924         }
2925
2926         SV_CheckVelocity (ent);
2927
2928         SV_LinkEdict(ent);
2929         SV_LinkEdict_TouchAreaGrid(ent);
2930
2931         SV_CheckVelocity (ent);
2932 }
2933
2934 /*
2935 ================
2936 SV_Physics
2937
2938 ================
2939 */
2940 void SV_Physics (void)
2941 {
2942         int i;
2943         prvm_edict_t *ent;
2944
2945 // let the progs know that a new frame has started
2946         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2947         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2948         prog->globals.server->time = sv.time;
2949         prog->globals.server->frametime = sv.frametime;
2950         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2951
2952         // run physics engine
2953         World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2954
2955 //
2956 // treat each object in turn
2957 //
2958
2959         // if force_retouch, relink all the entities
2960         if (prog->globals.server->force_retouch > 0)
2961                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2962                         if (!ent->priv.server->free)
2963                                 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2964
2965         if (sv_gameplayfix_consistentplayerprethink.integer)
2966         {
2967                 // run physics on the client entities in 3 stages
2968                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2969                         if (!ent->priv.server->free)
2970                                 SV_Physics_ClientEntity_PreThink(ent);
2971
2972                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2973                         if (!ent->priv.server->free)
2974                                 SV_Physics_ClientEntity(ent);
2975
2976                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2977                         if (!ent->priv.server->free)
2978                                 SV_Physics_ClientEntity_PostThink(ent);
2979         }
2980         else
2981         {
2982                 // run physics on the client entities
2983                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2984                 {
2985                         if (!ent->priv.server->free)
2986                         {
2987                                 SV_Physics_ClientEntity_PreThink(ent);
2988                                 SV_Physics_ClientEntity(ent);
2989                                 SV_Physics_ClientEntity_PostThink(ent);
2990                         }
2991                 }
2992         }
2993
2994         // run physics on all the non-client entities
2995         if (!sv_freezenonclients.integer)
2996         {
2997                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2998                         if (!ent->priv.server->free)
2999                                 SV_Physics_Entity(ent);
3000                 // make a second pass to see if any ents spawned this frame and make
3001                 // sure they run their move/think
3002                 if (sv_gameplayfix_delayprojectiles.integer < 0)
3003                         for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3004                                 if (!ent->priv.server->move && !ent->priv.server->free)
3005                                         SV_Physics_Entity(ent);
3006         }
3007
3008         if (prog->globals.server->force_retouch > 0)
3009                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
3010
3011         // LordHavoc: endframe support
3012         if (prog->funcoffsets.EndFrame)
3013         {
3014                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
3015                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
3016                 prog->globals.server->time = sv.time;
3017                 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
3018         }
3019
3020         // decrement prog->num_edicts if the highest number entities died
3021         for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3022
3023         if (!sv_freezenonclients.integer)
3024                 sv.time += sv.frametime;
3025 }