2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
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.
29 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
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
38 solid_edge items only clip against bsp models.
42 #define MOVE_EPSILON 0.01
44 void SV_Physics_Toss (prvm_edict_t *ent);
46 int SV_GetPitchSign(prvm_edict_t *ent)
51 ((modelindex = (int)ent->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[modelindex]))
53 model->type == mod_alias
56 (((unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
58 ((gamemode == GAME_TENEBRAE) && ((unsigned int)ent->fields.server->effects & (16 | 32)))
66 ===============================================================================
70 ===============================================================================
73 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
78 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
79 if (val && val->_float)
80 return (int)val->_float;
81 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
83 if ((int)passedict->fields.server->flags & FL_MONSTER)
84 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
86 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
88 else if (passedict->fields.server->solid == SOLID_CORPSE)
89 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
90 else if (passedict->fields.server->solid == SOLID_TRIGGER)
91 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
93 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
96 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
104 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
106 int i, bodysupercontents;
109 prvm_edict_t *traceowner, *touch;
111 // bounding box of entire move area
112 vec3_t clipboxmins, clipboxmaxs;
113 // size when clipping against monsters
114 vec3_t clipmins2, clipmaxs2;
115 // start and end origin of move
119 // matrices to transform into/out of other entity's space
120 matrix4x4_t matrix, imatrix;
121 // model of other entity
123 // list of entities to test for collisions
125 prvm_edict_t *touchedicts[MAX_EDICTS];
127 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
129 VectorCopy(start, clipstart);
130 VectorClear(clipmins2);
131 VectorClear(clipmaxs2);
132 #if COLLISIONPARANOID >= 3
133 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
137 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
138 cliptrace.bmodelstartsolid = cliptrace.startsolid;
139 if (cliptrace.startsolid || cliptrace.fraction < 1)
140 cliptrace.ent = prog->edicts;
141 if (type == MOVE_WORLDONLY)
144 if (type == MOVE_MISSILE)
146 // LordHavoc: modified this, was = -15, now -= 15
147 for (i = 0;i < 3;i++)
154 // create the bounding box of the entire move
155 for (i = 0;i < 3;i++)
157 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
158 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
161 // debug override to test against everything
162 if (sv_debugmove.integer)
164 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
165 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
168 // if the passedict is world, make it NULL (to avoid two checks each time)
169 if (passedict == prog->edicts)
171 // precalculate prog value for passedict for comparisons
172 passedictprog = PRVM_EDICT_TO_PROG(passedict);
173 // precalculate passedict's owner edict pointer for comparisons
174 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
177 // because this uses World_EntitiestoBox, we know all entity boxes overlap
178 // the clip region, so we can skip culling checks in the loop below
179 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
180 if (numtouchedicts > MAX_EDICTS)
182 // this never happens
183 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
184 numtouchedicts = MAX_EDICTS;
186 for (i = 0;i < numtouchedicts;i++)
188 touch = touchedicts[i];
190 if (touch->fields.server->solid < SOLID_BBOX)
192 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
197 // don't clip against self
198 if (passedict == touch)
200 // don't clip owned entities against owner
201 if (traceowner == touch)
203 // don't clip owner against owned entities
204 if (passedictprog == touch->fields.server->owner)
206 // don't clip points against points (they can't collide)
207 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
211 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
213 // might interact, so do an exact clip
215 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
217 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
218 // if the modelindex is 0, it shouldn't be SOLID_BSP!
219 if (modelindex > 0 && modelindex < MAX_MODELS)
220 model = sv.models[(int)touch->fields.server->modelindex];
221 pitchsign = SV_GetPitchSign(touch);
224 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);
226 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
227 Matrix4x4_Invert_Simple(&imatrix, &matrix);
228 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
229 Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
231 Collision_ClipPointToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
233 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
245 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
246 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
248 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
251 int i, bodysupercontents;
254 prvm_edict_t *traceowner, *touch;
256 // bounding box of entire move area
257 vec3_t clipboxmins, clipboxmaxs;
258 // size when clipping against monsters
259 vec3_t clipmins2, clipmaxs2;
260 // start and end origin of move
261 vec3_t clipstart, clipend;
264 // matrices to transform into/out of other entity's space
265 matrix4x4_t matrix, imatrix;
266 // model of other entity
268 // list of entities to test for collisions
270 prvm_edict_t *touchedicts[MAX_EDICTS];
271 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
275 if(!VectorCompare(start, pEnd))
277 // TRICK: make the trace 1 qu longer!
278 VectorSubtract(pEnd, start, end);
279 len = VectorNormalizeLength(end);
280 VectorAdd(pEnd, end, end);
283 VectorCopy(pEnd, end);
286 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
288 if (VectorCompare(start, end))
289 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
291 VectorCopy(start, clipstart);
292 VectorCopy(end, clipend);
293 VectorClear(clipmins2);
294 VectorClear(clipmaxs2);
295 #if COLLISIONPARANOID >= 3
296 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
300 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask);
301 cliptrace.bmodelstartsolid = cliptrace.startsolid;
302 if (cliptrace.startsolid || cliptrace.fraction < 1)
303 cliptrace.ent = prog->edicts;
304 if (type == MOVE_WORLDONLY)
307 if (type == MOVE_MISSILE)
309 // LordHavoc: modified this, was = -15, now -= 15
310 for (i = 0;i < 3;i++)
317 // create the bounding box of the entire move
318 for (i = 0;i < 3;i++)
320 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
321 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
324 // debug override to test against everything
325 if (sv_debugmove.integer)
327 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
328 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
331 // if the passedict is world, make it NULL (to avoid two checks each time)
332 if (passedict == prog->edicts)
334 // precalculate prog value for passedict for comparisons
335 passedictprog = PRVM_EDICT_TO_PROG(passedict);
336 // precalculate passedict's owner edict pointer for comparisons
337 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
340 // because this uses World_EntitiestoBox, we know all entity boxes overlap
341 // the clip region, so we can skip culling checks in the loop below
342 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
343 if (numtouchedicts > MAX_EDICTS)
345 // this never happens
346 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
347 numtouchedicts = MAX_EDICTS;
349 for (i = 0;i < numtouchedicts;i++)
351 touch = touchedicts[i];
353 if (touch->fields.server->solid < SOLID_BBOX)
355 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
360 // don't clip against self
361 if (passedict == touch)
363 // don't clip owned entities against owner
364 if (traceowner == touch)
366 // don't clip owner against owned entities
367 if (passedictprog == touch->fields.server->owner)
369 // don't clip points against points (they can't collide)
370 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
374 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
376 // might interact, so do an exact clip
378 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
380 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
381 // if the modelindex is 0, it shouldn't be SOLID_BSP!
382 if (modelindex > 0 && modelindex < MAX_MODELS)
383 model = sv.models[(int)touch->fields.server->modelindex];
384 pitchsign = SV_GetPitchSign(touch);
387 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);
389 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
390 Matrix4x4_Invert_Simple(&imatrix, &matrix);
391 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
392 Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
394 Collision_ClipLineToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
396 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
400 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
401 if(!VectorCompare(start, pEnd))
402 Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd);
412 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
413 #if COLLISIONPARANOID >= 1
414 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
416 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 #if COLLISIONPARANOID >= 1
420 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
422 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)
426 vec3_t hullmins, hullmaxs;
427 int i, bodysupercontents;
431 prvm_edict_t *traceowner, *touch;
433 // bounding box of entire move area
434 vec3_t clipboxmins, clipboxmaxs;
435 // size of the moving object
436 vec3_t clipmins, clipmaxs;
437 // size when clipping against monsters
438 vec3_t clipmins2, clipmaxs2;
439 // start and end origin of move
440 vec3_t clipstart, clipend;
443 // matrices to transform into/out of other entity's space
444 matrix4x4_t matrix, imatrix;
445 // model of other entity
447 // list of entities to test for collisions
449 prvm_edict_t *touchedicts[MAX_EDICTS];
450 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
454 if(!VectorCompare(start, pEnd))
456 // TRICK: make the trace 1 qu longer!
457 VectorSubtract(pEnd, start, end);
458 len = VectorNormalizeLength(end);
459 VectorAdd(pEnd, end, end);
462 VectorCopy(pEnd, end);
465 if (VectorCompare(mins, maxs))
467 vec3_t shiftstart, shiftend;
468 VectorAdd(start, mins, shiftstart);
469 VectorAdd(end, mins, shiftend);
470 if (VectorCompare(start, end))
471 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
473 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
474 VectorSubtract(trace.endpos, mins, trace.endpos);
478 VectorCopy(start, clipstart);
479 VectorCopy(end, clipend);
480 VectorCopy(mins, clipmins);
481 VectorCopy(maxs, clipmaxs);
482 VectorCopy(mins, clipmins2);
483 VectorCopy(maxs, clipmaxs2);
484 #if COLLISIONPARANOID >= 3
485 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
489 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
490 cliptrace.bmodelstartsolid = cliptrace.startsolid;
491 if (cliptrace.startsolid || cliptrace.fraction < 1)
492 cliptrace.ent = prog->edicts;
493 if (type == MOVE_WORLDONLY)
496 if (type == MOVE_MISSILE)
498 // LordHavoc: modified this, was = -15, now -= 15
499 for (i = 0;i < 3;i++)
506 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
507 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
508 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
511 VectorCopy(clipmins, hullmins);
512 VectorCopy(clipmaxs, hullmaxs);
515 // create the bounding box of the entire move
516 for (i = 0;i < 3;i++)
518 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
519 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
522 // debug override to test against everything
523 if (sv_debugmove.integer)
525 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
526 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
529 // if the passedict is world, make it NULL (to avoid two checks each time)
530 if (passedict == prog->edicts)
532 // precalculate prog value for passedict for comparisons
533 passedictprog = PRVM_EDICT_TO_PROG(passedict);
534 // figure out whether this is a point trace for comparisons
535 pointtrace = VectorCompare(clipmins, clipmaxs);
536 // precalculate passedict's owner edict pointer for comparisons
537 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
540 // because this uses World_EntitiestoBox, we know all entity boxes overlap
541 // the clip region, so we can skip culling checks in the loop below
542 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
543 if (numtouchedicts > MAX_EDICTS)
545 // this never happens
546 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
547 numtouchedicts = MAX_EDICTS;
549 for (i = 0;i < numtouchedicts;i++)
551 touch = touchedicts[i];
553 if (touch->fields.server->solid < SOLID_BBOX)
555 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
560 // don't clip against self
561 if (passedict == touch)
563 // don't clip owned entities against owner
564 if (traceowner == touch)
566 // don't clip owner against owned entities
567 if (passedictprog == touch->fields.server->owner)
569 // don't clip points against points (they can't collide)
570 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
574 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
576 // might interact, so do an exact clip
578 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
580 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
581 // if the modelindex is 0, it shouldn't be SOLID_BSP!
582 if (modelindex > 0 && modelindex < MAX_MODELS)
583 model = sv.models[(int)touch->fields.server->modelindex];
585 pitchsign = SV_GetPitchSign(touch);
588 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);
590 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
591 Matrix4x4_Invert_Simple(&imatrix, &matrix);
592 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
593 Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
595 Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
597 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
601 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
602 if(!VectorCompare(start, pEnd))
603 Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd);
608 #if COLLISIONPARANOID >= 1
609 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)
614 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
617 VectorCopy(trace.endpos, temp);
618 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
619 #if COLLISIONPARANOID < 3
620 if (trace.startsolid || endstuck)
622 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" : "");
628 int SV_PointSuperContents(const vec3_t point)
630 int supercontents = 0;
634 // matrices to transform into/out of other entity's space
635 matrix4x4_t matrix, imatrix;
636 // model of other entity
638 unsigned int modelindex;
640 // list of entities to test for collisions
642 prvm_edict_t *touchedicts[MAX_EDICTS];
644 // get world supercontents at this point
645 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
646 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
648 // if sv_gameplayfix_swiminbmodels is off we're done
649 if (!sv_gameplayfix_swiminbmodels.integer)
650 return supercontents;
652 // get list of entities at this point
653 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
654 if (numtouchedicts > MAX_EDICTS)
656 // this never happens
657 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
658 numtouchedicts = MAX_EDICTS;
660 for (i = 0;i < numtouchedicts;i++)
662 touch = touchedicts[i];
664 // we only care about SOLID_BSP for pointcontents
665 if (touch->fields.server->solid != SOLID_BSP)
668 // might interact, so do an exact clip
669 modelindex = (unsigned int)touch->fields.server->modelindex;
670 if (modelindex >= MAX_MODELS)
672 model = sv.models[(int)touch->fields.server->modelindex];
673 if (!model || !model->PointSuperContents)
675 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);
676 Matrix4x4_Invert_Simple(&imatrix, &matrix);
677 Matrix4x4_Transform(&imatrix, point, transformed);
678 frame = (int)touch->fields.server->frame;
679 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
682 return supercontents;
686 ===============================================================================
688 Linking entities into the world culling system
690 ===============================================================================
693 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
696 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
697 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
698 prog->globals.server->time = sv.time;
699 prog->globals.server->trace_allsolid = false;
700 prog->globals.server->trace_startsolid = false;
701 prog->globals.server->trace_fraction = 1;
702 prog->globals.server->trace_inwater = false;
703 prog->globals.server->trace_inopen = true;
704 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
705 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
706 prog->globals.server->trace_plane_dist = 0;
707 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
708 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
710 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
712 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
714 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
716 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
719 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
721 int i, numtouchedicts, old_self, old_other;
722 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
724 if (ent == prog->edicts)
725 return; // don't add the world
727 if (ent->priv.server->free)
730 if (ent->fields.server->solid == SOLID_NOT)
733 // build a list of edicts to touch, because the link loop can be corrupted
734 // by IncreaseEdicts called during touch functions
735 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
736 if (numtouchedicts > MAX_EDICTS)
738 // this never happens
739 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
740 numtouchedicts = MAX_EDICTS;
743 old_self = prog->globals.server->self;
744 old_other = prog->globals.server->other;
745 for (i = 0;i < numtouchedicts;i++)
747 touch = touchedicts[i];
748 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
750 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
753 prog->globals.server->self = old_self;
754 prog->globals.server->other = old_other;
757 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
761 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
763 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
764 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
765 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
766 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];
767 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];
768 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
769 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];
770 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];
771 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
772 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];
773 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];
774 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
775 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];
776 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];
777 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
778 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];
779 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];
780 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
781 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];
782 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];
783 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
784 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];
785 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 void SV_LinkEdict (prvm_edict_t *ent)
799 if (ent == prog->edicts)
800 return; // don't add the world
802 if (ent->priv.server->free)
807 if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
809 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
810 // TODO special handling for spheres?
811 RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs);
812 VectorAdd(ent->fields.server->origin, mins, mins);
813 VectorAdd(ent->fields.server->origin, maxs, maxs);
815 else if (ent->fields.server->solid == SOLID_BSP)
817 int modelindex = (int)ent->fields.server->modelindex;
818 if (modelindex < 0 || modelindex >= MAX_MODELS)
820 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
823 model = sv.models[modelindex];
826 if (!model->TraceBox && developer.integer >= 1)
827 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
829 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
831 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
832 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
834 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
836 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
837 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
841 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
842 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
847 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
848 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
849 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
854 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
855 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
859 // to make items easier to pick up and allow them to be grabbed off
860 // of shelves, the abs sizes are expanded
862 if ((int)ent->fields.server->flags & FL_ITEM)
873 // because movement is clipped an epsilon away from an actual edge,
874 // we must fully check even when bounding boxes don't quite touch
883 VectorCopy(mins, ent->fields.server->absmin);
884 VectorCopy(maxs, ent->fields.server->absmax);
886 World_LinkEdict(&sv.world, ent, mins, maxs);
890 ===============================================================================
894 ===============================================================================
899 SV_TestEntityPosition
901 returns true if the entity is in solid currently
904 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
909 contents = SV_GenericHitSuperContentsMask(ent);
910 VectorAdd(ent->fields.server->origin, offset, org);
911 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
912 if (trace.startsupercontents & contents)
916 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
918 // q1bsp/hlbsp use hulls and if the entity does not exactly match
919 // a hull size it is incorrectly tested, so this code tries to
920 // 'fix' it slightly...
921 // FIXME: this breaks entities larger than the hull size
924 VectorAdd(org, ent->fields.server->mins, m1);
925 VectorAdd(org, ent->fields.server->maxs, m2);
926 VectorSubtract(m2, m1, s);
927 #define EPSILON (1.0f / 32.0f)
928 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
929 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
930 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
931 for (i = 0;i < 8;i++)
933 v[0] = (i & 1) ? m2[0] : m1[0];
934 v[1] = (i & 2) ? m2[1] : m1[1];
935 v[2] = (i & 4) ? m2[2] : m1[2];
936 if (SV_PointSuperContents(v) & contents)
941 // if the trace found a better position for the entity, move it there
942 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
945 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
946 VectorCopy(trace.endpos, ent->fields.server->origin);
948 // verify if the endpos is REALLY outside solid
949 VectorCopy(trace.endpos, org);
950 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
952 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
954 VectorCopy(org, ent->fields.server->origin);
965 void SV_CheckAllEnts (void)
970 // see if any solid entities are inside the final position
971 check = PRVM_NEXT_EDICT(prog->edicts);
972 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
974 if (check->priv.server->free)
976 if (check->fields.server->movetype == MOVETYPE_PUSH
977 || check->fields.server->movetype == MOVETYPE_NONE
978 || check->fields.server->movetype == MOVETYPE_FOLLOW
979 || check->fields.server->movetype == MOVETYPE_NOCLIP)
982 if (SV_TestEntityPosition (check, vec3_origin))
983 Con_Print("entity in invalid position\n");
987 // DRESK - Support for Entity Contents Transition Event
990 SV_CheckContentsTransition
992 returns true if entity had a valid contentstransition function call
995 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
997 int bValidFunctionCall;
998 prvm_eval_t *contentstransition;
1000 // Default Valid Function Call to False
1001 bValidFunctionCall = false;
1003 if(ent->fields.server->watertype != nContents)
1004 { // Changed Contents
1005 // Acquire Contents Transition Function from QC
1006 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
1008 if(contentstransition->function)
1009 { // Valid Function; Execute
1010 // Assign Valid Function
1011 bValidFunctionCall = true;
1012 // Prepare Parameters (Original Contents, New Contents)
1013 // Original Contents
1014 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
1016 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1018 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1019 // Execute VM Function
1020 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
1024 // Return if Function Call was Valid
1025 return bValidFunctionCall;
1034 void SV_CheckVelocity (prvm_edict_t *ent)
1042 for (i=0 ; i<3 ; i++)
1044 if (IS_NAN(ent->fields.server->velocity[i]))
1046 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1047 ent->fields.server->velocity[i] = 0;
1049 if (IS_NAN(ent->fields.server->origin[i]))
1051 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1052 ent->fields.server->origin[i] = 0;
1056 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1057 // player_run/player_stand1 does not horribly malfunction if the
1058 // velocity becomes a denormalized float
1059 if (VectorLength2(ent->fields.server->velocity) < 0.0001)
1060 VectorClear(ent->fields.server->velocity);
1062 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1063 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1064 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1066 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1067 ent->fields.server->velocity[0] *= wishspeed;
1068 ent->fields.server->velocity[1] *= wishspeed;
1069 ent->fields.server->velocity[2] *= wishspeed;
1077 Runs thinking code if time. There is some play in the exact time the think
1078 function will be called, because it is called before any movement is done
1079 in a frame. Not used for pushmove objects, because they must be exact.
1080 Returns false if the entity removed itself.
1083 qboolean SV_RunThink (prvm_edict_t *ent)
1087 // don't let things stay in the past.
1088 // it is possible to start that way by a trigger with a local time.
1089 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1092 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1094 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1095 ent->fields.server->nextthink = 0;
1096 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1097 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1098 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1099 // mods often set nextthink to time to cause a think every frame,
1100 // we don't want to loop in that case, so exit if the new nextthink is
1101 // <= the time the qc was told, also exit if it is past the end of the
1103 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1106 return !ent->priv.server->free;
1113 Two entities have touched, so run their touch functions
1114 returns true if the impact kept the origin of the touching entity intact
1117 extern void VM_SetTraceGlobals(const trace_t *trace);
1118 extern sizebuf_t vm_tempstringsbuf;
1119 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1121 int restorevm_tempstringsbuf_cursize;
1122 int old_self, old_other;
1124 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1127 old_self = prog->globals.server->self;
1128 old_other = prog->globals.server->other;
1129 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1131 VectorCopy(e1->fields.server->origin, org);
1133 VM_SetTraceGlobals(trace);
1135 prog->globals.server->time = sv.time;
1136 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1138 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1139 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1140 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1143 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1145 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1146 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1147 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1148 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1149 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1150 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1151 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1153 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1155 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1157 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1159 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1162 prog->globals.server->self = old_self;
1163 prog->globals.server->other = old_other;
1164 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1166 return VectorCompare(e1->fields.server->origin, org);
1174 Slide off of the impacting object
1175 returns the blocked flags (1 = floor, 2 = step / wall)
1178 #define STOP_EPSILON 0.1
1179 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1184 backoff = -DotProduct (in, normal) * overbounce;
1185 VectorMA(in, backoff, normal, out);
1187 for (i = 0;i < 3;i++)
1188 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1197 The basic solid body movement clip that slides along multiple planes
1198 Returns the clipflags if the velocity was modified (hit something solid)
1202 8 = teleported by touch method
1203 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1206 static float SV_Gravity (prvm_edict_t *ent);
1207 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1208 #define MAX_CLIP_PLANES 5
1209 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
1211 int blocked, bumpcount;
1212 int i, j, numplanes;
1213 float d, time_left, gravity;
1214 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1224 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1226 gravity = SV_Gravity(ent) * 0.5f;
1227 ent->fields.server->velocity[2] -= gravity;
1231 applygravity = false;
1232 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1236 VectorCopy(ent->fields.server->velocity, original_velocity);
1237 VectorCopy(ent->fields.server->velocity, primal_velocity);
1240 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1242 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1245 VectorScale(ent->fields.server->velocity, time_left, push);
1247 VectorAdd(ent->fields.server->origin, push, end);
1249 if(!SV_PushEntity(&trace, ent, push, false, false))
1251 // we got teleported by a touch function
1252 // let's abort the move
1258 //if (trace.fraction < 0.002)
1263 VectorCopy(ent->fields.server->origin, start);
1264 start[2] += 3;//0.03125;
1265 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1266 end[2] += 3;//0.03125;
1267 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1268 if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
1270 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
1276 for (i = 0;i < numplanes;i++)
1278 VectorCopy(ent->fields.server->origin, start);
1279 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1280 VectorMA(start, 3, planes[i], start);
1281 VectorMA(end, 3, planes[i], end);
1282 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1283 if (trace.fraction < testtrace.fraction)
1286 VectorCopy(start, ent->fields.server->origin);
1291 // VectorAdd(ent->fields.server->origin, planes[j], start);
1297 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
1298 if (trace.fraction < 1)
1299 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
1304 if (trace.bmodelstartsolid)
1306 // LordHavoc: note: this code is what makes entities stick in place
1307 // if embedded in world only (you can walk through other objects if
1309 // entity is trapped in another solid
1310 VectorClear(ent->fields.server->velocity);
1315 if (trace.fraction == 1)
1317 if (trace.plane.normal[2])
1319 if (trace.plane.normal[2] > 0.7)
1326 Con_Printf ("SV_FlyMove: !trace.ent");
1327 trace.ent = prog->edicts;
1330 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1331 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1338 // save the trace for player extrafriction
1340 VectorCopy(trace.plane.normal, stepnormal);
1342 if (trace.fraction >= 0.001)
1344 // actually covered some distance
1345 VectorCopy(ent->fields.server->velocity, original_velocity);
1349 time_left *= 1 - trace.fraction;
1351 // clipped to another plane
1352 if (numplanes >= MAX_CLIP_PLANES)
1354 // this shouldn't really happen
1355 VectorClear(ent->fields.server->velocity);
1361 for (i = 0;i < numplanes;i++)
1362 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1366 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1371 VectorCopy(trace.plane.normal, planes[numplanes]);
1374 if (sv_newflymove.integer)
1375 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1378 // modify original_velocity so it parallels all of the clip planes
1379 for (i = 0;i < numplanes;i++)
1381 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1382 for (j = 0;j < numplanes;j++)
1387 if (DotProduct(new_velocity, planes[j]) < 0)
1397 // go along this plane
1398 VectorCopy(new_velocity, ent->fields.server->velocity);
1402 // go along the crease
1405 VectorClear(ent->fields.server->velocity);
1409 CrossProduct(planes[0], planes[1], dir);
1410 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1411 VectorNormalize(dir);
1412 d = DotProduct(dir, ent->fields.server->velocity);
1413 VectorScale(dir, d, ent->fields.server->velocity);
1417 // if current velocity is against the original velocity,
1418 // stop dead to avoid tiny occilations in sloping corners
1419 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1421 VectorClear(ent->fields.server->velocity);
1426 //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]);
1429 if ((blocked & 1) == 0 && bumpcount > 1)
1431 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1432 // flag ONGROUND if there's ground under it
1433 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1437 // LordHavoc: this came from QW and allows you to get out of water more easily
1438 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1439 VectorCopy(primal_velocity, ent->fields.server->velocity);
1440 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1441 ent->fields.server->velocity[2] -= gravity;
1451 static float SV_Gravity (prvm_edict_t *ent)
1456 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1457 if (val!=0 && val->_float)
1458 ent_gravity = val->_float;
1461 return ent_gravity * sv_gravity.value * sv.frametime;
1466 ===============================================================================
1470 ===============================================================================
1477 Does not change the entities velocity at all
1478 The trace struct is filled with the trace that has been done.
1479 Returns true if the push did not result in the entity being teleported by QC code.
1482 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1489 VectorCopy(ent->fields.server->origin, original);
1490 VectorAdd (ent->fields.server->origin, push, end);
1492 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1493 type = MOVE_MISSILE;
1494 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1495 type = MOVE_NOMONSTERS; // only clip against bmodels
1499 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1501 while (trace->startsolid && sv_gameplayfix_nudgeoutofsolid.integer)
1503 vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
1504 VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
1505 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1509 VectorCopy(original, ent->fields.server->origin);
1513 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1517 VectorCopy (trace->endpos, ent->fields.server->origin);
1521 if(!trace->startsolid)
1522 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)
1524 Con_Printf("something eeeeevil happened\n");
1529 SV_LinkEdict_TouchAreaGrid(ent);
1531 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))))
1532 return SV_Impact (ent, trace);
1544 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1547 int pusherowner, pusherprog;
1550 float savesolid, movetime2, pushltime;
1551 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1553 int numcheckentities;
1554 static prvm_edict_t *checkentities[MAX_EDICTS];
1555 dp_model_t *pushermodel;
1556 trace_t trace, trace2;
1557 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1558 unsigned short moved_edicts[MAX_EDICTS];
1560 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])
1562 pusher->fields.server->ltime += movetime;
1566 switch ((int) pusher->fields.server->solid)
1568 // LordHavoc: valid pusher types
1571 case SOLID_SLIDEBOX:
1572 case SOLID_CORPSE: // LordHavoc: this would be weird...
1574 // LordHavoc: no collisions
1577 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1578 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1579 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1580 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1581 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1582 pusher->fields.server->ltime += movetime;
1583 SV_LinkEdict(pusher);
1586 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1589 index = (int) pusher->fields.server->modelindex;
1590 if (index < 1 || index >= MAX_MODELS)
1592 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1595 pushermodel = sv.models[index];
1596 pusherowner = pusher->fields.server->owner;
1597 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1599 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1601 movetime2 = movetime;
1602 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1603 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1604 if (moveangle[0] || moveangle[2])
1606 for (i = 0;i < 3;i++)
1610 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1611 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1615 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1616 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1620 else if (moveangle[1])
1622 for (i = 0;i < 3;i++)
1626 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1627 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1631 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1632 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1638 for (i = 0;i < 3;i++)
1642 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1643 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1647 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1648 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1653 VectorNegate (moveangle, a);
1654 AngleVectorsFLU (a, forward, left, up);
1656 VectorCopy (pusher->fields.server->origin, pushorig);
1657 VectorCopy (pusher->fields.server->angles, pushang);
1658 pushltime = pusher->fields.server->ltime;
1660 // move the pusher to its final position
1662 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1663 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1664 pusher->fields.server->ltime += movetime;
1665 SV_LinkEdict(pusher);
1668 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1669 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1670 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);
1671 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1673 savesolid = pusher->fields.server->solid;
1675 // see if any solid entities are inside the final position
1678 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1679 for (e = 0;e < numcheckentities;e++)
1681 prvm_edict_t *check = checkentities[e];
1682 int movetype = (int)check->fields.server->movetype;
1687 case MOVETYPE_FOLLOW:
1688 case MOVETYPE_NOCLIP:
1689 case MOVETYPE_FAKEPUSH:
1695 if (check->fields.server->owner == pusherprog)
1698 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1701 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1703 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1704 check->priv.server->waterposition_forceupdate = true;
1706 checkcontents = SV_GenericHitSuperContentsMask(check);
1708 // if the entity is standing on the pusher, it will definitely be moved
1709 // if the entity is not standing on the pusher, but is in the pusher's
1710 // final position, move it
1711 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1713 Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, 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);
1714 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1715 if (!trace.startsolid)
1717 //Con_Printf("- not in solid\n");
1725 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1726 org2[0] = DotProduct (org, forward);
1727 org2[1] = DotProduct (org, left);
1728 org2[2] = DotProduct (org, up);
1729 VectorSubtract (org2, org, move);
1730 VectorAdd (move, move1, move);
1733 VectorCopy (move1, move);
1735 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1737 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1738 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1739 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1741 // physics objects need better collisions than this code can do
1742 if (movetype == MOVETYPE_PHYSICS)
1744 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1745 SV_LinkEdict(check);
1746 SV_LinkEdict_TouchAreaGrid(check);
1750 // try moving the contacted entity
1751 pusher->fields.server->solid = SOLID_NOT;
1752 if(!SV_PushEntity (&trace, check, move, true, true))
1754 // entity "check" got teleported
1755 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1756 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1757 continue; // pushed enough
1759 // FIXME: turn players specially
1760 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1761 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1762 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1764 // this trace.fraction < 1 check causes items to fall off of pushers
1765 // if they pass under or through a wall
1766 // the groundentity check causes items to fall off of ledges
1767 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1768 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1770 // if it is still inside the pusher, block
1771 Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1772 if (trace.startsolid)
1774 // try moving the contacted entity a tiny bit further to account for precision errors
1776 pusher->fields.server->solid = SOLID_NOT;
1777 VectorScale(move, 1.1, move2);
1778 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1779 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1780 if(!SV_PushEntity (&trace2, check, move2, true, true))
1782 // entity "check" got teleported
1785 pusher->fields.server->solid = savesolid;
1786 Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1787 if (trace.startsolid)
1789 // try moving the contacted entity a tiny bit less to account for precision errors
1790 pusher->fields.server->solid = SOLID_NOT;
1791 VectorScale(move, 0.9, move2);
1792 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1793 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1794 if(!SV_PushEntity (&trace2, check, move2, true, true))
1796 // entity "check" got teleported
1799 pusher->fields.server->solid = savesolid;
1800 Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, 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);
1801 if (trace.startsolid)
1803 // still inside pusher, so it's really blocked
1806 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1808 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1811 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1812 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1816 VectorCopy (pushorig, pusher->fields.server->origin);
1817 VectorCopy (pushang, pusher->fields.server->angles);
1818 pusher->fields.server->ltime = pushltime;
1819 SV_LinkEdict(pusher);
1821 // move back any entities we already moved
1822 for (i = 0;i < num_moved;i++)
1824 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1825 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1826 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1830 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1831 if (pusher->fields.server->blocked)
1833 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1834 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1835 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1842 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1843 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1844 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1853 void SV_Physics_Pusher (prvm_edict_t *ent)
1855 float thinktime, oldltime, movetime;
1857 oldltime = ent->fields.server->ltime;
1859 thinktime = ent->fields.server->nextthink;
1860 if (thinktime < ent->fields.server->ltime + sv.frametime)
1862 movetime = thinktime - ent->fields.server->ltime;
1867 movetime = sv.frametime;
1870 // advances ent->fields.server->ltime if not blocked
1871 SV_PushMove (ent, movetime);
1873 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1875 ent->fields.server->nextthink = 0;
1876 prog->globals.server->time = sv.time;
1877 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1878 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1879 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1885 ===============================================================================
1889 ===============================================================================
1892 static float unstickoffsets[] =
1894 // poutting -/+z changes first as they are least weird
1909 typedef enum unstickresult_e
1917 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1921 // if not stuck in a bmodel, just return
1922 if (!SV_TestEntityPosition(ent, vec3_origin))
1923 return UNSTICK_GOOD;
1925 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1927 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1929 VectorCopy(unstickoffsets + i, offset);
1931 //SV_LinkEdict_TouchAreaGrid(ent);
1932 return UNSTICK_UNSTUCK;
1936 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1937 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1939 for(i = 2; i <= maxunstick; ++i)
1941 VectorClear(offset);
1943 if (!SV_TestEntityPosition(ent, offset))
1946 //SV_LinkEdict_TouchAreaGrid(ent);
1947 return UNSTICK_UNSTUCK;
1950 if (!SV_TestEntityPosition(ent, offset))
1953 //SV_LinkEdict_TouchAreaGrid(ent);
1954 return UNSTICK_UNSTUCK;
1958 return UNSTICK_STUCK;
1961 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1964 switch(SV_UnstickEntityReturnOffset(ent, offset))
1968 case UNSTICK_UNSTUCK:
1969 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]);
1972 if (developer.integer >= 100)
1973 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1976 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1985 This is a big hack to try and fix the rare case of getting stuck in the world
1989 void SV_CheckStuck (prvm_edict_t *ent)
1993 switch(SV_UnstickEntityReturnOffset(ent, offset))
1996 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1998 case UNSTICK_UNSTUCK:
1999 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]);
2002 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
2003 if (!SV_TestEntityPosition(ent, offset))
2005 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2007 //SV_LinkEdict_TouchAreaGrid(ent);
2010 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2013 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2023 qboolean SV_CheckWater (prvm_edict_t *ent)
2026 int nNativeContents;
2029 point[0] = ent->fields.server->origin[0];
2030 point[1] = ent->fields.server->origin[1];
2031 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2033 // DRESK - Support for Entity Contents Transition Event
2034 // NOTE: Some logic needed to be slightly re-ordered
2035 // to not affect performance and allow for the feature.
2037 // Acquire Super Contents Prior to Resets
2038 cont = SV_PointSuperContents(point);
2039 // Acquire Native Contents Here
2040 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2042 // DRESK - Support for Entity Contents Transition Event
2043 if(ent->fields.server->watertype)
2044 // Entity did NOT Spawn; Check
2045 SV_CheckContentsTransition(ent, nNativeContents);
2048 ent->fields.server->waterlevel = 0;
2049 ent->fields.server->watertype = CONTENTS_EMPTY;
2050 cont = SV_PointSuperContents(point);
2051 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2053 ent->fields.server->watertype = nNativeContents;
2054 ent->fields.server->waterlevel = 1;
2055 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2056 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2058 ent->fields.server->waterlevel = 2;
2059 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2060 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2061 ent->fields.server->waterlevel = 3;
2065 return ent->fields.server->waterlevel > 1;
2074 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2077 vec3_t forward, into, side;
2079 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2080 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2082 // cut the tangential velocity
2083 i = DotProduct (stepnormal, ent->fields.server->velocity);
2084 VectorScale (stepnormal, i, into);
2085 VectorSubtract (ent->fields.server->velocity, into, side);
2086 ent->fields.server->velocity[0] = side[0] * (1 + d);
2087 ent->fields.server->velocity[1] = side[1] * (1 + d);
2093 =====================
2096 Player has come to a dead stop, possibly due to the problem with limited
2097 float precision at some angle joins in the BSP hull.
2099 Try fixing by pushing one pixel in each direction.
2101 This is a hack, but in the interest of good gameplay...
2102 ======================
2104 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2109 VectorCopy (ent->fields.server->origin, oldorg);
2112 for (i=0 ; i<8 ; i++)
2114 // try pushing a little in an axial direction
2117 case 0: dir[0] = 2; dir[1] = 0; break;
2118 case 1: dir[0] = 0; dir[1] = 2; break;
2119 case 2: dir[0] = -2; dir[1] = 0; break;
2120 case 3: dir[0] = 0; dir[1] = -2; break;
2121 case 4: dir[0] = 2; dir[1] = 2; break;
2122 case 5: dir[0] = -2; dir[1] = 2; break;
2123 case 6: dir[0] = 2; dir[1] = -2; break;
2124 case 7: dir[0] = -2; dir[1] = -2; break;
2127 SV_PushEntity (&trace, ent, dir, false, true);
2129 // retry the original move
2130 ent->fields.server->velocity[0] = oldvel[0];
2131 ent->fields.server->velocity[1] = oldvel[1];
2132 ent->fields.server->velocity[2] = 0;
2133 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2135 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2136 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2138 Con_DPrint("TryUnstick - success.\n");
2142 // go back to the original pos and try again
2143 VectorCopy (oldorg, ent->fields.server->origin);
2147 VectorClear (ent->fields.server->velocity);
2148 Con_DPrint("TryUnstick - failure.\n");
2154 =====================
2157 Only used by players
2158 ======================
2160 void SV_WalkMove (prvm_edict_t *ent)
2162 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2163 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2164 trace_t downtrace, trace;
2165 qboolean applygravity;
2167 // if frametime is 0 (due to client sending the same timestamp twice),
2169 if (sv.frametime <= 0)
2172 SV_CheckStuck (ent);
2174 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2176 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2178 SV_CheckVelocity(ent);
2180 // do a regular slide move unless it looks like you ran into a step
2181 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2183 VectorCopy (ent->fields.server->origin, start_origin);
2184 VectorCopy (ent->fields.server->velocity, start_velocity);
2186 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2188 // if the move did not hit the ground at any point, we're not on ground
2190 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2192 SV_CheckVelocity(ent);
2194 SV_LinkEdict_TouchAreaGrid(ent);
2196 if(clip & 8) // teleport
2199 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2202 if (sv_nostep.integer)
2205 VectorCopy(ent->fields.server->origin, originalmove_origin);
2206 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2207 originalmove_clip = clip;
2208 originalmove_flags = (int)ent->fields.server->flags;
2209 originalmove_groundentity = ent->fields.server->groundentity;
2211 // if move didn't block on a step, return
2214 // if move was not trying to move into the step, return
2215 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2218 if (ent->fields.server->movetype != MOVETYPE_FLY)
2220 // return if gibbed by a trigger
2221 if (ent->fields.server->movetype != MOVETYPE_WALK)
2224 // only step up while jumping if that is enabled
2225 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2226 if (!oldonground && ent->fields.server->waterlevel == 0)
2230 // try moving up and forward to go up a step
2231 // back to start pos
2232 VectorCopy (start_origin, ent->fields.server->origin);
2233 VectorCopy (start_velocity, ent->fields.server->velocity);
2236 VectorClear (upmove);
2237 upmove[2] = sv_stepheight.value;
2238 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2240 // we got teleported when upstepping... must abort the move
2245 ent->fields.server->velocity[2] = 0;
2246 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2247 ent->fields.server->velocity[2] += start_velocity[2];
2250 // we got teleported when upstepping... must abort the move
2251 // note that z velocity handling may not be what QC expects here, but we cannot help it
2255 SV_CheckVelocity(ent);
2257 SV_LinkEdict_TouchAreaGrid(ent);
2259 // check for stuckness, possibly due to the limited precision of floats
2260 // in the clipping hulls
2262 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2263 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2265 //Con_Printf("wall\n");
2266 // stepping up didn't make any progress, revert to original move
2267 VectorCopy(originalmove_origin, ent->fields.server->origin);
2268 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2269 //clip = originalmove_clip;
2270 ent->fields.server->flags = originalmove_flags;
2271 ent->fields.server->groundentity = originalmove_groundentity;
2272 // now try to unstick if needed
2273 //clip = SV_TryUnstick (ent, oldvel);
2277 //Con_Printf("step - ");
2279 // extra friction based on view angle
2280 if (clip & 2 && sv_wallfriction.integer)
2281 SV_WallFriction (ent, stepnormal);
2283 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2284 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))
2288 VectorClear (downmove);
2289 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2290 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2292 // we got teleported when downstepping... must abort the move
2296 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2298 // this has been disabled so that you can't jump when you are stepping
2299 // up while already jumping (also known as the Quake2 double jump bug)
2301 // LordHavoc: disabled this check so you can walk on monsters/players
2302 //if (ent->fields.server->solid == SOLID_BSP)
2304 //Con_Printf("onground\n");
2305 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2306 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2312 //Con_Printf("slope\n");
2313 // if the push down didn't end up on good ground, use the move without
2314 // the step up. This happens near wall / slope combinations, and can
2315 // cause the player to hop up higher on a slope too steep to climb
2316 VectorCopy(originalmove_origin, ent->fields.server->origin);
2317 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2318 //clip = originalmove_clip;
2319 ent->fields.server->flags = originalmove_flags;
2320 ent->fields.server->groundentity = originalmove_groundentity;
2323 SV_CheckVelocity(ent);
2325 SV_LinkEdict_TouchAreaGrid(ent);
2328 //============================================================================
2334 Entities that are "stuck" to another entity
2337 void SV_Physics_Follow (prvm_edict_t *ent)
2339 vec3_t vf, vr, vu, angles, v;
2343 if (!SV_RunThink (ent))
2346 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2347 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2348 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])
2350 // quick case for no rotation
2351 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2355 angles[0] = -ent->fields.server->punchangle[0];
2356 angles[1] = ent->fields.server->punchangle[1];
2357 angles[2] = ent->fields.server->punchangle[2];
2358 AngleVectors (angles, vf, vr, vu);
2359 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];
2360 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];
2361 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];
2362 angles[0] = -e->fields.server->angles[0];
2363 angles[1] = e->fields.server->angles[1];
2364 angles[2] = e->fields.server->angles[2];
2365 AngleVectors (angles, vf, vr, vu);
2366 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2367 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2368 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2370 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2372 //SV_LinkEdict_TouchAreaGrid(ent);
2376 ==============================================================================
2380 ==============================================================================
2385 SV_CheckWaterTransition
2389 void SV_CheckWaterTransition (prvm_edict_t *ent)
2392 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2393 if (!ent->fields.server->watertype)
2395 // just spawned here
2396 ent->fields.server->watertype = cont;
2397 ent->fields.server->waterlevel = 1;
2401 // DRESK - Support for Entity Contents Transition Event
2402 // NOTE: Call here BEFORE updating the watertype below,
2403 // and suppress watersplash sound if a valid function
2404 // call was made to allow for custom "splash" sounds.
2405 if( !SV_CheckContentsTransition(ent, cont) )
2406 { // Contents Transition Function Invalid; Potentially Play Water Sound
2407 // check if the entity crossed into or out of water
2408 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2409 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2412 if (cont <= CONTENTS_WATER)
2414 ent->fields.server->watertype = cont;
2415 ent->fields.server->waterlevel = 1;
2419 ent->fields.server->watertype = CONTENTS_EMPTY;
2420 ent->fields.server->waterlevel = 0;
2428 Toss, bounce, and fly movement. When onground, do nothing.
2431 void SV_Physics_Toss (prvm_edict_t *ent)
2438 // if onground, return without moving
2439 if ((int)ent->fields.server->flags & FL_ONGROUND)
2441 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2443 // don't stick to ground if onground and moving upward
2444 ent->fields.server->flags -= FL_ONGROUND;
2446 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2448 // we can trust FL_ONGROUND if groundentity is world because it never moves
2451 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2453 // if ent was supported by a brush model on previous frame,
2454 // and groundentity is now freed, set groundentity to 0 (world)
2455 // which leaves it suspended in the air
2456 ent->fields.server->groundentity = 0;
2457 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2461 ent->priv.server->suspendedinairflag = false;
2463 SV_CheckVelocity (ent);
2466 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2467 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2470 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2472 movetime = sv.frametime;
2473 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2476 VectorScale (ent->fields.server->velocity, movetime, move);
2477 if(!SV_PushEntity (&trace, ent, move, true, true))
2478 return; // teleported
2479 if (ent->priv.server->free)
2481 if (trace.bmodelstartsolid)
2483 // try to unstick the entity
2484 SV_UnstickEntity(ent);
2485 if(!SV_PushEntity (&trace, ent, move, false, true))
2486 return; // teleported
2487 if (ent->priv.server->free)
2490 if (trace.fraction == 1)
2492 movetime *= 1 - min(1, trace.fraction);
2493 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2496 float bouncefactor = 1.0f;
2497 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2498 if (val!=0 && val->_float)
2499 bouncefactor = val->_float;
2501 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2502 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2504 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2506 float d, ent_gravity;
2508 float bouncefactor = 0.5f;
2509 float bouncestop = 60.0f / 800.0f;
2511 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2512 if (val!=0 && val->_float)
2513 bouncefactor = val->_float;
2515 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2516 if (val!=0 && val->_float)
2517 bouncestop = val->_float;
2519 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2520 // LordHavoc: fixed grenades not bouncing when fired down a slope
2521 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2522 if (val!=0 && val->_float)
2523 ent_gravity = val->_float;
2526 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2528 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2529 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2531 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2532 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2533 VectorClear (ent->fields.server->velocity);
2534 VectorClear (ent->fields.server->avelocity);
2537 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2541 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2543 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2544 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2545 VectorClear (ent->fields.server->velocity);
2546 VectorClear (ent->fields.server->avelocity);
2549 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2554 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2555 if (trace.plane.normal[2] > 0.7)
2557 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2558 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2559 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2560 ent->priv.server->suspendedinairflag = true;
2561 VectorClear (ent->fields.server->velocity);
2562 VectorClear (ent->fields.server->avelocity);
2565 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2567 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2571 // check for in water
2572 SV_CheckWaterTransition (ent);
2576 ===============================================================================
2580 ===============================================================================
2587 Monsters freefall when they don't have a ground entity, otherwise
2588 all movement is done with discrete steps.
2590 This is also used for objects that have become still on the ground, but
2591 will fall if the floor is pulled out from under them.
2594 void SV_Physics_Step (prvm_edict_t *ent)
2596 int flags = (int)ent->fields.server->flags;
2599 // Backup Velocity in the event that movetypesteplandevent is called,
2600 // to provide a parameter with the entity's velocity at impact.
2601 prvm_eval_t *movetypesteplandevent;
2602 vec3_t backupVelocity;
2603 VectorCopy(ent->fields.server->velocity, backupVelocity);
2604 // don't fall at all if fly/swim
2605 if (!(flags & (FL_FLY | FL_SWIM)))
2607 if (flags & FL_ONGROUND)
2609 // freefall if onground and moving upward
2610 // freefall if not standing on a world surface (it may be a lift or trap door)
2611 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2613 ent->fields.server->flags -= FL_ONGROUND;
2614 SV_CheckVelocity(ent);
2615 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2617 SV_LinkEdict_TouchAreaGrid(ent);
2618 ent->priv.server->waterposition_forceupdate = true;
2623 // freefall if not onground
2624 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2626 SV_CheckVelocity(ent);
2627 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2629 SV_LinkEdict_TouchAreaGrid(ent);
2632 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2634 // DRESK - Check for Entity Land Event Function
2635 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2637 if(movetypesteplandevent->function)
2638 { // Valid Function; Execute
2639 // Prepare Parameters
2640 // Assign Velocity at Impact
2641 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2642 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2643 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2645 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2646 // Execute VM Function
2647 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2650 // Check for Engine Landing Sound
2651 if(sv_sound_land.string)
2652 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2654 ent->priv.server->waterposition_forceupdate = true;
2659 if (!SV_RunThink(ent))
2662 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2664 ent->priv.server->waterposition_forceupdate = false;
2665 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2666 SV_CheckWaterTransition(ent);
2670 //============================================================================
2672 static void SV_Physics_Entity (prvm_edict_t *ent)
2674 // don't run think/move on newly spawned projectiles as it messes up
2675 // movement interpolation and rocket trails, and is inconsistent with
2676 // respect to entities spawned in the same frame
2677 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2678 // but if it spawns a lower numbered ent, it doesn't - this never moves
2679 // ents in the first frame regardless)
2680 qboolean runmove = ent->priv.server->move;
2681 ent->priv.server->move = true;
2682 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2684 switch ((int) ent->fields.server->movetype)
2687 case MOVETYPE_FAKEPUSH:
2688 SV_Physics_Pusher (ent);
2691 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2692 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2695 case MOVETYPE_FOLLOW:
2696 SV_Physics_Follow (ent);
2698 case MOVETYPE_NOCLIP:
2699 if (SV_RunThink(ent))
2702 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2703 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2708 SV_Physics_Step (ent);
2711 if (SV_RunThink (ent))
2715 case MOVETYPE_BOUNCE:
2716 case MOVETYPE_BOUNCEMISSILE:
2717 case MOVETYPE_FLYMISSILE:
2720 if (SV_RunThink (ent))
2721 SV_Physics_Toss (ent);
2723 case MOVETYPE_PHYSICS:
2724 if (SV_RunThink(ent))
2727 SV_LinkEdict_TouchAreaGrid(ent);
2731 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2736 void SV_Physics_ClientMove(void)
2739 ent = host_client->edict;
2741 // call player physics, this needs the proper frametime
2742 prog->globals.server->frametime = sv.frametime;
2745 // call standard client pre-think, with frametime = 0
2746 prog->globals.server->time = sv.time;
2747 prog->globals.server->frametime = 0;
2748 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2749 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2750 prog->globals.server->frametime = sv.frametime;
2752 // make sure the velocity is sane (not a NaN)
2753 SV_CheckVelocity(ent);
2755 // perform MOVETYPE_WALK behavior
2758 // call standard player post-think, with frametime = 0
2759 prog->globals.server->time = sv.time;
2760 prog->globals.server->frametime = 0;
2761 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2762 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2763 prog->globals.server->frametime = sv.frametime;
2765 if(ent->fields.server->fixangle)
2767 // angle fixing was requested by physics code...
2768 // so store the current angles for later use
2769 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2770 host_client->fixangle_angles_set = TRUE;
2772 // and clear fixangle for the next frame
2773 ent->fields.server->fixangle = 0;
2777 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2779 // don't do physics on disconnected clients, FrikBot relies on this
2780 if (!host_client->spawned)
2783 // make sure the velocity is sane (not a NaN)
2784 SV_CheckVelocity(ent);
2786 // don't run physics here if running asynchronously
2787 if (host_client->clmovement_inputtimeout <= 0)
2790 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2793 // make sure the velocity is still sane (not a NaN)
2794 SV_CheckVelocity(ent);
2796 // call standard client pre-think
2797 prog->globals.server->time = sv.time;
2798 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2799 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2801 // make sure the velocity is still sane (not a NaN)
2802 SV_CheckVelocity(ent);
2805 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2807 // don't do physics on disconnected clients, FrikBot relies on this
2808 if (!host_client->spawned)
2811 // make sure the velocity is sane (not a NaN)
2812 SV_CheckVelocity(ent);
2814 // call standard player post-think
2815 prog->globals.server->time = sv.time;
2816 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2817 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2819 // make sure the velocity is still sane (not a NaN)
2820 SV_CheckVelocity(ent);
2822 if(ent->fields.server->fixangle)
2824 // angle fixing was requested by physics code...
2825 // so store the current angles for later use
2826 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2827 host_client->fixangle_angles_set = TRUE;
2829 // and clear fixangle for the next frame
2830 ent->fields.server->fixangle = 0;
2833 // decrement the countdown variable used to decide when to go back to
2834 // synchronous physics
2835 if (host_client->clmovement_inputtimeout > sv.frametime)
2836 host_client->clmovement_inputtimeout -= sv.frametime;
2838 host_client->clmovement_inputtimeout = 0;
2841 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2843 // don't do physics on disconnected clients, FrikBot relies on this
2844 if (!host_client->spawned)
2846 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2850 // make sure the velocity is sane (not a NaN)
2851 SV_CheckVelocity(ent);
2853 switch ((int) ent->fields.server->movetype)
2856 case MOVETYPE_FAKEPUSH:
2857 SV_Physics_Pusher (ent);
2860 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2861 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2864 case MOVETYPE_FOLLOW:
2865 SV_Physics_Follow (ent);
2867 case MOVETYPE_NOCLIP:
2870 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2871 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2874 SV_Physics_Step (ent);
2878 // don't run physics here if running asynchronously
2879 if (host_client->clmovement_inputtimeout <= 0)
2883 case MOVETYPE_BOUNCE:
2884 case MOVETYPE_BOUNCEMISSILE:
2885 case MOVETYPE_FLYMISSILE:
2888 SV_Physics_Toss (ent);
2894 case MOVETYPE_PHYSICS:
2898 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2902 SV_CheckVelocity (ent);
2905 SV_LinkEdict_TouchAreaGrid(ent);
2907 SV_CheckVelocity (ent);
2916 void SV_Physics (void)
2921 // let the progs know that a new frame has started
2922 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2923 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2924 prog->globals.server->time = sv.time;
2925 prog->globals.server->frametime = sv.frametime;
2926 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2928 // run physics engine
2929 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2932 // treat each object in turn
2935 // if force_retouch, relink all the entities
2936 if (prog->globals.server->force_retouch > 0)
2937 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2938 if (!ent->priv.server->free)
2939 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2941 if (sv_gameplayfix_consistentplayerprethink.integer)
2943 // run physics on the client entities in 3 stages
2944 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2945 if (!ent->priv.server->free)
2946 SV_Physics_ClientEntity_PreThink(ent);
2948 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2949 if (!ent->priv.server->free)
2950 SV_Physics_ClientEntity(ent);
2952 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2953 if (!ent->priv.server->free)
2954 SV_Physics_ClientEntity_PostThink(ent);
2958 // run physics on the client entities
2959 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2961 if (!ent->priv.server->free)
2963 SV_Physics_ClientEntity_PreThink(ent);
2964 SV_Physics_ClientEntity(ent);
2965 SV_Physics_ClientEntity_PostThink(ent);
2970 // run physics on all the non-client entities
2971 if (!sv_freezenonclients.integer)
2973 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2974 if (!ent->priv.server->free)
2975 SV_Physics_Entity(ent);
2976 // make a second pass to see if any ents spawned this frame and make
2977 // sure they run their move/think
2978 if (sv_gameplayfix_delayprojectiles.integer < 0)
2979 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2980 if (!ent->priv.server->move && !ent->priv.server->free)
2981 SV_Physics_Entity(ent);
2984 if (prog->globals.server->force_retouch > 0)
2985 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2987 // LordHavoc: endframe support
2988 if (prog->funcoffsets.EndFrame)
2990 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2991 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2992 prog->globals.server->time = sv.time;
2993 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2996 // decrement prog->num_edicts if the highest number entities died
2997 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2999 if (!sv_freezenonclients.integer)
3000 sv.time += sv.frametime;