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