]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
make ODE entities call touch functions
[xonotic/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23
24 /*
25
26
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
28
29 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
30
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
37
38 solid_edge items only clip against bsp models.
39
40 */
41
42 #define MOVE_EPSILON    0.01
43
44 void SV_Physics_Toss (prvm_edict_t *ent);
45
46 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         vec3_t end;
1486
1487         VectorAdd (ent->fields.server->origin, push, end);
1488
1489         if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1490                 type = MOVE_MISSILE;
1491         else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1492                 type = MOVE_NOMONSTERS; // only clip against bmodels
1493         else
1494                 type = MOVE_NORMAL;
1495
1496         *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1497         if (trace->bmodelstartsolid && failonbmodelstartsolid)
1498                 return true;
1499
1500         VectorCopy (trace->endpos, ent->fields.server->origin);
1501         SV_LinkEdict(ent);
1502
1503 #if 0
1504         if(!trace->startsolid)
1505         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)
1506         {
1507                 Con_Printf("something eeeeevil happened\n");
1508         }
1509 #endif
1510
1511         if (dolink)
1512                 SV_LinkEdict_TouchAreaGrid(ent);
1513
1514         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))))
1515                 return SV_Impact (ent, trace);
1516
1517         return true;
1518 }
1519
1520
1521 /*
1522 ============
1523 SV_PushMove
1524
1525 ============
1526 */
1527 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1528 {
1529         int i, e, index;
1530         int pusherowner, pusherprog;
1531         int checkcontents;
1532         qboolean rotated;
1533         float savesolid, movetime2, pushltime;
1534         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1535         int num_moved;
1536         int numcheckentities;
1537         static prvm_edict_t *checkentities[MAX_EDICTS];
1538         dp_model_t *pushermodel;
1539         trace_t trace, trace2;
1540         matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1541         unsigned short moved_edicts[MAX_EDICTS];
1542
1543         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])
1544         {
1545                 pusher->fields.server->ltime += movetime;
1546                 return;
1547         }
1548
1549         switch ((int) pusher->fields.server->solid)
1550         {
1551         // LordHavoc: valid pusher types
1552         case SOLID_BSP:
1553         case SOLID_BBOX:
1554         case SOLID_SLIDEBOX:
1555         case SOLID_CORPSE: // LordHavoc: this would be weird...
1556                 break;
1557         // LordHavoc: no collisions
1558         case SOLID_NOT:
1559         case SOLID_TRIGGER:
1560                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1561                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1562                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1563                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1564                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1565                 pusher->fields.server->ltime += movetime;
1566                 SV_LinkEdict(pusher);
1567                 return;
1568         default:
1569                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1570                 return;
1571         }
1572         index = (int) pusher->fields.server->modelindex;
1573         if (index < 1 || index >= MAX_MODELS)
1574         {
1575                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1576                 return;
1577         }
1578         pushermodel = sv.models[index];
1579         pusherowner = pusher->fields.server->owner;
1580         pusherprog = PRVM_EDICT_TO_PROG(pusher);
1581
1582         rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1583
1584         movetime2 = movetime;
1585         VectorScale(pusher->fields.server->velocity, movetime2, move1);
1586         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1587         if (moveangle[0] || moveangle[2])
1588         {
1589                 for (i = 0;i < 3;i++)
1590                 {
1591                         if (move1[i] > 0)
1592                         {
1593                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1594                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1595                         }
1596                         else
1597                         {
1598                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1599                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1600                         }
1601                 }
1602         }
1603         else if (moveangle[1])
1604         {
1605                 for (i = 0;i < 3;i++)
1606                 {
1607                         if (move1[i] > 0)
1608                         {
1609                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1610                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1611                         }
1612                         else
1613                         {
1614                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1615                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1616                         }
1617                 }
1618         }
1619         else
1620         {
1621                 for (i = 0;i < 3;i++)
1622                 {
1623                         if (move1[i] > 0)
1624                         {
1625                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1626                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1627                         }
1628                         else
1629                         {
1630                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1631                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1632                         }
1633                 }
1634         }
1635
1636         VectorNegate (moveangle, a);
1637         AngleVectorsFLU (a, forward, left, up);
1638
1639         VectorCopy (pusher->fields.server->origin, pushorig);
1640         VectorCopy (pusher->fields.server->angles, pushang);
1641         pushltime = pusher->fields.server->ltime;
1642
1643 // move the pusher to its final position
1644
1645         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1646         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1647         pusher->fields.server->ltime += movetime;
1648         SV_LinkEdict(pusher);
1649
1650         pushermodel = NULL;
1651         if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1652                 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1653         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);
1654         Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1655
1656         savesolid = pusher->fields.server->solid;
1657
1658 // see if any solid entities are inside the final position
1659         num_moved = 0;
1660
1661         numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1662         for (e = 0;e < numcheckentities;e++)
1663         {
1664                 prvm_edict_t *check = checkentities[e];
1665                 int movetype = (int)check->fields.server->movetype;
1666                 switch(movetype)
1667                 {
1668                 case MOVETYPE_NONE:
1669                 case MOVETYPE_PUSH:
1670                 case MOVETYPE_FOLLOW:
1671                 case MOVETYPE_NOCLIP:
1672                 case MOVETYPE_FAKEPUSH:
1673                         continue;
1674                 default:
1675                         break;
1676                 }
1677
1678                 if (check->fields.server->owner == pusherprog)
1679                         continue;
1680
1681                 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1682                         continue;
1683
1684                 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1685
1686                 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1687                 check->priv.server->waterposition_forceupdate = true;
1688
1689                 checkcontents = SV_GenericHitSuperContentsMask(check);
1690
1691                 // if the entity is standing on the pusher, it will definitely be moved
1692                 // if the entity is not standing on the pusher, but is in the pusher's
1693                 // final position, move it
1694                 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1695                 {
1696                         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);
1697                         //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1698                         if (!trace.startsolid)
1699                         {
1700                                 //Con_Printf("- not in solid\n");
1701                                 continue;
1702                         }
1703                 }
1704
1705                 if (rotated)
1706                 {
1707                         vec3_t org2;
1708                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1709                         org2[0] = DotProduct (org, forward);
1710                         org2[1] = DotProduct (org, left);
1711                         org2[2] = DotProduct (org, up);
1712                         VectorSubtract (org2, org, move);
1713                         VectorAdd (move, move1, move);
1714                 }
1715                 else
1716                         VectorCopy (move1, move);
1717
1718                 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1719
1720                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1721                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1722                 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1723
1724                 // physics objects need better collisions than this code can do
1725                 if (movetype == MOVETYPE_PHYSICS)
1726                 {
1727                         VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1728                         SV_LinkEdict(check);
1729                         SV_LinkEdict_TouchAreaGrid(check);
1730                         continue;
1731                 }
1732
1733                 // try moving the contacted entity
1734                 pusher->fields.server->solid = SOLID_NOT;
1735                 if(!SV_PushEntity (&trace, check, move, true, true))
1736                 {
1737                         // entity "check" got teleported
1738                         check->fields.server->angles[1] += trace.fraction * moveangle[1];
1739                         pusher->fields.server->solid = savesolid; // was SOLID_BSP
1740                         continue; // pushed enough
1741                 }
1742                 // FIXME: turn players specially
1743                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1744                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1745                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1746
1747                 // this trace.fraction < 1 check causes items to fall off of pushers
1748                 // if they pass under or through a wall
1749                 // the groundentity check causes items to fall off of ledges
1750                 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1751                         check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1752
1753                 // if it is still inside the pusher, block
1754                 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);
1755                 if (trace.startsolid)
1756                 {
1757                         // try moving the contacted entity a tiny bit further to account for precision errors
1758                         vec3_t move2;
1759                         pusher->fields.server->solid = SOLID_NOT;
1760                         VectorScale(move, 1.1, move2);
1761                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1762                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1763                         if(!SV_PushEntity (&trace2, check, move2, true, true))
1764                         {
1765                                 // entity "check" got teleported
1766                                 continue;
1767                         }
1768                         pusher->fields.server->solid = savesolid;
1769                         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);
1770                         if (trace.startsolid)
1771                         {
1772                                 // try moving the contacted entity a tiny bit less to account for precision errors
1773                                 pusher->fields.server->solid = SOLID_NOT;
1774                                 VectorScale(move, 0.9, move2);
1775                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1776                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1777                                 if(!SV_PushEntity (&trace2, check, move2, true, true))
1778                                 {
1779                                         // entity "check" got teleported
1780                                         continue;
1781                                 }
1782                                 pusher->fields.server->solid = savesolid;
1783                                 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);
1784                                 if (trace.startsolid)
1785                                 {
1786                                         // still inside pusher, so it's really blocked
1787
1788                                         // fail the move
1789                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1790                                                 continue;
1791                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1792                                         {
1793                                                 // corpse
1794                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1795                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1796                                                 continue;
1797                                         }
1798
1799                                         VectorCopy (pushorig, pusher->fields.server->origin);
1800                                         VectorCopy (pushang, pusher->fields.server->angles);
1801                                         pusher->fields.server->ltime = pushltime;
1802                                         SV_LinkEdict(pusher);
1803
1804                                         // move back any entities we already moved
1805                                         for (i = 0;i < num_moved;i++)
1806                                         {
1807                                                 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1808                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1809                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1810                                                 SV_LinkEdict(ed);
1811                                         }
1812
1813                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1814                                         if (pusher->fields.server->blocked)
1815                                         {
1816                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1817                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1818                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1819                                         }
1820                                         break;
1821                                 }
1822                         }
1823                 }
1824         }
1825         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1826         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1827         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1828 }
1829
1830 /*
1831 ================
1832 SV_Physics_Pusher
1833
1834 ================
1835 */
1836 void SV_Physics_Pusher (prvm_edict_t *ent)
1837 {
1838         float thinktime, oldltime, movetime;
1839
1840         oldltime = ent->fields.server->ltime;
1841
1842         thinktime = ent->fields.server->nextthink;
1843         if (thinktime < ent->fields.server->ltime + sv.frametime)
1844         {
1845                 movetime = thinktime - ent->fields.server->ltime;
1846                 if (movetime < 0)
1847                         movetime = 0;
1848         }
1849         else
1850                 movetime = sv.frametime;
1851
1852         if (movetime)
1853                 // advances ent->fields.server->ltime if not blocked
1854                 SV_PushMove (ent, movetime);
1855
1856         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1857         {
1858                 ent->fields.server->nextthink = 0;
1859                 prog->globals.server->time = sv.time;
1860                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1861                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1862                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1863         }
1864 }
1865
1866
1867 /*
1868 ===============================================================================
1869
1870 CLIENT MOVEMENT
1871
1872 ===============================================================================
1873 */
1874
1875 static float unstickoffsets[] =
1876 {
1877         // poutting -/+z changes first as they are least weird
1878          0,  0,  -1,
1879          0,  0,  1,
1880          // x or y changes
1881         -1,  0,  0,
1882          1,  0,  0,
1883          0, -1,  0,
1884          0,  1,  0,
1885          // x and y changes
1886         -1, -1,  0,
1887          1, -1,  0,
1888         -1,  1,  0,
1889          1,  1,  0,
1890 };
1891
1892 typedef enum unstickresult_e
1893 {
1894         UNSTICK_STUCK = 0,
1895         UNSTICK_GOOD = 1,
1896         UNSTICK_UNSTUCK = 2
1897 }
1898 unstickresult_t;
1899
1900 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1901 {
1902         int i, maxunstick;
1903
1904         // if not stuck in a bmodel, just return
1905         if (!SV_TestEntityPosition(ent, vec3_origin))
1906                 return UNSTICK_GOOD;
1907
1908         for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1909         {
1910                 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1911                 {
1912                         VectorCopy(unstickoffsets + i, offset);
1913                         SV_LinkEdict(ent);
1914                         //SV_LinkEdict_TouchAreaGrid(ent);
1915                         return UNSTICK_UNSTUCK;
1916                 }
1917         }
1918
1919         maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1920         // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1921
1922         for(i = 2; i <= maxunstick; ++i)
1923         {
1924                 VectorClear(offset);
1925                 offset[2] = -i;
1926                 if (!SV_TestEntityPosition(ent, offset))
1927                 {
1928                         SV_LinkEdict(ent);
1929                         //SV_LinkEdict_TouchAreaGrid(ent);
1930                         return UNSTICK_UNSTUCK;
1931                 }
1932                 offset[2] = i;
1933                 if (!SV_TestEntityPosition(ent, offset))
1934                 {
1935                         SV_LinkEdict(ent);
1936                         //SV_LinkEdict_TouchAreaGrid(ent);
1937                         return UNSTICK_UNSTUCK;
1938                 }
1939         }
1940
1941         return UNSTICK_STUCK;
1942 }
1943
1944 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1945 {
1946         vec3_t offset;
1947         switch(SV_UnstickEntityReturnOffset(ent, offset))
1948         {
1949                 case UNSTICK_GOOD:
1950                         return true;
1951                 case UNSTICK_UNSTUCK:
1952                         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]);
1953                         return true;
1954                 case UNSTICK_STUCK:
1955                         if (developer.integer >= 100)
1956                                 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1957                         return false;
1958                 default:
1959                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1960                         return false;
1961         }
1962 }
1963
1964 /*
1965 =============
1966 SV_CheckStuck
1967
1968 This is a big hack to try and fix the rare case of getting stuck in the world
1969 clipping hull.
1970 =============
1971 */
1972 void SV_CheckStuck (prvm_edict_t *ent)
1973 {
1974         vec3_t offset;
1975
1976         switch(SV_UnstickEntityReturnOffset(ent, offset))
1977         {
1978                 case UNSTICK_GOOD:
1979                         VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1980                         break;
1981                 case UNSTICK_UNSTUCK:
1982                         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]);
1983                         break;
1984                 case UNSTICK_STUCK:
1985                         VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1986                         if (!SV_TestEntityPosition(ent, offset))
1987                         {
1988                                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1989                                 SV_LinkEdict(ent);
1990                                 //SV_LinkEdict_TouchAreaGrid(ent);
1991                         }
1992                         else
1993                                 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1994                         break;
1995                 default:
1996                         Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1997         }
1998 }
1999
2000
2001 /*
2002 =============
2003 SV_CheckWater
2004 =============
2005 */
2006 qboolean SV_CheckWater (prvm_edict_t *ent)
2007 {
2008         int cont;
2009         int nNativeContents;
2010         vec3_t point;
2011
2012         point[0] = ent->fields.server->origin[0];
2013         point[1] = ent->fields.server->origin[1];
2014         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2015
2016         // DRESK - Support for Entity Contents Transition Event
2017         // NOTE: Some logic needed to be slightly re-ordered
2018         // to not affect performance and allow for the feature.
2019
2020         // Acquire Super Contents Prior to Resets
2021         cont = SV_PointSuperContents(point);
2022         // Acquire Native Contents Here
2023         nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2024
2025         // DRESK - Support for Entity Contents Transition Event
2026         if(ent->fields.server->watertype)
2027                 // Entity did NOT Spawn; Check
2028                 SV_CheckContentsTransition(ent, nNativeContents);
2029
2030
2031         ent->fields.server->waterlevel = 0;
2032         ent->fields.server->watertype = CONTENTS_EMPTY;
2033         cont = SV_PointSuperContents(point);
2034         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2035         {
2036                 ent->fields.server->watertype = nNativeContents;
2037                 ent->fields.server->waterlevel = 1;
2038                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2039                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2040                 {
2041                         ent->fields.server->waterlevel = 2;
2042                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2043                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2044                                 ent->fields.server->waterlevel = 3;
2045                 }
2046         }
2047
2048         return ent->fields.server->waterlevel > 1;
2049 }
2050
2051 /*
2052 ============
2053 SV_WallFriction
2054
2055 ============
2056 */
2057 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2058 {
2059         float d, i;
2060         vec3_t forward, into, side;
2061
2062         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2063         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2064         {
2065                 // cut the tangential velocity
2066                 i = DotProduct (stepnormal, ent->fields.server->velocity);
2067                 VectorScale (stepnormal, i, into);
2068                 VectorSubtract (ent->fields.server->velocity, into, side);
2069                 ent->fields.server->velocity[0] = side[0] * (1 + d);
2070                 ent->fields.server->velocity[1] = side[1] * (1 + d);
2071         }
2072 }
2073
2074 #if 0
2075 /*
2076 =====================
2077 SV_TryUnstick
2078
2079 Player has come to a dead stop, possibly due to the problem with limited
2080 float precision at some angle joins in the BSP hull.
2081
2082 Try fixing by pushing one pixel in each direction.
2083
2084 This is a hack, but in the interest of good gameplay...
2085 ======================
2086 */
2087 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2088 {
2089         int i, clip;
2090         vec3_t oldorg, dir;
2091
2092         VectorCopy (ent->fields.server->origin, oldorg);
2093         VectorClear (dir);
2094
2095         for (i=0 ; i<8 ; i++)
2096         {
2097                 // try pushing a little in an axial direction
2098                 switch (i)
2099                 {
2100                         case 0: dir[0] = 2; dir[1] = 0; break;
2101                         case 1: dir[0] = 0; dir[1] = 2; break;
2102                         case 2: dir[0] = -2; dir[1] = 0; break;
2103                         case 3: dir[0] = 0; dir[1] = -2; break;
2104                         case 4: dir[0] = 2; dir[1] = 2; break;
2105                         case 5: dir[0] = -2; dir[1] = 2; break;
2106                         case 6: dir[0] = 2; dir[1] = -2; break;
2107                         case 7: dir[0] = -2; dir[1] = -2; break;
2108                 }
2109
2110                 SV_PushEntity (&trace, ent, dir, false, true);
2111
2112                 // retry the original move
2113                 ent->fields.server->velocity[0] = oldvel[0];
2114                 ent->fields.server->velocity[1] = oldvel[1];
2115                 ent->fields.server->velocity[2] = 0;
2116                 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2117
2118                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2119                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2120                 {
2121                         Con_DPrint("TryUnstick - success.\n");
2122                         return clip;
2123                 }
2124
2125                 // go back to the original pos and try again
2126                 VectorCopy (oldorg, ent->fields.server->origin);
2127         }
2128
2129         // still not moving
2130         VectorClear (ent->fields.server->velocity);
2131         Con_DPrint("TryUnstick - failure.\n");
2132         return 7;
2133 }
2134 #endif
2135
2136 /*
2137 =====================
2138 SV_WalkMove
2139
2140 Only used by players
2141 ======================
2142 */
2143 void SV_WalkMove (prvm_edict_t *ent)
2144 {
2145         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2146         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2147         trace_t downtrace, trace;
2148         qboolean applygravity;
2149
2150         // if frametime is 0 (due to client sending the same timestamp twice),
2151         // don't move
2152         if (sv.frametime <= 0)
2153                 return;
2154
2155         SV_CheckStuck (ent);
2156
2157         applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2158
2159         hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2160
2161         SV_CheckVelocity(ent);
2162
2163         // do a regular slide move unless it looks like you ran into a step
2164         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2165
2166         VectorCopy (ent->fields.server->origin, start_origin);
2167         VectorCopy (ent->fields.server->velocity, start_velocity);
2168
2169         clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2170
2171         // if the move did not hit the ground at any point, we're not on ground
2172         if (!(clip & 1))
2173                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2174
2175         SV_CheckVelocity(ent);
2176         SV_LinkEdict(ent);
2177         SV_LinkEdict_TouchAreaGrid(ent);
2178
2179         if(clip & 8) // teleport
2180                 return;
2181
2182         if ((int)ent->fields.server->flags & FL_WATERJUMP)
2183                 return;
2184
2185         if (sv_nostep.integer)
2186                 return;
2187
2188         VectorCopy(ent->fields.server->origin, originalmove_origin);
2189         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2190         originalmove_clip = clip;
2191         originalmove_flags = (int)ent->fields.server->flags;
2192         originalmove_groundentity = ent->fields.server->groundentity;
2193
2194         // if move didn't block on a step, return
2195         if (clip & 2)
2196         {
2197                 // if move was not trying to move into the step, return
2198                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2199                         return;
2200
2201                 if (ent->fields.server->movetype != MOVETYPE_FLY)
2202                 {
2203                         // return if gibbed by a trigger
2204                         if (ent->fields.server->movetype != MOVETYPE_WALK)
2205                                 return;
2206
2207                         // only step up while jumping if that is enabled
2208                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2209                                 if (!oldonground && ent->fields.server->waterlevel == 0)
2210                                         return;
2211                 }
2212
2213                 // try moving up and forward to go up a step
2214                 // back to start pos
2215                 VectorCopy (start_origin, ent->fields.server->origin);
2216                 VectorCopy (start_velocity, ent->fields.server->velocity);
2217
2218                 // move up
2219                 VectorClear (upmove);
2220                 upmove[2] = sv_stepheight.value;
2221                 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2222                 {
2223                         // we got teleported when upstepping... must abort the move
2224                         return;
2225                 }
2226
2227                 // move forward
2228                 ent->fields.server->velocity[2] = 0;
2229                 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2230                 ent->fields.server->velocity[2] += start_velocity[2];
2231                 if(clip & 8)
2232                 {
2233                         // we got teleported when upstepping... must abort the move
2234                         // note that z velocity handling may not be what QC expects here, but we cannot help it
2235                         return;
2236                 }
2237
2238                 SV_CheckVelocity(ent);
2239                 SV_LinkEdict(ent);
2240                 SV_LinkEdict_TouchAreaGrid(ent);
2241
2242                 // check for stuckness, possibly due to the limited precision of floats
2243                 // in the clipping hulls
2244                 if (clip
2245                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2246                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2247                 {
2248                         //Con_Printf("wall\n");
2249                         // stepping up didn't make any progress, revert to original move
2250                         VectorCopy(originalmove_origin, ent->fields.server->origin);
2251                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2252                         //clip = originalmove_clip;
2253                         ent->fields.server->flags = originalmove_flags;
2254                         ent->fields.server->groundentity = originalmove_groundentity;
2255                         // now try to unstick if needed
2256                         //clip = SV_TryUnstick (ent, oldvel);
2257                         return;
2258                 }
2259
2260                 //Con_Printf("step - ");
2261
2262                 // extra friction based on view angle
2263                 if (clip & 2 && sv_wallfriction.integer)
2264                         SV_WallFriction (ent, stepnormal);
2265         }
2266         // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2267         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))
2268                 return;
2269
2270         // move down
2271         VectorClear (downmove);
2272         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2273         if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2274         {
2275                 // we got teleported when downstepping... must abort the move
2276                 return;
2277         }
2278
2279         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2280         {
2281                 // this has been disabled so that you can't jump when you are stepping
2282                 // up while already jumping (also known as the Quake2 double jump bug)
2283 #if 0
2284                 // LordHavoc: disabled this check so you can walk on monsters/players
2285                 //if (ent->fields.server->solid == SOLID_BSP)
2286                 {
2287                         //Con_Printf("onground\n");
2288                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
2289                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2290                 }
2291 #endif
2292         }
2293         else
2294         {
2295                 //Con_Printf("slope\n");
2296                 // if the push down didn't end up on good ground, use the move without
2297                 // the step up.  This happens near wall / slope combinations, and can
2298                 // cause the player to hop up higher on a slope too steep to climb
2299                 VectorCopy(originalmove_origin, ent->fields.server->origin);
2300                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2301                 //clip = originalmove_clip;
2302                 ent->fields.server->flags = originalmove_flags;
2303                 ent->fields.server->groundentity = originalmove_groundentity;
2304         }
2305
2306         SV_CheckVelocity(ent);
2307         SV_LinkEdict(ent);
2308         SV_LinkEdict_TouchAreaGrid(ent);
2309 }
2310
2311 //============================================================================
2312
2313 /*
2314 =============
2315 SV_Physics_Follow
2316
2317 Entities that are "stuck" to another entity
2318 =============
2319 */
2320 void SV_Physics_Follow (prvm_edict_t *ent)
2321 {
2322         vec3_t vf, vr, vu, angles, v;
2323         prvm_edict_t *e;
2324
2325         // regular thinking
2326         if (!SV_RunThink (ent))
2327                 return;
2328
2329         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2330         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2331         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])
2332         {
2333                 // quick case for no rotation
2334                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2335         }
2336         else
2337         {
2338                 angles[0] = -ent->fields.server->punchangle[0];
2339                 angles[1] =  ent->fields.server->punchangle[1];
2340                 angles[2] =  ent->fields.server->punchangle[2];
2341                 AngleVectors (angles, vf, vr, vu);
2342                 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];
2343                 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];
2344                 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];
2345                 angles[0] = -e->fields.server->angles[0];
2346                 angles[1] =  e->fields.server->angles[1];
2347                 angles[2] =  e->fields.server->angles[2];
2348                 AngleVectors (angles, vf, vr, vu);
2349                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2350                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2351                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2352         }
2353         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2354         SV_LinkEdict(ent);
2355         //SV_LinkEdict_TouchAreaGrid(ent);
2356 }
2357
2358 /*
2359 ==============================================================================
2360
2361 TOSS / BOUNCE
2362
2363 ==============================================================================
2364 */
2365
2366 /*
2367 =============
2368 SV_CheckWaterTransition
2369
2370 =============
2371 */
2372 void SV_CheckWaterTransition (prvm_edict_t *ent)
2373 {
2374         int cont;
2375         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2376         if (!ent->fields.server->watertype)
2377         {
2378                 // just spawned here
2379                 ent->fields.server->watertype = cont;
2380                 ent->fields.server->waterlevel = 1;
2381                 return;
2382         }
2383
2384         // DRESK - Support for Entity Contents Transition Event
2385         // NOTE: Call here BEFORE updating the watertype below,
2386         // and suppress watersplash sound if a valid function
2387         // call was made to allow for custom "splash" sounds.
2388         if( !SV_CheckContentsTransition(ent, cont) )
2389         { // Contents Transition Function Invalid; Potentially Play Water Sound
2390                 // check if the entity crossed into or out of water
2391                 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2392                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2393         }
2394
2395         if (cont <= CONTENTS_WATER)
2396         {
2397                 ent->fields.server->watertype = cont;
2398                 ent->fields.server->waterlevel = 1;
2399         }
2400         else
2401         {
2402                 ent->fields.server->watertype = CONTENTS_EMPTY;
2403                 ent->fields.server->waterlevel = 0;
2404         }
2405 }
2406
2407 /*
2408 =============
2409 SV_Physics_Toss
2410
2411 Toss, bounce, and fly movement.  When onground, do nothing.
2412 =============
2413 */
2414 void SV_Physics_Toss (prvm_edict_t *ent)
2415 {
2416         trace_t trace;
2417         vec3_t move;
2418         vec_t movetime;
2419         int bump;
2420
2421 // if onground, return without moving
2422         if ((int)ent->fields.server->flags & FL_ONGROUND)
2423         {
2424                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2425                 {
2426                         // don't stick to ground if onground and moving upward
2427                         ent->fields.server->flags -= FL_ONGROUND;
2428                 }
2429                 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2430                 {
2431                         // we can trust FL_ONGROUND if groundentity is world because it never moves
2432                         return;
2433                 }
2434                 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2435                 {
2436                         // if ent was supported by a brush model on previous frame,
2437                         // and groundentity is now freed, set groundentity to 0 (world)
2438                         // which leaves it suspended in the air
2439                         ent->fields.server->groundentity = 0;
2440                         if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2441                                 return;
2442                 }
2443         }
2444         ent->priv.server->suspendedinairflag = false;
2445
2446         SV_CheckVelocity (ent);
2447
2448 // add gravity
2449         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2450                 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2451
2452 // move angles
2453         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2454
2455         movetime = sv.frametime;
2456         for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2457         {
2458         // move origin
2459                 VectorScale (ent->fields.server->velocity, movetime, move);
2460                 if(!SV_PushEntity (&trace, ent, move, true, true))
2461                         return; // teleported
2462                 if (ent->priv.server->free)
2463                         return;
2464                 if (trace.bmodelstartsolid)
2465                 {
2466                         // try to unstick the entity
2467                         SV_UnstickEntity(ent);
2468                         if(!SV_PushEntity (&trace, ent, move, false, true))
2469                                 return; // teleported
2470                         if (ent->priv.server->free)
2471                                 return;
2472                 }
2473                 if (trace.fraction == 1)
2474                         break;
2475                 movetime *= 1 - min(1, trace.fraction);
2476                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2477                 {
2478                         prvm_eval_t *val;
2479                         float bouncefactor = 1.0f;
2480                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2481                         if (val!=0 && val->_float)
2482                                 bouncefactor = val->_float;
2483
2484                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2485                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2486                 }
2487                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2488                 {
2489                         float d, ent_gravity;
2490                         prvm_eval_t *val;
2491                         float bouncefactor = 0.5f;
2492                         float bouncestop = 60.0f / 800.0f;
2493
2494                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2495                         if (val!=0 && val->_float)
2496                                 bouncefactor = val->_float;
2497
2498                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2499                         if (val!=0 && val->_float)
2500                                 bouncestop = val->_float;
2501
2502                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2503                         // LordHavoc: fixed grenades not bouncing when fired down a slope
2504                         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2505                         if (val!=0 && val->_float)
2506                                 ent_gravity = val->_float;
2507                         else
2508                                 ent_gravity = 1.0;
2509                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
2510                         {
2511                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2512                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2513                                 {
2514                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2515                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2516                                         VectorClear (ent->fields.server->velocity);
2517                                         VectorClear (ent->fields.server->avelocity);
2518                                 }
2519                                 else
2520                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2521                         }
2522                         else
2523                         {
2524                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2525                                 {
2526                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2527                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2528                                         VectorClear (ent->fields.server->velocity);
2529                                         VectorClear (ent->fields.server->avelocity);
2530                                 }
2531                                 else
2532                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2533                         }
2534                 }
2535                 else
2536                 {
2537                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2538                         if (trace.plane.normal[2] > 0.7)
2539                         {
2540                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2541                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2542                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2543                                         ent->priv.server->suspendedinairflag = true;
2544                                 VectorClear (ent->fields.server->velocity);
2545                                 VectorClear (ent->fields.server->avelocity);
2546                         }
2547                         else
2548                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2549                 }
2550                 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2551                         break;
2552         }
2553
2554 // check for in water
2555         SV_CheckWaterTransition (ent);
2556 }
2557
2558 /*
2559 ===============================================================================
2560
2561 STEPPING MOVEMENT
2562
2563 ===============================================================================
2564 */
2565
2566 /*
2567 =============
2568 SV_Physics_Step
2569
2570 Monsters freefall when they don't have a ground entity, otherwise
2571 all movement is done with discrete steps.
2572
2573 This is also used for objects that have become still on the ground, but
2574 will fall if the floor is pulled out from under them.
2575 =============
2576 */
2577 void SV_Physics_Step (prvm_edict_t *ent)
2578 {
2579         int flags = (int)ent->fields.server->flags;
2580
2581         // DRESK
2582         // Backup Velocity in the event that movetypesteplandevent is called,
2583         // to provide a parameter with the entity's velocity at impact.
2584         prvm_eval_t *movetypesteplandevent;
2585         vec3_t backupVelocity;
2586         VectorCopy(ent->fields.server->velocity, backupVelocity);
2587         // don't fall at all if fly/swim
2588         if (!(flags & (FL_FLY | FL_SWIM)))
2589         {
2590                 if (flags & FL_ONGROUND)
2591                 {
2592                         // freefall if onground and moving upward
2593                         // freefall if not standing on a world surface (it may be a lift or trap door)
2594                         if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2595                         {
2596                                 ent->fields.server->flags -= FL_ONGROUND;
2597                                 SV_CheckVelocity(ent);
2598                                 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2599                                 SV_LinkEdict(ent);
2600                                 SV_LinkEdict_TouchAreaGrid(ent);
2601                                 ent->priv.server->waterposition_forceupdate = true;
2602                         }
2603                 }
2604                 else
2605                 {
2606                         // freefall if not onground
2607                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2608
2609                         SV_CheckVelocity(ent);
2610                         SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2611                         SV_LinkEdict(ent);
2612                         SV_LinkEdict_TouchAreaGrid(ent);
2613
2614                         // just hit ground
2615                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2616                         {
2617                                 // DRESK - Check for Entity Land Event Function
2618                                 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2619
2620                                 if(movetypesteplandevent->function)
2621                                 { // Valid Function; Execute
2622                                         // Prepare Parameters
2623                                                 // Assign Velocity at Impact
2624                                                 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2625                                                 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2626                                                 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2627                                                 // Assign Self
2628                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2629                                         // Execute VM Function
2630                                         PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2631                                 }
2632                                 else
2633                                 // Check for Engine Landing Sound
2634                                 if(sv_sound_land.string)
2635                                         SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2636                         }
2637                         ent->priv.server->waterposition_forceupdate = true;
2638                 }
2639         }
2640
2641 // regular thinking
2642         if (!SV_RunThink(ent))
2643                 return;
2644
2645         if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2646         {
2647                 ent->priv.server->waterposition_forceupdate = false;
2648                 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2649                 SV_CheckWaterTransition(ent);
2650         }
2651 }
2652
2653 //============================================================================
2654
2655 static void SV_Physics_Entity (prvm_edict_t *ent)
2656 {
2657         // don't run think/move on newly spawned projectiles as it messes up
2658         // movement interpolation and rocket trails, and is inconsistent with
2659         // respect to entities spawned in the same frame
2660         // (if an ent spawns a higher numbered ent, it moves in the same frame,
2661         //  but if it spawns a lower numbered ent, it doesn't - this never moves
2662         //  ents in the first frame regardless)
2663         qboolean runmove = ent->priv.server->move;
2664         ent->priv.server->move = true;
2665         if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2666                 return;
2667         switch ((int) ent->fields.server->movetype)
2668         {
2669         case MOVETYPE_PUSH:
2670         case MOVETYPE_FAKEPUSH:
2671                 SV_Physics_Pusher (ent);
2672                 break;
2673         case MOVETYPE_NONE:
2674                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2675                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2676                         SV_RunThink (ent);
2677                 break;
2678         case MOVETYPE_FOLLOW:
2679                 SV_Physics_Follow (ent);
2680                 break;
2681         case MOVETYPE_NOCLIP:
2682                 if (SV_RunThink(ent))
2683                 {
2684                         SV_CheckWater(ent);
2685                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2686                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2687                 }
2688                 SV_LinkEdict(ent);
2689                 break;
2690         case MOVETYPE_STEP:
2691                 SV_Physics_Step (ent);
2692                 break;
2693         case MOVETYPE_WALK:
2694                 if (SV_RunThink (ent))
2695                         SV_WalkMove (ent);
2696                 break;
2697         case MOVETYPE_TOSS:
2698         case MOVETYPE_BOUNCE:
2699         case MOVETYPE_BOUNCEMISSILE:
2700         case MOVETYPE_FLYMISSILE:
2701         case MOVETYPE_FLY:
2702                 // regular thinking
2703                 if (SV_RunThink (ent))
2704                         SV_Physics_Toss (ent);
2705                 break;
2706         case MOVETYPE_PHYSICS:
2707                 if (SV_RunThink(ent))
2708                 {
2709                         SV_LinkEdict(ent);
2710                         SV_LinkEdict_TouchAreaGrid(ent);
2711                 }
2712                 break;
2713         default:
2714                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2715                 break;
2716         }
2717 }
2718
2719 void SV_Physics_ClientMove(void)
2720 {
2721         prvm_edict_t *ent;
2722         ent = host_client->edict;
2723
2724         // call player physics, this needs the proper frametime
2725         prog->globals.server->frametime = sv.frametime;
2726         SV_ClientThink();
2727
2728         // call standard client pre-think, with frametime = 0
2729         prog->globals.server->time = sv.time;
2730         prog->globals.server->frametime = 0;
2731         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2732         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2733         prog->globals.server->frametime = sv.frametime;
2734
2735         // make sure the velocity is sane (not a NaN)
2736         SV_CheckVelocity(ent);
2737
2738         // perform MOVETYPE_WALK behavior
2739         SV_WalkMove (ent);
2740
2741         // call standard player post-think, with frametime = 0
2742         prog->globals.server->time = sv.time;
2743         prog->globals.server->frametime = 0;
2744         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2745         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2746         prog->globals.server->frametime = sv.frametime;
2747
2748         if(ent->fields.server->fixangle)
2749         {
2750                 // angle fixing was requested by physics code...
2751                 // so store the current angles for later use
2752                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2753                 host_client->fixangle_angles_set = TRUE;
2754
2755                 // and clear fixangle for the next frame
2756                 ent->fields.server->fixangle = 0;
2757         }
2758 }
2759
2760 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2761 {
2762         // don't do physics on disconnected clients, FrikBot relies on this
2763         if (!host_client->spawned)
2764                 return;
2765
2766         // make sure the velocity is sane (not a NaN)
2767         SV_CheckVelocity(ent);
2768
2769         // don't run physics here if running asynchronously
2770         if (host_client->clmovement_inputtimeout <= 0)
2771         {
2772                 SV_ClientThink();
2773                 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2774         }
2775
2776         // make sure the velocity is still sane (not a NaN)
2777         SV_CheckVelocity(ent);
2778
2779         // call standard client pre-think
2780         prog->globals.server->time = sv.time;
2781         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2782         PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2783
2784         // make sure the velocity is still sane (not a NaN)
2785         SV_CheckVelocity(ent);
2786 }
2787
2788 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2789 {
2790         // don't do physics on disconnected clients, FrikBot relies on this
2791         if (!host_client->spawned)
2792                 return;
2793
2794         // make sure the velocity is sane (not a NaN)
2795         SV_CheckVelocity(ent);
2796
2797         // call standard player post-think
2798         prog->globals.server->time = sv.time;
2799         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2800         PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2801
2802         // make sure the velocity is still sane (not a NaN)
2803         SV_CheckVelocity(ent);
2804
2805         if(ent->fields.server->fixangle)
2806         {
2807                 // angle fixing was requested by physics code...
2808                 // so store the current angles for later use
2809                 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2810                 host_client->fixangle_angles_set = TRUE;
2811
2812                 // and clear fixangle for the next frame
2813                 ent->fields.server->fixangle = 0;
2814         }
2815
2816         // decrement the countdown variable used to decide when to go back to
2817         // synchronous physics
2818         if (host_client->clmovement_inputtimeout > sv.frametime)
2819                 host_client->clmovement_inputtimeout -= sv.frametime;
2820         else
2821                 host_client->clmovement_inputtimeout = 0;
2822 }
2823
2824 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2825 {
2826         // don't do physics on disconnected clients, FrikBot relies on this
2827         if (!host_client->spawned)
2828         {
2829                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2830                 return;
2831         }
2832
2833         // make sure the velocity is sane (not a NaN)
2834         SV_CheckVelocity(ent);
2835
2836         switch ((int) ent->fields.server->movetype)
2837         {
2838         case MOVETYPE_PUSH:
2839         case MOVETYPE_FAKEPUSH:
2840                 SV_Physics_Pusher (ent);
2841                 break;
2842         case MOVETYPE_NONE:
2843                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2844                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2845                         SV_RunThink (ent);
2846                 break;
2847         case MOVETYPE_FOLLOW:
2848                 SV_Physics_Follow (ent);
2849                 break;
2850         case MOVETYPE_NOCLIP:
2851                 SV_RunThink(ent);
2852                 SV_CheckWater(ent);
2853                 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2854                 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2855                 break;
2856         case MOVETYPE_STEP:
2857                 SV_Physics_Step (ent);
2858                 break;
2859         case MOVETYPE_WALK:
2860                 SV_RunThink (ent);
2861                 // don't run physics here if running asynchronously
2862                 if (host_client->clmovement_inputtimeout <= 0)
2863                         SV_WalkMove (ent);
2864                 break;
2865         case MOVETYPE_TOSS:
2866         case MOVETYPE_BOUNCE:
2867         case MOVETYPE_BOUNCEMISSILE:
2868         case MOVETYPE_FLYMISSILE:
2869                 // regular thinking
2870                 SV_RunThink (ent);
2871                 SV_Physics_Toss (ent);
2872                 break;
2873         case MOVETYPE_FLY:
2874                 SV_RunThink (ent);
2875                 SV_WalkMove (ent);
2876                 break;
2877         case MOVETYPE_PHYSICS:
2878                 SV_RunThink (ent);
2879                 break;
2880         default:
2881                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2882                 break;
2883         }
2884
2885         SV_CheckVelocity (ent);
2886
2887         SV_LinkEdict(ent);
2888         SV_LinkEdict_TouchAreaGrid(ent);
2889
2890         SV_CheckVelocity (ent);
2891 }
2892
2893 /*
2894 ================
2895 SV_Physics
2896
2897 ================
2898 */
2899 void SV_Physics (void)
2900 {
2901         int i;
2902         prvm_edict_t *ent;
2903
2904 // let the progs know that a new frame has started
2905         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2906         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2907         prog->globals.server->time = sv.time;
2908         prog->globals.server->frametime = sv.frametime;
2909         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2910
2911         // run physics engine
2912         World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2913
2914 //
2915 // treat each object in turn
2916 //
2917
2918         // if force_retouch, relink all the entities
2919         if (prog->globals.server->force_retouch > 0)
2920                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2921                         if (!ent->priv.server->free)
2922                                 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2923
2924         if (sv_gameplayfix_consistentplayerprethink.integer)
2925         {
2926                 // run physics on the client entities in 3 stages
2927                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2928                         if (!ent->priv.server->free)
2929                                 SV_Physics_ClientEntity_PreThink(ent);
2930
2931                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2932                         if (!ent->priv.server->free)
2933                                 SV_Physics_ClientEntity(ent);
2934
2935                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2936                         if (!ent->priv.server->free)
2937                                 SV_Physics_ClientEntity_PostThink(ent);
2938         }
2939         else
2940         {
2941                 // run physics on the client entities
2942                 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2943                 {
2944                         if (!ent->priv.server->free)
2945                         {
2946                                 SV_Physics_ClientEntity_PreThink(ent);
2947                                 SV_Physics_ClientEntity(ent);
2948                                 SV_Physics_ClientEntity_PostThink(ent);
2949                         }
2950                 }
2951         }
2952
2953         // run physics on all the non-client entities
2954         if (!sv_freezenonclients.integer)
2955         {
2956                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2957                         if (!ent->priv.server->free)
2958                                 SV_Physics_Entity(ent);
2959                 // make a second pass to see if any ents spawned this frame and make
2960                 // sure they run their move/think
2961                 if (sv_gameplayfix_delayprojectiles.integer < 0)
2962                         for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2963                                 if (!ent->priv.server->move && !ent->priv.server->free)
2964                                         SV_Physics_Entity(ent);
2965         }
2966
2967         if (prog->globals.server->force_retouch > 0)
2968                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2969
2970         // LordHavoc: endframe support
2971         if (prog->funcoffsets.EndFrame)
2972         {
2973                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2974                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2975                 prog->globals.server->time = sv.time;
2976                 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2977         }
2978
2979         // decrement prog->num_edicts if the highest number entities died
2980         for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2981
2982         if (!sv_freezenonclients.integer)
2983                 sv.time += sv.frametime;
2984 }