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