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