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