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