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(prvm_edict_t *ent)
695 int i, numtouchedicts, old_self, old_other;
696 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
698 if (ent == prog->edicts)
699 return; // don't add the world
701 if (ent->priv.server->free)
704 if (ent->fields.server->solid == SOLID_NOT)
707 // build a list of edicts to touch, because the link loop can be corrupted
708 // by IncreaseEdicts called during touch functions
709 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
710 if (numtouchedicts > MAX_EDICTS)
712 // this never happens
713 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
714 numtouchedicts = MAX_EDICTS;
717 old_self = prog->globals.server->self;
718 old_other = prog->globals.server->other;
719 for (i = 0;i < numtouchedicts;i++)
721 touch = touchedicts[i];
722 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
725 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
726 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
727 prog->globals.server->time = sv.time;
728 prog->globals.server->trace_allsolid = false;
729 prog->globals.server->trace_startsolid = false;
730 prog->globals.server->trace_fraction = 1;
731 prog->globals.server->trace_inwater = false;
732 prog->globals.server->trace_inopen = true;
733 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
734 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
735 prog->globals.server->trace_plane_dist = 0;
736 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
737 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
739 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
741 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
743 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
745 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
748 prog->globals.server->self = old_self;
749 prog->globals.server->other = old_other;
758 void SV_LinkEdict (prvm_edict_t *ent)
763 if (ent == prog->edicts)
764 return; // don't add the world
766 if (ent->priv.server->free)
771 if (ent->fields.server->solid == SOLID_BSP)
773 int modelindex = (int)ent->fields.server->modelindex;
774 if (modelindex < 0 || modelindex >= MAX_MODELS)
776 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
779 model = sv.models[modelindex];
782 if (!model->TraceBox && developer.integer >= 1)
783 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
785 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
787 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
788 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
790 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
792 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
793 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
797 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
798 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
803 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
804 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
805 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
810 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
811 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
815 // to make items easier to pick up and allow them to be grabbed off
816 // of shelves, the abs sizes are expanded
818 if ((int)ent->fields.server->flags & FL_ITEM)
829 // because movement is clipped an epsilon away from an actual edge,
830 // we must fully check even when bounding boxes don't quite touch
839 VectorCopy(mins, ent->fields.server->absmin);
840 VectorCopy(maxs, ent->fields.server->absmax);
842 World_LinkEdict(&sv.world, ent, mins, maxs);
846 ===============================================================================
850 ===============================================================================
855 SV_TestEntityPosition
857 returns true if the entity is in solid currently
860 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
865 contents = SV_GenericHitSuperContentsMask(ent);
866 VectorAdd(ent->fields.server->origin, offset, org);
867 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
868 if (trace.startsupercontents & contents)
872 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
874 // q1bsp/hlbsp use hulls and if the entity does not exactly match
875 // a hull size it is incorrectly tested, so this code tries to
876 // 'fix' it slightly...
877 // FIXME: this breaks entities larger than the hull size
880 VectorAdd(org, ent->fields.server->mins, m1);
881 VectorAdd(org, ent->fields.server->maxs, m2);
882 VectorSubtract(m2, m1, s);
883 #define EPSILON (1.0f / 32.0f)
884 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
885 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
886 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
887 for (i = 0;i < 8;i++)
889 v[0] = (i & 1) ? m2[0] : m1[0];
890 v[1] = (i & 2) ? m2[1] : m1[1];
891 v[2] = (i & 4) ? m2[2] : m1[2];
892 if (SV_PointSuperContents(v) & contents)
897 // if the trace found a better position for the entity, move it there
898 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
901 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
902 VectorCopy(trace.endpos, ent->fields.server->origin);
904 // verify if the endpos is REALLY outside solid
905 VectorCopy(trace.endpos, org);
906 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
908 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
910 VectorCopy(org, ent->fields.server->origin);
921 void SV_CheckAllEnts (void)
926 // see if any solid entities are inside the final position
927 check = PRVM_NEXT_EDICT(prog->edicts);
928 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
930 if (check->priv.server->free)
932 if (check->fields.server->movetype == MOVETYPE_PUSH
933 || check->fields.server->movetype == MOVETYPE_NONE
934 || check->fields.server->movetype == MOVETYPE_FOLLOW
935 || check->fields.server->movetype == MOVETYPE_NOCLIP)
938 if (SV_TestEntityPosition (check, vec3_origin))
939 Con_Print("entity in invalid position\n");
943 // DRESK - Support for Entity Contents Transition Event
946 SV_CheckContentsTransition
948 returns true if entity had a valid contentstransition function call
951 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
953 int bValidFunctionCall;
954 prvm_eval_t *contentstransition;
956 // Default Valid Function Call to False
957 bValidFunctionCall = false;
959 if(ent->fields.server->watertype != nContents)
960 { // Changed Contents
961 // Acquire Contents Transition Function from QC
962 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
964 if(contentstransition->function)
965 { // Valid Function; Execute
966 // Assign Valid Function
967 bValidFunctionCall = true;
968 // Prepare Parameters (Original Contents, New Contents)
970 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
972 PRVM_G_FLOAT(OFS_PARM1) = nContents;
974 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
975 // Execute VM Function
976 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
980 // Return if Function Call was Valid
981 return bValidFunctionCall;
990 void SV_CheckVelocity (prvm_edict_t *ent)
998 for (i=0 ; i<3 ; i++)
1000 if (IS_NAN(ent->fields.server->velocity[i]))
1002 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1003 ent->fields.server->velocity[i] = 0;
1005 if (IS_NAN(ent->fields.server->origin[i]))
1007 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1008 ent->fields.server->origin[i] = 0;
1012 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1013 // player_run/player_stand1 does not horribly malfunction if the
1014 // velocity becomes a denormalized float
1015 if (VectorLength2(ent->fields.server->velocity) < 0.0001)
1016 VectorClear(ent->fields.server->velocity);
1018 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1019 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1020 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1022 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1023 ent->fields.server->velocity[0] *= wishspeed;
1024 ent->fields.server->velocity[1] *= wishspeed;
1025 ent->fields.server->velocity[2] *= wishspeed;
1033 Runs thinking code if time. There is some play in the exact time the think
1034 function will be called, because it is called before any movement is done
1035 in a frame. Not used for pushmove objects, because they must be exact.
1036 Returns false if the entity removed itself.
1039 qboolean SV_RunThink (prvm_edict_t *ent)
1043 // don't let things stay in the past.
1044 // it is possible to start that way by a trigger with a local time.
1045 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1048 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1050 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1051 ent->fields.server->nextthink = 0;
1052 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1053 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1054 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1055 // mods often set nextthink to time to cause a think every frame,
1056 // we don't want to loop in that case, so exit if the new nextthink is
1057 // <= the time the qc was told, also exit if it is past the end of the
1059 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1062 return !ent->priv.server->free;
1069 Two entities have touched, so run their touch functions
1070 returns true if the impact kept the origin of the touching entity intact
1073 extern void VM_SetTraceGlobals(const trace_t *trace);
1074 extern sizebuf_t vm_tempstringsbuf;
1075 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1077 int restorevm_tempstringsbuf_cursize;
1078 int old_self, old_other;
1080 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1083 old_self = prog->globals.server->self;
1084 old_other = prog->globals.server->other;
1085 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1087 VectorCopy(e1->fields.server->origin, org);
1089 VM_SetTraceGlobals(trace);
1091 prog->globals.server->time = sv.time;
1092 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1094 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1095 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1096 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1099 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1101 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1102 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1103 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1104 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1105 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1106 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1107 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1109 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1111 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1113 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1115 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1118 prog->globals.server->self = old_self;
1119 prog->globals.server->other = old_other;
1120 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1122 return VectorCompare(e1->fields.server->origin, org);
1130 Slide off of the impacting object
1131 returns the blocked flags (1 = floor, 2 = step / wall)
1134 #define STOP_EPSILON 0.1
1135 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1140 backoff = -DotProduct (in, normal) * overbounce;
1141 VectorMA(in, backoff, normal, out);
1143 for (i = 0;i < 3;i++)
1144 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1153 The basic solid body movement clip that slides along multiple planes
1154 Returns the clipflags if the velocity was modified (hit something solid)
1158 8 = teleported by touch method
1159 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1162 static float SV_Gravity (prvm_edict_t *ent);
1163 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1164 #define MAX_CLIP_PLANES 5
1165 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
1167 int blocked, bumpcount;
1168 int i, j, numplanes;
1169 float d, time_left, gravity;
1170 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1180 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1182 gravity = SV_Gravity(ent) * 0.5f;
1183 ent->fields.server->velocity[2] -= gravity;
1187 applygravity = false;
1188 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1192 VectorCopy(ent->fields.server->velocity, original_velocity);
1193 VectorCopy(ent->fields.server->velocity, primal_velocity);
1196 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1198 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1201 VectorScale(ent->fields.server->velocity, time_left, push);
1203 VectorAdd(ent->fields.server->origin, push, end);
1205 if(!SV_PushEntity(&trace, ent, push, false, false))
1207 // we got teleported by a touch function
1208 // let's abort the move
1214 //if (trace.fraction < 0.002)
1219 VectorCopy(ent->fields.server->origin, start);
1220 start[2] += 3;//0.03125;
1221 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1222 end[2] += 3;//0.03125;
1223 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1224 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)))
1226 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
1232 for (i = 0;i < numplanes;i++)
1234 VectorCopy(ent->fields.server->origin, start);
1235 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1236 VectorMA(start, 3, planes[i], start);
1237 VectorMA(end, 3, planes[i], end);
1238 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1239 if (trace.fraction < testtrace.fraction)
1242 VectorCopy(start, ent->fields.server->origin);
1247 // VectorAdd(ent->fields.server->origin, planes[j], start);
1253 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);
1254 if (trace.fraction < 1)
1255 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
1260 if (trace.bmodelstartsolid)
1262 // LordHavoc: note: this code is what makes entities stick in place
1263 // if embedded in world only (you can walk through other objects if
1265 // entity is trapped in another solid
1266 VectorClear(ent->fields.server->velocity);
1271 if (trace.fraction == 1)
1273 if (trace.plane.normal[2])
1275 if (trace.plane.normal[2] > 0.7)
1282 Con_Printf ("SV_FlyMove: !trace.ent");
1283 trace.ent = prog->edicts;
1286 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1287 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1294 // save the trace for player extrafriction
1296 VectorCopy(trace.plane.normal, stepnormal);
1298 if (trace.fraction >= 0.001)
1300 // actually covered some distance
1301 VectorCopy(ent->fields.server->velocity, original_velocity);
1305 time_left *= 1 - trace.fraction;
1307 // clipped to another plane
1308 if (numplanes >= MAX_CLIP_PLANES)
1310 // this shouldn't really happen
1311 VectorClear(ent->fields.server->velocity);
1317 for (i = 0;i < numplanes;i++)
1318 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1322 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1327 VectorCopy(trace.plane.normal, planes[numplanes]);
1330 if (sv_newflymove.integer)
1331 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1334 // modify original_velocity so it parallels all of the clip planes
1335 for (i = 0;i < numplanes;i++)
1337 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1338 for (j = 0;j < numplanes;j++)
1343 if (DotProduct(new_velocity, planes[j]) < 0)
1353 // go along this plane
1354 VectorCopy(new_velocity, ent->fields.server->velocity);
1358 // go along the crease
1361 VectorClear(ent->fields.server->velocity);
1365 CrossProduct(planes[0], planes[1], dir);
1366 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1367 VectorNormalize(dir);
1368 d = DotProduct(dir, ent->fields.server->velocity);
1369 VectorScale(dir, d, ent->fields.server->velocity);
1373 // if current velocity is against the original velocity,
1374 // stop dead to avoid tiny occilations in sloping corners
1375 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1377 VectorClear(ent->fields.server->velocity);
1382 //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]);
1385 if ((blocked & 1) == 0 && bumpcount > 1)
1387 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1388 // flag ONGROUND if there's ground under it
1389 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1393 // LordHavoc: this came from QW and allows you to get out of water more easily
1394 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1395 VectorCopy(primal_velocity, ent->fields.server->velocity);
1396 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1397 ent->fields.server->velocity[2] -= gravity;
1407 static float SV_Gravity (prvm_edict_t *ent)
1412 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1413 if (val!=0 && val->_float)
1414 ent_gravity = val->_float;
1417 return ent_gravity * sv_gravity.value * sv.frametime;
1422 ===============================================================================
1426 ===============================================================================
1433 Does not change the entities velocity at all
1434 The trace struct is filled with the trace that has been done.
1435 Returns true if the push did not result in the entity being teleported by QC code.
1438 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1443 VectorAdd (ent->fields.server->origin, push, end);
1445 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1446 type = MOVE_MISSILE;
1447 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1448 type = MOVE_NOMONSTERS; // only clip against bmodels
1452 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1453 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1456 VectorCopy (trace->endpos, ent->fields.server->origin);
1460 if(!trace->startsolid)
1461 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)
1463 Con_Printf("something eeeeevil happened\n");
1468 SV_LinkEdict_TouchAreaGrid(ent);
1470 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))))
1471 return SV_Impact (ent, trace);
1483 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1486 int pusherowner, pusherprog;
1489 float savesolid, movetime2, pushltime;
1490 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1492 int numcheckentities;
1493 static prvm_edict_t *checkentities[MAX_EDICTS];
1494 dp_model_t *pushermodel;
1495 trace_t trace, trace2;
1496 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1497 unsigned short moved_edicts[MAX_EDICTS];
1499 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])
1501 pusher->fields.server->ltime += movetime;
1505 switch ((int) pusher->fields.server->solid)
1507 // LordHavoc: valid pusher types
1510 case SOLID_SLIDEBOX:
1511 case SOLID_CORPSE: // LordHavoc: this would be weird...
1513 // LordHavoc: no collisions
1516 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1517 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1518 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1519 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1520 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1521 pusher->fields.server->ltime += movetime;
1522 SV_LinkEdict(pusher);
1525 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1528 index = (int) pusher->fields.server->modelindex;
1529 if (index < 1 || index >= MAX_MODELS)
1531 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1534 pushermodel = sv.models[index];
1535 pusherowner = pusher->fields.server->owner;
1536 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1538 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1540 movetime2 = movetime;
1541 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1542 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1543 if (moveangle[0] || moveangle[2])
1545 for (i = 0;i < 3;i++)
1549 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1550 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1554 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1555 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1559 else if (moveangle[1])
1561 for (i = 0;i < 3;i++)
1565 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1566 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1570 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1571 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1577 for (i = 0;i < 3;i++)
1581 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1582 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1586 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1587 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1592 VectorNegate (moveangle, a);
1593 AngleVectorsFLU (a, forward, left, up);
1595 VectorCopy (pusher->fields.server->origin, pushorig);
1596 VectorCopy (pusher->fields.server->angles, pushang);
1597 pushltime = pusher->fields.server->ltime;
1599 // move the pusher to its final position
1601 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1602 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1603 pusher->fields.server->ltime += movetime;
1604 SV_LinkEdict(pusher);
1607 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1608 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1609 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);
1610 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1612 savesolid = pusher->fields.server->solid;
1614 // see if any solid entities are inside the final position
1617 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1618 for (e = 0;e < numcheckentities;e++)
1620 prvm_edict_t *check = checkentities[e];
1621 int movetype = (int)check->fields.server->movetype;
1626 case MOVETYPE_FOLLOW:
1627 case MOVETYPE_NOCLIP:
1628 case MOVETYPE_FAKEPUSH:
1634 if (check->fields.server->owner == pusherprog)
1637 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1640 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1642 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1643 check->priv.server->waterposition_forceupdate = true;
1645 checkcontents = SV_GenericHitSuperContentsMask(check);
1647 // if the entity is standing on the pusher, it will definitely be moved
1648 // if the entity is not standing on the pusher, but is in the pusher's
1649 // final position, move it
1650 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1652 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);
1653 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1654 if (!trace.startsolid)
1656 //Con_Printf("- not in solid\n");
1664 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1665 org2[0] = DotProduct (org, forward);
1666 org2[1] = DotProduct (org, left);
1667 org2[2] = DotProduct (org, up);
1668 VectorSubtract (org2, org, move);
1669 VectorAdd (move, move1, move);
1672 VectorCopy (move1, move);
1674 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1676 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1677 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1678 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1680 // physics objects need better collisions than this code can do
1681 if (movetype == MOVETYPE_PHYSICS)
1683 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1684 SV_LinkEdict(check);
1685 SV_LinkEdict_TouchAreaGrid(check);
1689 // try moving the contacted entity
1690 pusher->fields.server->solid = SOLID_NOT;
1691 if(!SV_PushEntity (&trace, check, move, true, true))
1693 // entity "check" got teleported
1694 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1695 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1696 continue; // pushed enough
1698 // FIXME: turn players specially
1699 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1700 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1701 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1703 // this trace.fraction < 1 check causes items to fall off of pushers
1704 // if they pass under or through a wall
1705 // the groundentity check causes items to fall off of ledges
1706 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1707 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1709 // if it is still inside the pusher, block
1710 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);
1711 if (trace.startsolid)
1713 // try moving the contacted entity a tiny bit further to account for precision errors
1715 pusher->fields.server->solid = SOLID_NOT;
1716 VectorScale(move, 1.1, move2);
1717 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1718 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1719 if(!SV_PushEntity (&trace2, check, move2, true, true))
1721 // entity "check" got teleported
1724 pusher->fields.server->solid = savesolid;
1725 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);
1726 if (trace.startsolid)
1728 // try moving the contacted entity a tiny bit less to account for precision errors
1729 pusher->fields.server->solid = SOLID_NOT;
1730 VectorScale(move, 0.9, move2);
1731 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1732 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1733 if(!SV_PushEntity (&trace2, check, move2, true, true))
1735 // entity "check" got teleported
1738 pusher->fields.server->solid = savesolid;
1739 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);
1740 if (trace.startsolid)
1742 // still inside pusher, so it's really blocked
1745 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1747 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1750 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1751 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1755 VectorCopy (pushorig, pusher->fields.server->origin);
1756 VectorCopy (pushang, pusher->fields.server->angles);
1757 pusher->fields.server->ltime = pushltime;
1758 SV_LinkEdict(pusher);
1760 // move back any entities we already moved
1761 for (i = 0;i < num_moved;i++)
1763 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1764 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1765 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1769 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1770 if (pusher->fields.server->blocked)
1772 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1773 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1774 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1781 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1782 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1783 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1792 void SV_Physics_Pusher (prvm_edict_t *ent)
1794 float thinktime, oldltime, movetime;
1796 oldltime = ent->fields.server->ltime;
1798 thinktime = ent->fields.server->nextthink;
1799 if (thinktime < ent->fields.server->ltime + sv.frametime)
1801 movetime = thinktime - ent->fields.server->ltime;
1806 movetime = sv.frametime;
1809 // advances ent->fields.server->ltime if not blocked
1810 SV_PushMove (ent, movetime);
1812 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1814 ent->fields.server->nextthink = 0;
1815 prog->globals.server->time = sv.time;
1816 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1817 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1818 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1824 ===============================================================================
1828 ===============================================================================
1831 static float unstickoffsets[] =
1833 // poutting -/+z changes first as they are least weird
1848 typedef enum unstickresult_e
1856 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1860 // if not stuck in a bmodel, just return
1861 if (!SV_TestEntityPosition(ent, vec3_origin))
1862 return UNSTICK_GOOD;
1864 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1866 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1868 VectorCopy(unstickoffsets + i, offset);
1870 //SV_LinkEdict_TouchAreaGrid(ent);
1871 return UNSTICK_UNSTUCK;
1875 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1876 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1878 for(i = 2; i <= maxunstick; ++i)
1880 VectorClear(offset);
1882 if (!SV_TestEntityPosition(ent, offset))
1885 //SV_LinkEdict_TouchAreaGrid(ent);
1886 return UNSTICK_UNSTUCK;
1889 if (!SV_TestEntityPosition(ent, offset))
1892 //SV_LinkEdict_TouchAreaGrid(ent);
1893 return UNSTICK_UNSTUCK;
1897 return UNSTICK_STUCK;
1900 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1903 switch(SV_UnstickEntityReturnOffset(ent, offset))
1907 case UNSTICK_UNSTUCK:
1908 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]);
1911 if (developer.integer >= 100)
1912 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1915 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1924 This is a big hack to try and fix the rare case of getting stuck in the world
1928 void SV_CheckStuck (prvm_edict_t *ent)
1932 switch(SV_UnstickEntityReturnOffset(ent, offset))
1935 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1937 case UNSTICK_UNSTUCK:
1938 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]);
1941 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1942 if (!SV_TestEntityPosition(ent, offset))
1944 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1946 //SV_LinkEdict_TouchAreaGrid(ent);
1949 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1952 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1962 qboolean SV_CheckWater (prvm_edict_t *ent)
1965 int nNativeContents;
1968 point[0] = ent->fields.server->origin[0];
1969 point[1] = ent->fields.server->origin[1];
1970 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1972 // DRESK - Support for Entity Contents Transition Event
1973 // NOTE: Some logic needed to be slightly re-ordered
1974 // to not affect performance and allow for the feature.
1976 // Acquire Super Contents Prior to Resets
1977 cont = SV_PointSuperContents(point);
1978 // Acquire Native Contents Here
1979 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1981 // DRESK - Support for Entity Contents Transition Event
1982 if(ent->fields.server->watertype)
1983 // Entity did NOT Spawn; Check
1984 SV_CheckContentsTransition(ent, nNativeContents);
1987 ent->fields.server->waterlevel = 0;
1988 ent->fields.server->watertype = CONTENTS_EMPTY;
1989 cont = SV_PointSuperContents(point);
1990 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1992 ent->fields.server->watertype = nNativeContents;
1993 ent->fields.server->waterlevel = 1;
1994 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1995 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1997 ent->fields.server->waterlevel = 2;
1998 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1999 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2000 ent->fields.server->waterlevel = 3;
2004 return ent->fields.server->waterlevel > 1;
2013 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2016 vec3_t forward, into, side;
2018 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2019 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2021 // cut the tangential velocity
2022 i = DotProduct (stepnormal, ent->fields.server->velocity);
2023 VectorScale (stepnormal, i, into);
2024 VectorSubtract (ent->fields.server->velocity, into, side);
2025 ent->fields.server->velocity[0] = side[0] * (1 + d);
2026 ent->fields.server->velocity[1] = side[1] * (1 + d);
2032 =====================
2035 Player has come to a dead stop, possibly due to the problem with limited
2036 float precision at some angle joins in the BSP hull.
2038 Try fixing by pushing one pixel in each direction.
2040 This is a hack, but in the interest of good gameplay...
2041 ======================
2043 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2048 VectorCopy (ent->fields.server->origin, oldorg);
2051 for (i=0 ; i<8 ; i++)
2053 // try pushing a little in an axial direction
2056 case 0: dir[0] = 2; dir[1] = 0; break;
2057 case 1: dir[0] = 0; dir[1] = 2; break;
2058 case 2: dir[0] = -2; dir[1] = 0; break;
2059 case 3: dir[0] = 0; dir[1] = -2; break;
2060 case 4: dir[0] = 2; dir[1] = 2; break;
2061 case 5: dir[0] = -2; dir[1] = 2; break;
2062 case 6: dir[0] = 2; dir[1] = -2; break;
2063 case 7: dir[0] = -2; dir[1] = -2; break;
2066 SV_PushEntity (&trace, ent, dir, false, true);
2068 // retry the original move
2069 ent->fields.server->velocity[0] = oldvel[0];
2070 ent->fields.server->velocity[1] = oldvel[1];
2071 ent->fields.server->velocity[2] = 0;
2072 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2074 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2075 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2077 Con_DPrint("TryUnstick - success.\n");
2081 // go back to the original pos and try again
2082 VectorCopy (oldorg, ent->fields.server->origin);
2086 VectorClear (ent->fields.server->velocity);
2087 Con_DPrint("TryUnstick - failure.\n");
2093 =====================
2096 Only used by players
2097 ======================
2099 void SV_WalkMove (prvm_edict_t *ent)
2101 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2102 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2103 trace_t downtrace, trace;
2104 qboolean applygravity;
2106 // if frametime is 0 (due to client sending the same timestamp twice),
2108 if (sv.frametime <= 0)
2111 SV_CheckStuck (ent);
2113 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2115 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2117 SV_CheckVelocity(ent);
2119 // do a regular slide move unless it looks like you ran into a step
2120 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2122 VectorCopy (ent->fields.server->origin, start_origin);
2123 VectorCopy (ent->fields.server->velocity, start_velocity);
2125 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2127 // if the move did not hit the ground at any point, we're not on ground
2129 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2131 SV_CheckVelocity(ent);
2133 SV_LinkEdict_TouchAreaGrid(ent);
2135 if(clip & 8) // teleport
2138 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2141 if (sv_nostep.integer)
2144 VectorCopy(ent->fields.server->origin, originalmove_origin);
2145 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2146 originalmove_clip = clip;
2147 originalmove_flags = (int)ent->fields.server->flags;
2148 originalmove_groundentity = ent->fields.server->groundentity;
2150 // if move didn't block on a step, return
2153 // if move was not trying to move into the step, return
2154 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2157 if (ent->fields.server->movetype != MOVETYPE_FLY)
2159 // return if gibbed by a trigger
2160 if (ent->fields.server->movetype != MOVETYPE_WALK)
2163 // only step up while jumping if that is enabled
2164 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2165 if (!oldonground && ent->fields.server->waterlevel == 0)
2169 // try moving up and forward to go up a step
2170 // back to start pos
2171 VectorCopy (start_origin, ent->fields.server->origin);
2172 VectorCopy (start_velocity, ent->fields.server->velocity);
2175 VectorClear (upmove);
2176 upmove[2] = sv_stepheight.value;
2177 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2179 // we got teleported when upstepping... must abort the move
2184 ent->fields.server->velocity[2] = 0;
2185 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2186 ent->fields.server->velocity[2] += start_velocity[2];
2189 // we got teleported when upstepping... must abort the move
2190 // note that z velocity handling may not be what QC expects here, but we cannot help it
2194 SV_CheckVelocity(ent);
2196 SV_LinkEdict_TouchAreaGrid(ent);
2198 // check for stuckness, possibly due to the limited precision of floats
2199 // in the clipping hulls
2201 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2202 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2204 //Con_Printf("wall\n");
2205 // stepping up didn't make any progress, revert to original move
2206 VectorCopy(originalmove_origin, ent->fields.server->origin);
2207 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2208 //clip = originalmove_clip;
2209 ent->fields.server->flags = originalmove_flags;
2210 ent->fields.server->groundentity = originalmove_groundentity;
2211 // now try to unstick if needed
2212 //clip = SV_TryUnstick (ent, oldvel);
2216 //Con_Printf("step - ");
2218 // extra friction based on view angle
2219 if (clip & 2 && sv_wallfriction.integer)
2220 SV_WallFriction (ent, stepnormal);
2222 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2223 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))
2227 VectorClear (downmove);
2228 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2229 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2231 // we got teleported when downstepping... must abort the move
2235 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2237 // this has been disabled so that you can't jump when you are stepping
2238 // up while already jumping (also known as the Quake2 double jump bug)
2240 // LordHavoc: disabled this check so you can walk on monsters/players
2241 //if (ent->fields.server->solid == SOLID_BSP)
2243 //Con_Printf("onground\n");
2244 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2245 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2251 //Con_Printf("slope\n");
2252 // if the push down didn't end up on good ground, use the move without
2253 // the step up. This happens near wall / slope combinations, and can
2254 // cause the player to hop up higher on a slope too steep to climb
2255 VectorCopy(originalmove_origin, ent->fields.server->origin);
2256 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2257 //clip = originalmove_clip;
2258 ent->fields.server->flags = originalmove_flags;
2259 ent->fields.server->groundentity = originalmove_groundentity;
2262 SV_CheckVelocity(ent);
2264 SV_LinkEdict_TouchAreaGrid(ent);
2267 //============================================================================
2273 Entities that are "stuck" to another entity
2276 void SV_Physics_Follow (prvm_edict_t *ent)
2278 vec3_t vf, vr, vu, angles, v;
2282 if (!SV_RunThink (ent))
2285 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2286 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2287 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])
2289 // quick case for no rotation
2290 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2294 angles[0] = -ent->fields.server->punchangle[0];
2295 angles[1] = ent->fields.server->punchangle[1];
2296 angles[2] = ent->fields.server->punchangle[2];
2297 AngleVectors (angles, vf, vr, vu);
2298 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];
2299 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];
2300 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];
2301 angles[0] = -e->fields.server->angles[0];
2302 angles[1] = e->fields.server->angles[1];
2303 angles[2] = e->fields.server->angles[2];
2304 AngleVectors (angles, vf, vr, vu);
2305 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2306 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2307 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2309 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2311 //SV_LinkEdict_TouchAreaGrid(ent);
2315 ==============================================================================
2319 ==============================================================================
2324 SV_CheckWaterTransition
2328 void SV_CheckWaterTransition (prvm_edict_t *ent)
2331 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2332 if (!ent->fields.server->watertype)
2334 // just spawned here
2335 ent->fields.server->watertype = cont;
2336 ent->fields.server->waterlevel = 1;
2340 // DRESK - Support for Entity Contents Transition Event
2341 // NOTE: Call here BEFORE updating the watertype below,
2342 // and suppress watersplash sound if a valid function
2343 // call was made to allow for custom "splash" sounds.
2344 if( !SV_CheckContentsTransition(ent, cont) )
2345 { // Contents Transition Function Invalid; Potentially Play Water Sound
2346 // check if the entity crossed into or out of water
2347 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2348 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2351 if (cont <= CONTENTS_WATER)
2353 ent->fields.server->watertype = cont;
2354 ent->fields.server->waterlevel = 1;
2358 ent->fields.server->watertype = CONTENTS_EMPTY;
2359 ent->fields.server->waterlevel = 0;
2367 Toss, bounce, and fly movement. When onground, do nothing.
2370 void SV_Physics_Toss (prvm_edict_t *ent)
2377 // if onground, return without moving
2378 if ((int)ent->fields.server->flags & FL_ONGROUND)
2380 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2382 // don't stick to ground if onground and moving upward
2383 ent->fields.server->flags -= FL_ONGROUND;
2385 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2387 // we can trust FL_ONGROUND if groundentity is world because it never moves
2390 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2392 // if ent was supported by a brush model on previous frame,
2393 // and groundentity is now freed, set groundentity to 0 (world)
2394 // which leaves it suspended in the air
2395 ent->fields.server->groundentity = 0;
2396 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2400 ent->priv.server->suspendedinairflag = false;
2402 SV_CheckVelocity (ent);
2405 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2406 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2409 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2411 movetime = sv.frametime;
2412 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2415 VectorScale (ent->fields.server->velocity, movetime, move);
2416 if(!SV_PushEntity (&trace, ent, move, true, true))
2417 return; // teleported
2418 if (ent->priv.server->free)
2420 if (trace.bmodelstartsolid)
2422 // try to unstick the entity
2423 SV_UnstickEntity(ent);
2424 if(!SV_PushEntity (&trace, ent, move, false, true))
2425 return; // teleported
2426 if (ent->priv.server->free)
2429 if (trace.fraction == 1)
2431 movetime *= 1 - min(1, trace.fraction);
2432 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2435 float bouncefactor = 1.0f;
2436 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2437 if (val!=0 && val->_float)
2438 bouncefactor = val->_float;
2440 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2441 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2443 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2445 float d, ent_gravity;
2447 float bouncefactor = 0.5f;
2448 float bouncestop = 60.0f / 800.0f;
2450 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2451 if (val!=0 && val->_float)
2452 bouncefactor = val->_float;
2454 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2455 if (val!=0 && val->_float)
2456 bouncestop = val->_float;
2458 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2459 // LordHavoc: fixed grenades not bouncing when fired down a slope
2460 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2461 if (val!=0 && val->_float)
2462 ent_gravity = val->_float;
2465 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2467 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2468 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2470 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2471 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2472 VectorClear (ent->fields.server->velocity);
2473 VectorClear (ent->fields.server->avelocity);
2476 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2480 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2482 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2483 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2484 VectorClear (ent->fields.server->velocity);
2485 VectorClear (ent->fields.server->avelocity);
2488 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2493 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2494 if (trace.plane.normal[2] > 0.7)
2496 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2497 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2498 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2499 ent->priv.server->suspendedinairflag = true;
2500 VectorClear (ent->fields.server->velocity);
2501 VectorClear (ent->fields.server->avelocity);
2504 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2506 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2510 // check for in water
2511 SV_CheckWaterTransition (ent);
2515 ===============================================================================
2519 ===============================================================================
2526 Monsters freefall when they don't have a ground entity, otherwise
2527 all movement is done with discrete steps.
2529 This is also used for objects that have become still on the ground, but
2530 will fall if the floor is pulled out from under them.
2533 void SV_Physics_Step (prvm_edict_t *ent)
2535 int flags = (int)ent->fields.server->flags;
2538 // Backup Velocity in the event that movetypesteplandevent is called,
2539 // to provide a parameter with the entity's velocity at impact.
2540 prvm_eval_t *movetypesteplandevent;
2541 vec3_t backupVelocity;
2542 VectorCopy(ent->fields.server->velocity, backupVelocity);
2543 // don't fall at all if fly/swim
2544 if (!(flags & (FL_FLY | FL_SWIM)))
2546 if (flags & FL_ONGROUND)
2548 // freefall if onground and moving upward
2549 // freefall if not standing on a world surface (it may be a lift or trap door)
2550 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2552 ent->fields.server->flags -= FL_ONGROUND;
2553 SV_CheckVelocity(ent);
2554 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2556 SV_LinkEdict_TouchAreaGrid(ent);
2557 ent->priv.server->waterposition_forceupdate = true;
2562 // freefall if not onground
2563 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2565 SV_CheckVelocity(ent);
2566 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2568 SV_LinkEdict_TouchAreaGrid(ent);
2571 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2573 // DRESK - Check for Entity Land Event Function
2574 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2576 if(movetypesteplandevent->function)
2577 { // Valid Function; Execute
2578 // Prepare Parameters
2579 // Assign Velocity at Impact
2580 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2581 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2582 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2584 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2585 // Execute VM Function
2586 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2589 // Check for Engine Landing Sound
2590 if(sv_sound_land.string)
2591 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2593 ent->priv.server->waterposition_forceupdate = true;
2598 if (!SV_RunThink(ent))
2601 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2603 ent->priv.server->waterposition_forceupdate = false;
2604 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2605 SV_CheckWaterTransition(ent);
2609 //============================================================================
2611 static void SV_Physics_Entity (prvm_edict_t *ent)
2613 // don't run think/move on newly spawned projectiles as it messes up
2614 // movement interpolation and rocket trails, and is inconsistent with
2615 // respect to entities spawned in the same frame
2616 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2617 // but if it spawns a lower numbered ent, it doesn't - this never moves
2618 // ents in the first frame regardless)
2619 qboolean runmove = ent->priv.server->move;
2620 ent->priv.server->move = true;
2621 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2623 switch ((int) ent->fields.server->movetype)
2626 case MOVETYPE_FAKEPUSH:
2627 SV_Physics_Pusher (ent);
2630 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2631 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2634 case MOVETYPE_FOLLOW:
2635 SV_Physics_Follow (ent);
2637 case MOVETYPE_NOCLIP:
2638 if (SV_RunThink(ent))
2641 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2642 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2647 SV_Physics_Step (ent);
2650 if (SV_RunThink (ent))
2654 case MOVETYPE_BOUNCE:
2655 case MOVETYPE_BOUNCEMISSILE:
2656 case MOVETYPE_FLYMISSILE:
2659 if (SV_RunThink (ent))
2660 SV_Physics_Toss (ent);
2662 case MOVETYPE_PHYSICS:
2663 if (SV_RunThink(ent))
2666 SV_LinkEdict_TouchAreaGrid(ent);
2670 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2675 void SV_Physics_ClientMove(void)
2678 ent = host_client->edict;
2680 // call player physics, this needs the proper frametime
2681 prog->globals.server->frametime = sv.frametime;
2684 // call standard client pre-think, with frametime = 0
2685 prog->globals.server->time = sv.time;
2686 prog->globals.server->frametime = 0;
2687 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2688 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2689 prog->globals.server->frametime = sv.frametime;
2691 // make sure the velocity is sane (not a NaN)
2692 SV_CheckVelocity(ent);
2694 // perform MOVETYPE_WALK behavior
2697 // call standard player post-think, with frametime = 0
2698 prog->globals.server->time = sv.time;
2699 prog->globals.server->frametime = 0;
2700 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2701 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2702 prog->globals.server->frametime = sv.frametime;
2704 if(ent->fields.server->fixangle)
2706 // angle fixing was requested by physics code...
2707 // so store the current angles for later use
2708 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2709 host_client->fixangle_angles_set = TRUE;
2711 // and clear fixangle for the next frame
2712 ent->fields.server->fixangle = 0;
2716 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2718 // don't do physics on disconnected clients, FrikBot relies on this
2719 if (!host_client->spawned)
2722 // make sure the velocity is sane (not a NaN)
2723 SV_CheckVelocity(ent);
2725 // don't run physics here if running asynchronously
2726 if (host_client->clmovement_inputtimeout <= 0)
2729 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2732 // make sure the velocity is still sane (not a NaN)
2733 SV_CheckVelocity(ent);
2735 // call standard client pre-think
2736 prog->globals.server->time = sv.time;
2737 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2738 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2740 // make sure the velocity is still sane (not a NaN)
2741 SV_CheckVelocity(ent);
2744 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2746 // don't do physics on disconnected clients, FrikBot relies on this
2747 if (!host_client->spawned)
2750 // make sure the velocity is sane (not a NaN)
2751 SV_CheckVelocity(ent);
2753 // call standard player post-think
2754 prog->globals.server->time = sv.time;
2755 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2756 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2758 // make sure the velocity is still sane (not a NaN)
2759 SV_CheckVelocity(ent);
2761 if(ent->fields.server->fixangle)
2763 // angle fixing was requested by physics code...
2764 // so store the current angles for later use
2765 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2766 host_client->fixangle_angles_set = TRUE;
2768 // and clear fixangle for the next frame
2769 ent->fields.server->fixangle = 0;
2772 // decrement the countdown variable used to decide when to go back to
2773 // synchronous physics
2774 if (host_client->clmovement_inputtimeout > sv.frametime)
2775 host_client->clmovement_inputtimeout -= sv.frametime;
2777 host_client->clmovement_inputtimeout = 0;
2780 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2782 // don't do physics on disconnected clients, FrikBot relies on this
2783 if (!host_client->spawned)
2785 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2789 // make sure the velocity is sane (not a NaN)
2790 SV_CheckVelocity(ent);
2792 switch ((int) ent->fields.server->movetype)
2795 case MOVETYPE_FAKEPUSH:
2796 SV_Physics_Pusher (ent);
2799 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2800 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2803 case MOVETYPE_FOLLOW:
2804 SV_Physics_Follow (ent);
2806 case MOVETYPE_NOCLIP:
2809 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2810 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2813 SV_Physics_Step (ent);
2817 // don't run physics here if running asynchronously
2818 if (host_client->clmovement_inputtimeout <= 0)
2822 case MOVETYPE_BOUNCE:
2823 case MOVETYPE_BOUNCEMISSILE:
2824 case MOVETYPE_FLYMISSILE:
2827 SV_Physics_Toss (ent);
2833 case MOVETYPE_PHYSICS:
2837 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2841 SV_CheckVelocity (ent);
2844 SV_LinkEdict_TouchAreaGrid(ent);
2846 SV_CheckVelocity (ent);
2855 void SV_Physics (void)
2860 // let the progs know that a new frame has started
2861 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2862 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2863 prog->globals.server->time = sv.time;
2864 prog->globals.server->frametime = sv.frametime;
2865 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2867 // run physics engine
2868 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2871 // treat each object in turn
2874 // if force_retouch, relink all the entities
2875 if (prog->globals.server->force_retouch > 0)
2876 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2877 if (!ent->priv.server->free)
2878 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2880 if (sv_gameplayfix_consistentplayerprethink.integer)
2882 // run physics on the client entities in 3 stages
2883 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2884 if (!ent->priv.server->free)
2885 SV_Physics_ClientEntity_PreThink(ent);
2887 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2888 if (!ent->priv.server->free)
2889 SV_Physics_ClientEntity(ent);
2891 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2892 if (!ent->priv.server->free)
2893 SV_Physics_ClientEntity_PostThink(ent);
2897 // run physics on the client entities
2898 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2900 if (!ent->priv.server->free)
2902 SV_Physics_ClientEntity_PreThink(ent);
2903 SV_Physics_ClientEntity(ent);
2904 SV_Physics_ClientEntity_PostThink(ent);
2909 // run physics on all the non-client entities
2910 if (!sv_freezenonclients.integer)
2912 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2913 if (!ent->priv.server->free)
2914 SV_Physics_Entity(ent);
2915 // make a second pass to see if any ents spawned this frame and make
2916 // sure they run their move/think
2917 if (sv_gameplayfix_delayprojectiles.integer < 0)
2918 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2919 if (!ent->priv.server->move && !ent->priv.server->free)
2920 SV_Physics_Entity(ent);
2923 if (prog->globals.server->force_retouch > 0)
2924 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2926 // LordHavoc: endframe support
2927 if (prog->funcoffsets.EndFrame)
2929 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2930 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2931 prog->globals.server->time = sv.time;
2932 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2935 // decrement prog->num_edicts if the highest number entities died
2936 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2938 if (!sv_freezenonclients.integer)
2939 sv.time += sv.frametime;