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