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