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