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 VectorMA(ent->fields.server->origin, -trace->startdepth, trace->startdepthnormal, ent->fields.server->origin);
1504 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1508 VectorCopy(original, ent->fields.server->origin);
1512 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1516 VectorCopy (trace->endpos, ent->fields.server->origin);
1520 if(!trace->startsolid)
1521 if(SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, type, ent, SV_GenericHitSuperContentsMask(ent)).startsolid)
1523 Con_Printf("something eeeeevil happened\n");
1528 SV_LinkEdict_TouchAreaGrid(ent);
1530 if((ent->fields.server->solid >= SOLID_TRIGGER && trace->ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace->ent))))
1531 return SV_Impact (ent, trace);
1543 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1546 int pusherowner, pusherprog;
1549 float savesolid, movetime2, pushltime;
1550 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1552 int numcheckentities;
1553 static prvm_edict_t *checkentities[MAX_EDICTS];
1554 dp_model_t *pushermodel;
1555 trace_t trace, trace2;
1556 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1557 unsigned short moved_edicts[MAX_EDICTS];
1559 if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
1561 pusher->fields.server->ltime += movetime;
1565 switch ((int) pusher->fields.server->solid)
1567 // LordHavoc: valid pusher types
1570 case SOLID_SLIDEBOX:
1571 case SOLID_CORPSE: // LordHavoc: this would be weird...
1573 // LordHavoc: no collisions
1576 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1577 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1578 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1579 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1580 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1581 pusher->fields.server->ltime += movetime;
1582 SV_LinkEdict(pusher);
1585 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1588 index = (int) pusher->fields.server->modelindex;
1589 if (index < 1 || index >= MAX_MODELS)
1591 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1594 pushermodel = sv.models[index];
1595 pusherowner = pusher->fields.server->owner;
1596 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1598 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1600 movetime2 = movetime;
1601 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1602 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1603 if (moveangle[0] || moveangle[2])
1605 for (i = 0;i < 3;i++)
1609 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1610 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1614 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1615 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1619 else if (moveangle[1])
1621 for (i = 0;i < 3;i++)
1625 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1626 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1630 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1631 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1637 for (i = 0;i < 3;i++)
1641 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1642 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1646 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1647 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1652 VectorNegate (moveangle, a);
1653 AngleVectorsFLU (a, forward, left, up);
1655 VectorCopy (pusher->fields.server->origin, pushorig);
1656 VectorCopy (pusher->fields.server->angles, pushang);
1657 pushltime = pusher->fields.server->ltime;
1659 // move the pusher to its final position
1661 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1662 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1663 pusher->fields.server->ltime += movetime;
1664 SV_LinkEdict(pusher);
1667 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1668 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1669 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);
1670 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1672 savesolid = pusher->fields.server->solid;
1674 // see if any solid entities are inside the final position
1677 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1678 for (e = 0;e < numcheckentities;e++)
1680 prvm_edict_t *check = checkentities[e];
1681 int movetype = (int)check->fields.server->movetype;
1686 case MOVETYPE_FOLLOW:
1687 case MOVETYPE_NOCLIP:
1688 case MOVETYPE_FAKEPUSH:
1694 if (check->fields.server->owner == pusherprog)
1697 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1700 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1702 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1703 check->priv.server->waterposition_forceupdate = true;
1705 checkcontents = SV_GenericHitSuperContentsMask(check);
1707 // if the entity is standing on the pusher, it will definitely be moved
1708 // if the entity is not standing on the pusher, but is in the pusher's
1709 // final position, move it
1710 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1712 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);
1713 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1714 if (!trace.startsolid)
1716 //Con_Printf("- not in solid\n");
1724 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1725 org2[0] = DotProduct (org, forward);
1726 org2[1] = DotProduct (org, left);
1727 org2[2] = DotProduct (org, up);
1728 VectorSubtract (org2, org, move);
1729 VectorAdd (move, move1, move);
1732 VectorCopy (move1, move);
1734 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1736 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1737 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1738 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1740 // physics objects need better collisions than this code can do
1741 if (movetype == MOVETYPE_PHYSICS)
1743 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1744 SV_LinkEdict(check);
1745 SV_LinkEdict_TouchAreaGrid(check);
1749 // try moving the contacted entity
1750 pusher->fields.server->solid = SOLID_NOT;
1751 if(!SV_PushEntity (&trace, check, move, true, true))
1753 // entity "check" got teleported
1754 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1755 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1756 continue; // pushed enough
1758 // FIXME: turn players specially
1759 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1760 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1761 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1763 // this trace.fraction < 1 check causes items to fall off of pushers
1764 // if they pass under or through a wall
1765 // the groundentity check causes items to fall off of ledges
1766 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1767 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1769 // if it is still inside the pusher, block
1770 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);
1771 if (trace.startsolid)
1773 // try moving the contacted entity a tiny bit further to account for precision errors
1775 pusher->fields.server->solid = SOLID_NOT;
1776 VectorScale(move, 1.1, move2);
1777 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1778 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1779 if(!SV_PushEntity (&trace2, check, move2, true, true))
1781 // entity "check" got teleported
1784 pusher->fields.server->solid = savesolid;
1785 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);
1786 if (trace.startsolid)
1788 // try moving the contacted entity a tiny bit less to account for precision errors
1789 pusher->fields.server->solid = SOLID_NOT;
1790 VectorScale(move, 0.9, move2);
1791 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1792 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1793 if(!SV_PushEntity (&trace2, check, move2, true, true))
1795 // entity "check" got teleported
1798 pusher->fields.server->solid = savesolid;
1799 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);
1800 if (trace.startsolid)
1802 // still inside pusher, so it's really blocked
1805 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1807 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1810 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1811 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1815 VectorCopy (pushorig, pusher->fields.server->origin);
1816 VectorCopy (pushang, pusher->fields.server->angles);
1817 pusher->fields.server->ltime = pushltime;
1818 SV_LinkEdict(pusher);
1820 // move back any entities we already moved
1821 for (i = 0;i < num_moved;i++)
1823 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1824 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1825 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1829 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1830 if (pusher->fields.server->blocked)
1832 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1833 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1834 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1841 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1842 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1843 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1852 void SV_Physics_Pusher (prvm_edict_t *ent)
1854 float thinktime, oldltime, movetime;
1856 oldltime = ent->fields.server->ltime;
1858 thinktime = ent->fields.server->nextthink;
1859 if (thinktime < ent->fields.server->ltime + sv.frametime)
1861 movetime = thinktime - ent->fields.server->ltime;
1866 movetime = sv.frametime;
1869 // advances ent->fields.server->ltime if not blocked
1870 SV_PushMove (ent, movetime);
1872 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1874 ent->fields.server->nextthink = 0;
1875 prog->globals.server->time = sv.time;
1876 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1877 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1878 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1884 ===============================================================================
1888 ===============================================================================
1891 static float unstickoffsets[] =
1893 // poutting -/+z changes first as they are least weird
1908 typedef enum unstickresult_e
1916 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1920 // if not stuck in a bmodel, just return
1921 if (!SV_TestEntityPosition(ent, vec3_origin))
1922 return UNSTICK_GOOD;
1924 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1926 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1928 VectorCopy(unstickoffsets + i, offset);
1930 //SV_LinkEdict_TouchAreaGrid(ent);
1931 return UNSTICK_UNSTUCK;
1935 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1936 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1938 for(i = 2; i <= maxunstick; ++i)
1940 VectorClear(offset);
1942 if (!SV_TestEntityPosition(ent, offset))
1945 //SV_LinkEdict_TouchAreaGrid(ent);
1946 return UNSTICK_UNSTUCK;
1949 if (!SV_TestEntityPosition(ent, offset))
1952 //SV_LinkEdict_TouchAreaGrid(ent);
1953 return UNSTICK_UNSTUCK;
1957 return UNSTICK_STUCK;
1960 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1963 switch(SV_UnstickEntityReturnOffset(ent, offset))
1967 case UNSTICK_UNSTUCK:
1968 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]);
1971 if (developer.integer >= 100)
1972 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1975 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1984 This is a big hack to try and fix the rare case of getting stuck in the world
1988 void SV_CheckStuck (prvm_edict_t *ent)
1992 switch(SV_UnstickEntityReturnOffset(ent, offset))
1995 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1997 case UNSTICK_UNSTUCK:
1998 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]);
2001 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
2002 if (!SV_TestEntityPosition(ent, offset))
2004 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2006 //SV_LinkEdict_TouchAreaGrid(ent);
2009 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2012 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2022 qboolean SV_CheckWater (prvm_edict_t *ent)
2025 int nNativeContents;
2028 point[0] = ent->fields.server->origin[0];
2029 point[1] = ent->fields.server->origin[1];
2030 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2032 // DRESK - Support for Entity Contents Transition Event
2033 // NOTE: Some logic needed to be slightly re-ordered
2034 // to not affect performance and allow for the feature.
2036 // Acquire Super Contents Prior to Resets
2037 cont = SV_PointSuperContents(point);
2038 // Acquire Native Contents Here
2039 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2041 // DRESK - Support for Entity Contents Transition Event
2042 if(ent->fields.server->watertype)
2043 // Entity did NOT Spawn; Check
2044 SV_CheckContentsTransition(ent, nNativeContents);
2047 ent->fields.server->waterlevel = 0;
2048 ent->fields.server->watertype = CONTENTS_EMPTY;
2049 cont = SV_PointSuperContents(point);
2050 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2052 ent->fields.server->watertype = nNativeContents;
2053 ent->fields.server->waterlevel = 1;
2054 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2055 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2057 ent->fields.server->waterlevel = 2;
2058 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2059 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2060 ent->fields.server->waterlevel = 3;
2064 return ent->fields.server->waterlevel > 1;
2073 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2076 vec3_t forward, into, side;
2078 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2079 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2081 // cut the tangential velocity
2082 i = DotProduct (stepnormal, ent->fields.server->velocity);
2083 VectorScale (stepnormal, i, into);
2084 VectorSubtract (ent->fields.server->velocity, into, side);
2085 ent->fields.server->velocity[0] = side[0] * (1 + d);
2086 ent->fields.server->velocity[1] = side[1] * (1 + d);
2092 =====================
2095 Player has come to a dead stop, possibly due to the problem with limited
2096 float precision at some angle joins in the BSP hull.
2098 Try fixing by pushing one pixel in each direction.
2100 This is a hack, but in the interest of good gameplay...
2101 ======================
2103 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2108 VectorCopy (ent->fields.server->origin, oldorg);
2111 for (i=0 ; i<8 ; i++)
2113 // try pushing a little in an axial direction
2116 case 0: dir[0] = 2; dir[1] = 0; break;
2117 case 1: dir[0] = 0; dir[1] = 2; break;
2118 case 2: dir[0] = -2; dir[1] = 0; break;
2119 case 3: dir[0] = 0; dir[1] = -2; break;
2120 case 4: dir[0] = 2; dir[1] = 2; break;
2121 case 5: dir[0] = -2; dir[1] = 2; break;
2122 case 6: dir[0] = 2; dir[1] = -2; break;
2123 case 7: dir[0] = -2; dir[1] = -2; break;
2126 SV_PushEntity (&trace, ent, dir, false, true);
2128 // retry the original move
2129 ent->fields.server->velocity[0] = oldvel[0];
2130 ent->fields.server->velocity[1] = oldvel[1];
2131 ent->fields.server->velocity[2] = 0;
2132 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2134 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2135 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2137 Con_DPrint("TryUnstick - success.\n");
2141 // go back to the original pos and try again
2142 VectorCopy (oldorg, ent->fields.server->origin);
2146 VectorClear (ent->fields.server->velocity);
2147 Con_DPrint("TryUnstick - failure.\n");
2153 =====================
2156 Only used by players
2157 ======================
2159 void SV_WalkMove (prvm_edict_t *ent)
2161 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2162 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2163 trace_t downtrace, trace;
2164 qboolean applygravity;
2166 // if frametime is 0 (due to client sending the same timestamp twice),
2168 if (sv.frametime <= 0)
2171 SV_CheckStuck (ent);
2173 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2175 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2177 SV_CheckVelocity(ent);
2179 // do a regular slide move unless it looks like you ran into a step
2180 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2182 VectorCopy (ent->fields.server->origin, start_origin);
2183 VectorCopy (ent->fields.server->velocity, start_velocity);
2185 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2187 // if the move did not hit the ground at any point, we're not on ground
2189 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2191 SV_CheckVelocity(ent);
2193 SV_LinkEdict_TouchAreaGrid(ent);
2195 if(clip & 8) // teleport
2198 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2201 if (sv_nostep.integer)
2204 VectorCopy(ent->fields.server->origin, originalmove_origin);
2205 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2206 originalmove_clip = clip;
2207 originalmove_flags = (int)ent->fields.server->flags;
2208 originalmove_groundentity = ent->fields.server->groundentity;
2210 // if move didn't block on a step, return
2213 // if move was not trying to move into the step, return
2214 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2217 if (ent->fields.server->movetype != MOVETYPE_FLY)
2219 // return if gibbed by a trigger
2220 if (ent->fields.server->movetype != MOVETYPE_WALK)
2223 // only step up while jumping if that is enabled
2224 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2225 if (!oldonground && ent->fields.server->waterlevel == 0)
2229 // try moving up and forward to go up a step
2230 // back to start pos
2231 VectorCopy (start_origin, ent->fields.server->origin);
2232 VectorCopy (start_velocity, ent->fields.server->velocity);
2235 VectorClear (upmove);
2236 upmove[2] = sv_stepheight.value;
2237 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2239 // we got teleported when upstepping... must abort the move
2244 ent->fields.server->velocity[2] = 0;
2245 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2246 ent->fields.server->velocity[2] += start_velocity[2];
2249 // we got teleported when upstepping... must abort the move
2250 // note that z velocity handling may not be what QC expects here, but we cannot help it
2254 SV_CheckVelocity(ent);
2256 SV_LinkEdict_TouchAreaGrid(ent);
2258 // check for stuckness, possibly due to the limited precision of floats
2259 // in the clipping hulls
2261 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2262 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2264 //Con_Printf("wall\n");
2265 // stepping up didn't make any progress, revert to original move
2266 VectorCopy(originalmove_origin, ent->fields.server->origin);
2267 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2268 //clip = originalmove_clip;
2269 ent->fields.server->flags = originalmove_flags;
2270 ent->fields.server->groundentity = originalmove_groundentity;
2271 // now try to unstick if needed
2272 //clip = SV_TryUnstick (ent, oldvel);
2276 //Con_Printf("step - ");
2278 // extra friction based on view angle
2279 if (clip & 2 && sv_wallfriction.integer)
2280 SV_WallFriction (ent, stepnormal);
2282 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2283 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))
2287 VectorClear (downmove);
2288 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2289 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2291 // we got teleported when downstepping... must abort the move
2295 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2297 // this has been disabled so that you can't jump when you are stepping
2298 // up while already jumping (also known as the Quake2 double jump bug)
2300 // LordHavoc: disabled this check so you can walk on monsters/players
2301 //if (ent->fields.server->solid == SOLID_BSP)
2303 //Con_Printf("onground\n");
2304 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2305 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2311 //Con_Printf("slope\n");
2312 // if the push down didn't end up on good ground, use the move without
2313 // the step up. This happens near wall / slope combinations, and can
2314 // cause the player to hop up higher on a slope too steep to climb
2315 VectorCopy(originalmove_origin, ent->fields.server->origin);
2316 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2317 //clip = originalmove_clip;
2318 ent->fields.server->flags = originalmove_flags;
2319 ent->fields.server->groundentity = originalmove_groundentity;
2322 SV_CheckVelocity(ent);
2324 SV_LinkEdict_TouchAreaGrid(ent);
2327 //============================================================================
2333 Entities that are "stuck" to another entity
2336 void SV_Physics_Follow (prvm_edict_t *ent)
2338 vec3_t vf, vr, vu, angles, v;
2342 if (!SV_RunThink (ent))
2345 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2346 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2347 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])
2349 // quick case for no rotation
2350 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2354 angles[0] = -ent->fields.server->punchangle[0];
2355 angles[1] = ent->fields.server->punchangle[1];
2356 angles[2] = ent->fields.server->punchangle[2];
2357 AngleVectors (angles, vf, vr, vu);
2358 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];
2359 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];
2360 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];
2361 angles[0] = -e->fields.server->angles[0];
2362 angles[1] = e->fields.server->angles[1];
2363 angles[2] = e->fields.server->angles[2];
2364 AngleVectors (angles, vf, vr, vu);
2365 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2366 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2367 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2369 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2371 //SV_LinkEdict_TouchAreaGrid(ent);
2375 ==============================================================================
2379 ==============================================================================
2384 SV_CheckWaterTransition
2388 void SV_CheckWaterTransition (prvm_edict_t *ent)
2391 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2392 if (!ent->fields.server->watertype)
2394 // just spawned here
2395 ent->fields.server->watertype = cont;
2396 ent->fields.server->waterlevel = 1;
2400 // DRESK - Support for Entity Contents Transition Event
2401 // NOTE: Call here BEFORE updating the watertype below,
2402 // and suppress watersplash sound if a valid function
2403 // call was made to allow for custom "splash" sounds.
2404 if( !SV_CheckContentsTransition(ent, cont) )
2405 { // Contents Transition Function Invalid; Potentially Play Water Sound
2406 // check if the entity crossed into or out of water
2407 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2408 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2411 if (cont <= CONTENTS_WATER)
2413 ent->fields.server->watertype = cont;
2414 ent->fields.server->waterlevel = 1;
2418 ent->fields.server->watertype = CONTENTS_EMPTY;
2419 ent->fields.server->waterlevel = 0;
2427 Toss, bounce, and fly movement. When onground, do nothing.
2430 void SV_Physics_Toss (prvm_edict_t *ent)
2437 // if onground, return without moving
2438 if ((int)ent->fields.server->flags & FL_ONGROUND)
2440 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2442 // don't stick to ground if onground and moving upward
2443 ent->fields.server->flags -= FL_ONGROUND;
2445 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2447 // we can trust FL_ONGROUND if groundentity is world because it never moves
2450 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2452 // if ent was supported by a brush model on previous frame,
2453 // and groundentity is now freed, set groundentity to 0 (world)
2454 // which leaves it suspended in the air
2455 ent->fields.server->groundentity = 0;
2456 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2460 ent->priv.server->suspendedinairflag = false;
2462 SV_CheckVelocity (ent);
2465 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2466 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2469 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2471 movetime = sv.frametime;
2472 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2475 VectorScale (ent->fields.server->velocity, movetime, move);
2476 if(!SV_PushEntity (&trace, ent, move, true, true))
2477 return; // teleported
2478 if (ent->priv.server->free)
2480 if (trace.bmodelstartsolid)
2482 // try to unstick the entity
2483 SV_UnstickEntity(ent);
2484 if(!SV_PushEntity (&trace, ent, move, false, true))
2485 return; // teleported
2486 if (ent->priv.server->free)
2489 if (trace.fraction == 1)
2491 movetime *= 1 - min(1, trace.fraction);
2492 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2495 float bouncefactor = 1.0f;
2496 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2497 if (val!=0 && val->_float)
2498 bouncefactor = val->_float;
2500 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2501 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2503 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2505 float d, ent_gravity;
2507 float bouncefactor = 0.5f;
2508 float bouncestop = 60.0f / 800.0f;
2510 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2511 if (val!=0 && val->_float)
2512 bouncefactor = val->_float;
2514 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2515 if (val!=0 && val->_float)
2516 bouncestop = val->_float;
2518 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2519 // LordHavoc: fixed grenades not bouncing when fired down a slope
2520 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2521 if (val!=0 && val->_float)
2522 ent_gravity = val->_float;
2525 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2527 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2528 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2530 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2531 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2532 VectorClear (ent->fields.server->velocity);
2533 VectorClear (ent->fields.server->avelocity);
2536 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2540 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2542 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2543 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2544 VectorClear (ent->fields.server->velocity);
2545 VectorClear (ent->fields.server->avelocity);
2548 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2553 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2554 if (trace.plane.normal[2] > 0.7)
2556 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2557 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2558 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2559 ent->priv.server->suspendedinairflag = true;
2560 VectorClear (ent->fields.server->velocity);
2561 VectorClear (ent->fields.server->avelocity);
2564 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2566 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2570 // check for in water
2571 SV_CheckWaterTransition (ent);
2575 ===============================================================================
2579 ===============================================================================
2586 Monsters freefall when they don't have a ground entity, otherwise
2587 all movement is done with discrete steps.
2589 This is also used for objects that have become still on the ground, but
2590 will fall if the floor is pulled out from under them.
2593 void SV_Physics_Step (prvm_edict_t *ent)
2595 int flags = (int)ent->fields.server->flags;
2598 // Backup Velocity in the event that movetypesteplandevent is called,
2599 // to provide a parameter with the entity's velocity at impact.
2600 prvm_eval_t *movetypesteplandevent;
2601 vec3_t backupVelocity;
2602 VectorCopy(ent->fields.server->velocity, backupVelocity);
2603 // don't fall at all if fly/swim
2604 if (!(flags & (FL_FLY | FL_SWIM)))
2606 if (flags & FL_ONGROUND)
2608 // freefall if onground and moving upward
2609 // freefall if not standing on a world surface (it may be a lift or trap door)
2610 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2612 ent->fields.server->flags -= FL_ONGROUND;
2613 SV_CheckVelocity(ent);
2614 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2616 SV_LinkEdict_TouchAreaGrid(ent);
2617 ent->priv.server->waterposition_forceupdate = true;
2622 // freefall if not onground
2623 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2625 SV_CheckVelocity(ent);
2626 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2628 SV_LinkEdict_TouchAreaGrid(ent);
2631 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2633 // DRESK - Check for Entity Land Event Function
2634 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2636 if(movetypesteplandevent->function)
2637 { // Valid Function; Execute
2638 // Prepare Parameters
2639 // Assign Velocity at Impact
2640 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2641 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2642 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2644 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2645 // Execute VM Function
2646 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2649 // Check for Engine Landing Sound
2650 if(sv_sound_land.string)
2651 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2653 ent->priv.server->waterposition_forceupdate = true;
2658 if (!SV_RunThink(ent))
2661 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2663 ent->priv.server->waterposition_forceupdate = false;
2664 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2665 SV_CheckWaterTransition(ent);
2669 //============================================================================
2671 static void SV_Physics_Entity (prvm_edict_t *ent)
2673 // don't run think/move on newly spawned projectiles as it messes up
2674 // movement interpolation and rocket trails, and is inconsistent with
2675 // respect to entities spawned in the same frame
2676 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2677 // but if it spawns a lower numbered ent, it doesn't - this never moves
2678 // ents in the first frame regardless)
2679 qboolean runmove = ent->priv.server->move;
2680 ent->priv.server->move = true;
2681 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2683 switch ((int) ent->fields.server->movetype)
2686 case MOVETYPE_FAKEPUSH:
2687 SV_Physics_Pusher (ent);
2690 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2691 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2694 case MOVETYPE_FOLLOW:
2695 SV_Physics_Follow (ent);
2697 case MOVETYPE_NOCLIP:
2698 if (SV_RunThink(ent))
2701 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2702 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2707 SV_Physics_Step (ent);
2710 if (SV_RunThink (ent))
2714 case MOVETYPE_BOUNCE:
2715 case MOVETYPE_BOUNCEMISSILE:
2716 case MOVETYPE_FLYMISSILE:
2719 if (SV_RunThink (ent))
2720 SV_Physics_Toss (ent);
2722 case MOVETYPE_PHYSICS:
2723 if (SV_RunThink(ent))
2726 SV_LinkEdict_TouchAreaGrid(ent);
2730 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2735 void SV_Physics_ClientMove(void)
2738 ent = host_client->edict;
2740 // call player physics, this needs the proper frametime
2741 prog->globals.server->frametime = sv.frametime;
2744 // call standard client pre-think, with frametime = 0
2745 prog->globals.server->time = sv.time;
2746 prog->globals.server->frametime = 0;
2747 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2748 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2749 prog->globals.server->frametime = sv.frametime;
2751 // make sure the velocity is sane (not a NaN)
2752 SV_CheckVelocity(ent);
2754 // perform MOVETYPE_WALK behavior
2757 // call standard player post-think, with frametime = 0
2758 prog->globals.server->time = sv.time;
2759 prog->globals.server->frametime = 0;
2760 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2761 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2762 prog->globals.server->frametime = sv.frametime;
2764 if(ent->fields.server->fixangle)
2766 // angle fixing was requested by physics code...
2767 // so store the current angles for later use
2768 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2769 host_client->fixangle_angles_set = TRUE;
2771 // and clear fixangle for the next frame
2772 ent->fields.server->fixangle = 0;
2776 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2778 // don't do physics on disconnected clients, FrikBot relies on this
2779 if (!host_client->spawned)
2782 // make sure the velocity is sane (not a NaN)
2783 SV_CheckVelocity(ent);
2785 // don't run physics here if running asynchronously
2786 if (host_client->clmovement_inputtimeout <= 0)
2789 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2792 // make sure the velocity is still sane (not a NaN)
2793 SV_CheckVelocity(ent);
2795 // call standard client pre-think
2796 prog->globals.server->time = sv.time;
2797 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2798 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2800 // make sure the velocity is still sane (not a NaN)
2801 SV_CheckVelocity(ent);
2804 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2806 // don't do physics on disconnected clients, FrikBot relies on this
2807 if (!host_client->spawned)
2810 // make sure the velocity is sane (not a NaN)
2811 SV_CheckVelocity(ent);
2813 // call standard player post-think
2814 prog->globals.server->time = sv.time;
2815 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2816 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2818 // make sure the velocity is still sane (not a NaN)
2819 SV_CheckVelocity(ent);
2821 if(ent->fields.server->fixangle)
2823 // angle fixing was requested by physics code...
2824 // so store the current angles for later use
2825 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2826 host_client->fixangle_angles_set = TRUE;
2828 // and clear fixangle for the next frame
2829 ent->fields.server->fixangle = 0;
2832 // decrement the countdown variable used to decide when to go back to
2833 // synchronous physics
2834 if (host_client->clmovement_inputtimeout > sv.frametime)
2835 host_client->clmovement_inputtimeout -= sv.frametime;
2837 host_client->clmovement_inputtimeout = 0;
2840 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2842 // don't do physics on disconnected clients, FrikBot relies on this
2843 if (!host_client->spawned)
2845 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2849 // make sure the velocity is sane (not a NaN)
2850 SV_CheckVelocity(ent);
2852 switch ((int) ent->fields.server->movetype)
2855 case MOVETYPE_FAKEPUSH:
2856 SV_Physics_Pusher (ent);
2859 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2860 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2863 case MOVETYPE_FOLLOW:
2864 SV_Physics_Follow (ent);
2866 case MOVETYPE_NOCLIP:
2869 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2870 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2873 SV_Physics_Step (ent);
2877 // don't run physics here if running asynchronously
2878 if (host_client->clmovement_inputtimeout <= 0)
2882 case MOVETYPE_BOUNCE:
2883 case MOVETYPE_BOUNCEMISSILE:
2884 case MOVETYPE_FLYMISSILE:
2887 SV_Physics_Toss (ent);
2893 case MOVETYPE_PHYSICS:
2897 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2901 SV_CheckVelocity (ent);
2904 SV_LinkEdict_TouchAreaGrid(ent);
2906 SV_CheckVelocity (ent);
2915 void SV_Physics (void)
2920 // let the progs know that a new frame has started
2921 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2922 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2923 prog->globals.server->time = sv.time;
2924 prog->globals.server->frametime = sv.frametime;
2925 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2927 // run physics engine
2928 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2931 // treat each object in turn
2934 // if force_retouch, relink all the entities
2935 if (prog->globals.server->force_retouch > 0)
2936 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2937 if (!ent->priv.server->free)
2938 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2940 if (sv_gameplayfix_consistentplayerprethink.integer)
2942 // run physics on the client entities in 3 stages
2943 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2944 if (!ent->priv.server->free)
2945 SV_Physics_ClientEntity_PreThink(ent);
2947 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2948 if (!ent->priv.server->free)
2949 SV_Physics_ClientEntity(ent);
2951 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2952 if (!ent->priv.server->free)
2953 SV_Physics_ClientEntity_PostThink(ent);
2957 // run physics on the client entities
2958 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2960 if (!ent->priv.server->free)
2962 SV_Physics_ClientEntity_PreThink(ent);
2963 SV_Physics_ClientEntity(ent);
2964 SV_Physics_ClientEntity_PostThink(ent);
2969 // run physics on all the non-client entities
2970 if (!sv_freezenonclients.integer)
2972 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2973 if (!ent->priv.server->free)
2974 SV_Physics_Entity(ent);
2975 // make a second pass to see if any ents spawned this frame and make
2976 // sure they run their move/think
2977 if (sv_gameplayfix_delayprojectiles.integer < 0)
2978 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2979 if (!ent->priv.server->move && !ent->priv.server->free)
2980 SV_Physics_Entity(ent);
2983 if (prog->globals.server->force_retouch > 0)
2984 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2986 // LordHavoc: endframe support
2987 if (prog->funcoffsets.EndFrame)
2989 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2990 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2991 prog->globals.server->time = sv.time;
2992 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2995 // decrement prog->num_edicts if the highest number entities died
2996 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2998 if (!sv_freezenonclients.integer)
2999 sv.time += sv.frametime;