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