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