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)
1487 VectorAdd (ent->fields.server->origin, push, end);
1489 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1490 type = MOVE_MISSILE;
1491 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1492 type = MOVE_NOMONSTERS; // only clip against bmodels
1496 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1497 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1500 VectorCopy (trace->endpos, ent->fields.server->origin);
1504 if(!trace->startsolid)
1505 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)
1507 Con_Printf("something eeeeevil happened\n");
1512 SV_LinkEdict_TouchAreaGrid(ent);
1514 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))))
1515 return SV_Impact (ent, trace);
1527 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1530 int pusherowner, pusherprog;
1533 float savesolid, movetime2, pushltime;
1534 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1536 int numcheckentities;
1537 static prvm_edict_t *checkentities[MAX_EDICTS];
1538 dp_model_t *pushermodel;
1539 trace_t trace, trace2;
1540 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1541 unsigned short moved_edicts[MAX_EDICTS];
1543 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])
1545 pusher->fields.server->ltime += movetime;
1549 switch ((int) pusher->fields.server->solid)
1551 // LordHavoc: valid pusher types
1554 case SOLID_SLIDEBOX:
1555 case SOLID_CORPSE: // LordHavoc: this would be weird...
1557 // LordHavoc: no collisions
1560 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1561 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1562 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1563 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1564 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1565 pusher->fields.server->ltime += movetime;
1566 SV_LinkEdict(pusher);
1569 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1572 index = (int) pusher->fields.server->modelindex;
1573 if (index < 1 || index >= MAX_MODELS)
1575 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1578 pushermodel = sv.models[index];
1579 pusherowner = pusher->fields.server->owner;
1580 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1582 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1584 movetime2 = movetime;
1585 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1586 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1587 if (moveangle[0] || moveangle[2])
1589 for (i = 0;i < 3;i++)
1593 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1594 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1598 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1599 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1603 else if (moveangle[1])
1605 for (i = 0;i < 3;i++)
1609 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1610 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1614 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1615 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1621 for (i = 0;i < 3;i++)
1625 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1626 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1630 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1631 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1636 VectorNegate (moveangle, a);
1637 AngleVectorsFLU (a, forward, left, up);
1639 VectorCopy (pusher->fields.server->origin, pushorig);
1640 VectorCopy (pusher->fields.server->angles, pushang);
1641 pushltime = pusher->fields.server->ltime;
1643 // move the pusher to its final position
1645 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1646 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1647 pusher->fields.server->ltime += movetime;
1648 SV_LinkEdict(pusher);
1651 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1652 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1653 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);
1654 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1656 savesolid = pusher->fields.server->solid;
1658 // see if any solid entities are inside the final position
1661 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1662 for (e = 0;e < numcheckentities;e++)
1664 prvm_edict_t *check = checkentities[e];
1665 int movetype = (int)check->fields.server->movetype;
1670 case MOVETYPE_FOLLOW:
1671 case MOVETYPE_NOCLIP:
1672 case MOVETYPE_FAKEPUSH:
1678 if (check->fields.server->owner == pusherprog)
1681 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1684 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1686 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1687 check->priv.server->waterposition_forceupdate = true;
1689 checkcontents = SV_GenericHitSuperContentsMask(check);
1691 // if the entity is standing on the pusher, it will definitely be moved
1692 // if the entity is not standing on the pusher, but is in the pusher's
1693 // final position, move it
1694 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1696 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);
1697 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1698 if (!trace.startsolid)
1700 //Con_Printf("- not in solid\n");
1708 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1709 org2[0] = DotProduct (org, forward);
1710 org2[1] = DotProduct (org, left);
1711 org2[2] = DotProduct (org, up);
1712 VectorSubtract (org2, org, move);
1713 VectorAdd (move, move1, move);
1716 VectorCopy (move1, move);
1718 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1720 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1721 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1722 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1724 // physics objects need better collisions than this code can do
1725 if (movetype == MOVETYPE_PHYSICS)
1727 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1728 SV_LinkEdict(check);
1729 SV_LinkEdict_TouchAreaGrid(check);
1733 // try moving the contacted entity
1734 pusher->fields.server->solid = SOLID_NOT;
1735 if(!SV_PushEntity (&trace, check, move, true, true))
1737 // entity "check" got teleported
1738 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1739 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1740 continue; // pushed enough
1742 // FIXME: turn players specially
1743 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1744 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1745 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1747 // this trace.fraction < 1 check causes items to fall off of pushers
1748 // if they pass under or through a wall
1749 // the groundentity check causes items to fall off of ledges
1750 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1751 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1753 // if it is still inside the pusher, block
1754 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);
1755 if (trace.startsolid)
1757 // try moving the contacted entity a tiny bit further to account for precision errors
1759 pusher->fields.server->solid = SOLID_NOT;
1760 VectorScale(move, 1.1, move2);
1761 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1762 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1763 if(!SV_PushEntity (&trace2, check, move2, true, true))
1765 // entity "check" got teleported
1768 pusher->fields.server->solid = savesolid;
1769 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);
1770 if (trace.startsolid)
1772 // try moving the contacted entity a tiny bit less to account for precision errors
1773 pusher->fields.server->solid = SOLID_NOT;
1774 VectorScale(move, 0.9, move2);
1775 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1776 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1777 if(!SV_PushEntity (&trace2, check, move2, true, true))
1779 // entity "check" got teleported
1782 pusher->fields.server->solid = savesolid;
1783 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);
1784 if (trace.startsolid)
1786 // still inside pusher, so it's really blocked
1789 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1791 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1794 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1795 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1799 VectorCopy (pushorig, pusher->fields.server->origin);
1800 VectorCopy (pushang, pusher->fields.server->angles);
1801 pusher->fields.server->ltime = pushltime;
1802 SV_LinkEdict(pusher);
1804 // move back any entities we already moved
1805 for (i = 0;i < num_moved;i++)
1807 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1808 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1809 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1813 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1814 if (pusher->fields.server->blocked)
1816 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1817 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1818 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1825 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1826 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1827 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1836 void SV_Physics_Pusher (prvm_edict_t *ent)
1838 float thinktime, oldltime, movetime;
1840 oldltime = ent->fields.server->ltime;
1842 thinktime = ent->fields.server->nextthink;
1843 if (thinktime < ent->fields.server->ltime + sv.frametime)
1845 movetime = thinktime - ent->fields.server->ltime;
1850 movetime = sv.frametime;
1853 // advances ent->fields.server->ltime if not blocked
1854 SV_PushMove (ent, movetime);
1856 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1858 ent->fields.server->nextthink = 0;
1859 prog->globals.server->time = sv.time;
1860 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1861 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1862 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1868 ===============================================================================
1872 ===============================================================================
1875 static float unstickoffsets[] =
1877 // poutting -/+z changes first as they are least weird
1892 typedef enum unstickresult_e
1900 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1904 // if not stuck in a bmodel, just return
1905 if (!SV_TestEntityPosition(ent, vec3_origin))
1906 return UNSTICK_GOOD;
1908 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1910 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1912 VectorCopy(unstickoffsets + i, offset);
1914 //SV_LinkEdict_TouchAreaGrid(ent);
1915 return UNSTICK_UNSTUCK;
1919 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1920 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1922 for(i = 2; i <= maxunstick; ++i)
1924 VectorClear(offset);
1926 if (!SV_TestEntityPosition(ent, offset))
1929 //SV_LinkEdict_TouchAreaGrid(ent);
1930 return UNSTICK_UNSTUCK;
1933 if (!SV_TestEntityPosition(ent, offset))
1936 //SV_LinkEdict_TouchAreaGrid(ent);
1937 return UNSTICK_UNSTUCK;
1941 return UNSTICK_STUCK;
1944 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1947 switch(SV_UnstickEntityReturnOffset(ent, offset))
1951 case UNSTICK_UNSTUCK:
1952 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]);
1955 if (developer.integer >= 100)
1956 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1959 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1968 This is a big hack to try and fix the rare case of getting stuck in the world
1972 void SV_CheckStuck (prvm_edict_t *ent)
1976 switch(SV_UnstickEntityReturnOffset(ent, offset))
1979 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1981 case UNSTICK_UNSTUCK:
1982 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]);
1985 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1986 if (!SV_TestEntityPosition(ent, offset))
1988 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1990 //SV_LinkEdict_TouchAreaGrid(ent);
1993 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1996 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2006 qboolean SV_CheckWater (prvm_edict_t *ent)
2009 int nNativeContents;
2012 point[0] = ent->fields.server->origin[0];
2013 point[1] = ent->fields.server->origin[1];
2014 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2016 // DRESK - Support for Entity Contents Transition Event
2017 // NOTE: Some logic needed to be slightly re-ordered
2018 // to not affect performance and allow for the feature.
2020 // Acquire Super Contents Prior to Resets
2021 cont = SV_PointSuperContents(point);
2022 // Acquire Native Contents Here
2023 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2025 // DRESK - Support for Entity Contents Transition Event
2026 if(ent->fields.server->watertype)
2027 // Entity did NOT Spawn; Check
2028 SV_CheckContentsTransition(ent, nNativeContents);
2031 ent->fields.server->waterlevel = 0;
2032 ent->fields.server->watertype = CONTENTS_EMPTY;
2033 cont = SV_PointSuperContents(point);
2034 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2036 ent->fields.server->watertype = nNativeContents;
2037 ent->fields.server->waterlevel = 1;
2038 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2039 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2041 ent->fields.server->waterlevel = 2;
2042 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2043 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2044 ent->fields.server->waterlevel = 3;
2048 return ent->fields.server->waterlevel > 1;
2057 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2060 vec3_t forward, into, side;
2062 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2063 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2065 // cut the tangential velocity
2066 i = DotProduct (stepnormal, ent->fields.server->velocity);
2067 VectorScale (stepnormal, i, into);
2068 VectorSubtract (ent->fields.server->velocity, into, side);
2069 ent->fields.server->velocity[0] = side[0] * (1 + d);
2070 ent->fields.server->velocity[1] = side[1] * (1 + d);
2076 =====================
2079 Player has come to a dead stop, possibly due to the problem with limited
2080 float precision at some angle joins in the BSP hull.
2082 Try fixing by pushing one pixel in each direction.
2084 This is a hack, but in the interest of good gameplay...
2085 ======================
2087 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2092 VectorCopy (ent->fields.server->origin, oldorg);
2095 for (i=0 ; i<8 ; i++)
2097 // try pushing a little in an axial direction
2100 case 0: dir[0] = 2; dir[1] = 0; break;
2101 case 1: dir[0] = 0; dir[1] = 2; break;
2102 case 2: dir[0] = -2; dir[1] = 0; break;
2103 case 3: dir[0] = 0; dir[1] = -2; break;
2104 case 4: dir[0] = 2; dir[1] = 2; break;
2105 case 5: dir[0] = -2; dir[1] = 2; break;
2106 case 6: dir[0] = 2; dir[1] = -2; break;
2107 case 7: dir[0] = -2; dir[1] = -2; break;
2110 SV_PushEntity (&trace, ent, dir, false, true);
2112 // retry the original move
2113 ent->fields.server->velocity[0] = oldvel[0];
2114 ent->fields.server->velocity[1] = oldvel[1];
2115 ent->fields.server->velocity[2] = 0;
2116 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2118 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2119 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2121 Con_DPrint("TryUnstick - success.\n");
2125 // go back to the original pos and try again
2126 VectorCopy (oldorg, ent->fields.server->origin);
2130 VectorClear (ent->fields.server->velocity);
2131 Con_DPrint("TryUnstick - failure.\n");
2137 =====================
2140 Only used by players
2141 ======================
2143 void SV_WalkMove (prvm_edict_t *ent)
2145 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2146 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2147 trace_t downtrace, trace;
2148 qboolean applygravity;
2150 // if frametime is 0 (due to client sending the same timestamp twice),
2152 if (sv.frametime <= 0)
2155 SV_CheckStuck (ent);
2157 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2159 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2161 SV_CheckVelocity(ent);
2163 // do a regular slide move unless it looks like you ran into a step
2164 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2166 VectorCopy (ent->fields.server->origin, start_origin);
2167 VectorCopy (ent->fields.server->velocity, start_velocity);
2169 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2171 // if the move did not hit the ground at any point, we're not on ground
2173 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2175 SV_CheckVelocity(ent);
2177 SV_LinkEdict_TouchAreaGrid(ent);
2179 if(clip & 8) // teleport
2182 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2185 if (sv_nostep.integer)
2188 VectorCopy(ent->fields.server->origin, originalmove_origin);
2189 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2190 originalmove_clip = clip;
2191 originalmove_flags = (int)ent->fields.server->flags;
2192 originalmove_groundentity = ent->fields.server->groundentity;
2194 // if move didn't block on a step, return
2197 // if move was not trying to move into the step, return
2198 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2201 if (ent->fields.server->movetype != MOVETYPE_FLY)
2203 // return if gibbed by a trigger
2204 if (ent->fields.server->movetype != MOVETYPE_WALK)
2207 // only step up while jumping if that is enabled
2208 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2209 if (!oldonground && ent->fields.server->waterlevel == 0)
2213 // try moving up and forward to go up a step
2214 // back to start pos
2215 VectorCopy (start_origin, ent->fields.server->origin);
2216 VectorCopy (start_velocity, ent->fields.server->velocity);
2219 VectorClear (upmove);
2220 upmove[2] = sv_stepheight.value;
2221 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2223 // we got teleported when upstepping... must abort the move
2228 ent->fields.server->velocity[2] = 0;
2229 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2230 ent->fields.server->velocity[2] += start_velocity[2];
2233 // we got teleported when upstepping... must abort the move
2234 // note that z velocity handling may not be what QC expects here, but we cannot help it
2238 SV_CheckVelocity(ent);
2240 SV_LinkEdict_TouchAreaGrid(ent);
2242 // check for stuckness, possibly due to the limited precision of floats
2243 // in the clipping hulls
2245 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2246 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2248 //Con_Printf("wall\n");
2249 // stepping up didn't make any progress, revert to original move
2250 VectorCopy(originalmove_origin, ent->fields.server->origin);
2251 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2252 //clip = originalmove_clip;
2253 ent->fields.server->flags = originalmove_flags;
2254 ent->fields.server->groundentity = originalmove_groundentity;
2255 // now try to unstick if needed
2256 //clip = SV_TryUnstick (ent, oldvel);
2260 //Con_Printf("step - ");
2262 // extra friction based on view angle
2263 if (clip & 2 && sv_wallfriction.integer)
2264 SV_WallFriction (ent, stepnormal);
2266 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2267 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))
2271 VectorClear (downmove);
2272 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2273 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2275 // we got teleported when downstepping... must abort the move
2279 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2281 // this has been disabled so that you can't jump when you are stepping
2282 // up while already jumping (also known as the Quake2 double jump bug)
2284 // LordHavoc: disabled this check so you can walk on monsters/players
2285 //if (ent->fields.server->solid == SOLID_BSP)
2287 //Con_Printf("onground\n");
2288 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2289 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2295 //Con_Printf("slope\n");
2296 // if the push down didn't end up on good ground, use the move without
2297 // the step up. This happens near wall / slope combinations, and can
2298 // cause the player to hop up higher on a slope too steep to climb
2299 VectorCopy(originalmove_origin, ent->fields.server->origin);
2300 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2301 //clip = originalmove_clip;
2302 ent->fields.server->flags = originalmove_flags;
2303 ent->fields.server->groundentity = originalmove_groundentity;
2306 SV_CheckVelocity(ent);
2308 SV_LinkEdict_TouchAreaGrid(ent);
2311 //============================================================================
2317 Entities that are "stuck" to another entity
2320 void SV_Physics_Follow (prvm_edict_t *ent)
2322 vec3_t vf, vr, vu, angles, v;
2326 if (!SV_RunThink (ent))
2329 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2330 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2331 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])
2333 // quick case for no rotation
2334 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2338 angles[0] = -ent->fields.server->punchangle[0];
2339 angles[1] = ent->fields.server->punchangle[1];
2340 angles[2] = ent->fields.server->punchangle[2];
2341 AngleVectors (angles, vf, vr, vu);
2342 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];
2343 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];
2344 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];
2345 angles[0] = -e->fields.server->angles[0];
2346 angles[1] = e->fields.server->angles[1];
2347 angles[2] = e->fields.server->angles[2];
2348 AngleVectors (angles, vf, vr, vu);
2349 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2350 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2351 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2353 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2355 //SV_LinkEdict_TouchAreaGrid(ent);
2359 ==============================================================================
2363 ==============================================================================
2368 SV_CheckWaterTransition
2372 void SV_CheckWaterTransition (prvm_edict_t *ent)
2375 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2376 if (!ent->fields.server->watertype)
2378 // just spawned here
2379 ent->fields.server->watertype = cont;
2380 ent->fields.server->waterlevel = 1;
2384 // DRESK - Support for Entity Contents Transition Event
2385 // NOTE: Call here BEFORE updating the watertype below,
2386 // and suppress watersplash sound if a valid function
2387 // call was made to allow for custom "splash" sounds.
2388 if( !SV_CheckContentsTransition(ent, cont) )
2389 { // Contents Transition Function Invalid; Potentially Play Water Sound
2390 // check if the entity crossed into or out of water
2391 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2392 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2395 if (cont <= CONTENTS_WATER)
2397 ent->fields.server->watertype = cont;
2398 ent->fields.server->waterlevel = 1;
2402 ent->fields.server->watertype = CONTENTS_EMPTY;
2403 ent->fields.server->waterlevel = 0;
2411 Toss, bounce, and fly movement. When onground, do nothing.
2414 void SV_Physics_Toss (prvm_edict_t *ent)
2421 // if onground, return without moving
2422 if ((int)ent->fields.server->flags & FL_ONGROUND)
2424 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2426 // don't stick to ground if onground and moving upward
2427 ent->fields.server->flags -= FL_ONGROUND;
2429 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2431 // we can trust FL_ONGROUND if groundentity is world because it never moves
2434 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2436 // if ent was supported by a brush model on previous frame,
2437 // and groundentity is now freed, set groundentity to 0 (world)
2438 // which leaves it suspended in the air
2439 ent->fields.server->groundentity = 0;
2440 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2444 ent->priv.server->suspendedinairflag = false;
2446 SV_CheckVelocity (ent);
2449 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2450 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2453 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2455 movetime = sv.frametime;
2456 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2459 VectorScale (ent->fields.server->velocity, movetime, move);
2460 if(!SV_PushEntity (&trace, ent, move, true, true))
2461 return; // teleported
2462 if (ent->priv.server->free)
2464 if (trace.bmodelstartsolid)
2466 // try to unstick the entity
2467 SV_UnstickEntity(ent);
2468 if(!SV_PushEntity (&trace, ent, move, false, true))
2469 return; // teleported
2470 if (ent->priv.server->free)
2473 if (trace.fraction == 1)
2475 movetime *= 1 - min(1, trace.fraction);
2476 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2479 float bouncefactor = 1.0f;
2480 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2481 if (val!=0 && val->_float)
2482 bouncefactor = val->_float;
2484 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2485 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2487 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2489 float d, ent_gravity;
2491 float bouncefactor = 0.5f;
2492 float bouncestop = 60.0f / 800.0f;
2494 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2495 if (val!=0 && val->_float)
2496 bouncefactor = val->_float;
2498 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2499 if (val!=0 && val->_float)
2500 bouncestop = val->_float;
2502 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2503 // LordHavoc: fixed grenades not bouncing when fired down a slope
2504 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2505 if (val!=0 && val->_float)
2506 ent_gravity = val->_float;
2509 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2511 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2512 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2514 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2515 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2516 VectorClear (ent->fields.server->velocity);
2517 VectorClear (ent->fields.server->avelocity);
2520 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2524 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2526 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2527 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2528 VectorClear (ent->fields.server->velocity);
2529 VectorClear (ent->fields.server->avelocity);
2532 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2537 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2538 if (trace.plane.normal[2] > 0.7)
2540 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2541 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2542 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2543 ent->priv.server->suspendedinairflag = true;
2544 VectorClear (ent->fields.server->velocity);
2545 VectorClear (ent->fields.server->avelocity);
2548 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2550 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2554 // check for in water
2555 SV_CheckWaterTransition (ent);
2559 ===============================================================================
2563 ===============================================================================
2570 Monsters freefall when they don't have a ground entity, otherwise
2571 all movement is done with discrete steps.
2573 This is also used for objects that have become still on the ground, but
2574 will fall if the floor is pulled out from under them.
2577 void SV_Physics_Step (prvm_edict_t *ent)
2579 int flags = (int)ent->fields.server->flags;
2582 // Backup Velocity in the event that movetypesteplandevent is called,
2583 // to provide a parameter with the entity's velocity at impact.
2584 prvm_eval_t *movetypesteplandevent;
2585 vec3_t backupVelocity;
2586 VectorCopy(ent->fields.server->velocity, backupVelocity);
2587 // don't fall at all if fly/swim
2588 if (!(flags & (FL_FLY | FL_SWIM)))
2590 if (flags & FL_ONGROUND)
2592 // freefall if onground and moving upward
2593 // freefall if not standing on a world surface (it may be a lift or trap door)
2594 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2596 ent->fields.server->flags -= FL_ONGROUND;
2597 SV_CheckVelocity(ent);
2598 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2600 SV_LinkEdict_TouchAreaGrid(ent);
2601 ent->priv.server->waterposition_forceupdate = true;
2606 // freefall if not onground
2607 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2609 SV_CheckVelocity(ent);
2610 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2612 SV_LinkEdict_TouchAreaGrid(ent);
2615 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2617 // DRESK - Check for Entity Land Event Function
2618 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2620 if(movetypesteplandevent->function)
2621 { // Valid Function; Execute
2622 // Prepare Parameters
2623 // Assign Velocity at Impact
2624 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2625 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2626 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2628 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2629 // Execute VM Function
2630 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2633 // Check for Engine Landing Sound
2634 if(sv_sound_land.string)
2635 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2637 ent->priv.server->waterposition_forceupdate = true;
2642 if (!SV_RunThink(ent))
2645 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2647 ent->priv.server->waterposition_forceupdate = false;
2648 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2649 SV_CheckWaterTransition(ent);
2653 //============================================================================
2655 static void SV_Physics_Entity (prvm_edict_t *ent)
2657 // don't run think/move on newly spawned projectiles as it messes up
2658 // movement interpolation and rocket trails, and is inconsistent with
2659 // respect to entities spawned in the same frame
2660 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2661 // but if it spawns a lower numbered ent, it doesn't - this never moves
2662 // ents in the first frame regardless)
2663 qboolean runmove = ent->priv.server->move;
2664 ent->priv.server->move = true;
2665 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2667 switch ((int) ent->fields.server->movetype)
2670 case MOVETYPE_FAKEPUSH:
2671 SV_Physics_Pusher (ent);
2674 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2675 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2678 case MOVETYPE_FOLLOW:
2679 SV_Physics_Follow (ent);
2681 case MOVETYPE_NOCLIP:
2682 if (SV_RunThink(ent))
2685 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2686 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2691 SV_Physics_Step (ent);
2694 if (SV_RunThink (ent))
2698 case MOVETYPE_BOUNCE:
2699 case MOVETYPE_BOUNCEMISSILE:
2700 case MOVETYPE_FLYMISSILE:
2703 if (SV_RunThink (ent))
2704 SV_Physics_Toss (ent);
2706 case MOVETYPE_PHYSICS:
2707 if (SV_RunThink(ent))
2710 SV_LinkEdict_TouchAreaGrid(ent);
2714 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2719 void SV_Physics_ClientMove(void)
2722 ent = host_client->edict;
2724 // call player physics, this needs the proper frametime
2725 prog->globals.server->frametime = sv.frametime;
2728 // call standard client pre-think, with frametime = 0
2729 prog->globals.server->time = sv.time;
2730 prog->globals.server->frametime = 0;
2731 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2732 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2733 prog->globals.server->frametime = sv.frametime;
2735 // make sure the velocity is sane (not a NaN)
2736 SV_CheckVelocity(ent);
2738 // perform MOVETYPE_WALK behavior
2741 // call standard player post-think, with frametime = 0
2742 prog->globals.server->time = sv.time;
2743 prog->globals.server->frametime = 0;
2744 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2745 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2746 prog->globals.server->frametime = sv.frametime;
2748 if(ent->fields.server->fixangle)
2750 // angle fixing was requested by physics code...
2751 // so store the current angles for later use
2752 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2753 host_client->fixangle_angles_set = TRUE;
2755 // and clear fixangle for the next frame
2756 ent->fields.server->fixangle = 0;
2760 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2762 // don't do physics on disconnected clients, FrikBot relies on this
2763 if (!host_client->spawned)
2766 // make sure the velocity is sane (not a NaN)
2767 SV_CheckVelocity(ent);
2769 // don't run physics here if running asynchronously
2770 if (host_client->clmovement_inputtimeout <= 0)
2773 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2776 // make sure the velocity is still sane (not a NaN)
2777 SV_CheckVelocity(ent);
2779 // call standard client pre-think
2780 prog->globals.server->time = sv.time;
2781 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2782 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2784 // make sure the velocity is still sane (not a NaN)
2785 SV_CheckVelocity(ent);
2788 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2790 // don't do physics on disconnected clients, FrikBot relies on this
2791 if (!host_client->spawned)
2794 // make sure the velocity is sane (not a NaN)
2795 SV_CheckVelocity(ent);
2797 // call standard player post-think
2798 prog->globals.server->time = sv.time;
2799 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2800 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2802 // make sure the velocity is still sane (not a NaN)
2803 SV_CheckVelocity(ent);
2805 if(ent->fields.server->fixangle)
2807 // angle fixing was requested by physics code...
2808 // so store the current angles for later use
2809 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2810 host_client->fixangle_angles_set = TRUE;
2812 // and clear fixangle for the next frame
2813 ent->fields.server->fixangle = 0;
2816 // decrement the countdown variable used to decide when to go back to
2817 // synchronous physics
2818 if (host_client->clmovement_inputtimeout > sv.frametime)
2819 host_client->clmovement_inputtimeout -= sv.frametime;
2821 host_client->clmovement_inputtimeout = 0;
2824 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2826 // don't do physics on disconnected clients, FrikBot relies on this
2827 if (!host_client->spawned)
2829 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2833 // make sure the velocity is sane (not a NaN)
2834 SV_CheckVelocity(ent);
2836 switch ((int) ent->fields.server->movetype)
2839 case MOVETYPE_FAKEPUSH:
2840 SV_Physics_Pusher (ent);
2843 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2844 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2847 case MOVETYPE_FOLLOW:
2848 SV_Physics_Follow (ent);
2850 case MOVETYPE_NOCLIP:
2853 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2854 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2857 SV_Physics_Step (ent);
2861 // don't run physics here if running asynchronously
2862 if (host_client->clmovement_inputtimeout <= 0)
2866 case MOVETYPE_BOUNCE:
2867 case MOVETYPE_BOUNCEMISSILE:
2868 case MOVETYPE_FLYMISSILE:
2871 SV_Physics_Toss (ent);
2877 case MOVETYPE_PHYSICS:
2881 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2885 SV_CheckVelocity (ent);
2888 SV_LinkEdict_TouchAreaGrid(ent);
2890 SV_CheckVelocity (ent);
2899 void SV_Physics (void)
2904 // let the progs know that a new frame has started
2905 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2906 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2907 prog->globals.server->time = sv.time;
2908 prog->globals.server->frametime = sv.frametime;
2909 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2911 // run physics engine
2912 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2915 // treat each object in turn
2918 // if force_retouch, relink all the entities
2919 if (prog->globals.server->force_retouch > 0)
2920 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2921 if (!ent->priv.server->free)
2922 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2924 if (sv_gameplayfix_consistentplayerprethink.integer)
2926 // run physics on the client entities in 3 stages
2927 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2928 if (!ent->priv.server->free)
2929 SV_Physics_ClientEntity_PreThink(ent);
2931 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2932 if (!ent->priv.server->free)
2933 SV_Physics_ClientEntity(ent);
2935 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2936 if (!ent->priv.server->free)
2937 SV_Physics_ClientEntity_PostThink(ent);
2941 // run physics on the client entities
2942 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)
2946 SV_Physics_ClientEntity_PreThink(ent);
2947 SV_Physics_ClientEntity(ent);
2948 SV_Physics_ClientEntity_PostThink(ent);
2953 // run physics on all the non-client entities
2954 if (!sv_freezenonclients.integer)
2956 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2957 if (!ent->priv.server->free)
2958 SV_Physics_Entity(ent);
2959 // make a second pass to see if any ents spawned this frame and make
2960 // sure they run their move/think
2961 if (sv_gameplayfix_delayprojectiles.integer < 0)
2962 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2963 if (!ent->priv.server->move && !ent->priv.server->free)
2964 SV_Physics_Entity(ent);
2967 if (prog->globals.server->force_retouch > 0)
2968 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2970 // LordHavoc: endframe support
2971 if (prog->funcoffsets.EndFrame)
2973 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2974 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2975 prog->globals.server->time = sv.time;
2976 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2979 // decrement prog->num_edicts if the highest number entities died
2980 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2982 if (!sv_freezenonclients.integer)
2983 sv.time += sv.frametime;