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