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