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)
50 (model = SV_GetModelFromEdict(ent))
52 model->type == mod_alias
55 (((unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
57 ((gamemode == GAME_TENEBRAE) && ((unsigned int)ent->fields.server->effects & (16 | 32)))
65 ===============================================================================
69 ===============================================================================
72 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
77 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
78 if (val && val->_float)
79 return (int)val->_float;
80 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
82 if ((int)passedict->fields.server->flags & FL_MONSTER)
83 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
85 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
87 else if (passedict->fields.server->solid == SOLID_CORPSE)
88 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
89 else if (passedict->fields.server->solid == SOLID_TRIGGER)
90 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
92 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
95 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
103 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
105 int i, bodysupercontents;
108 prvm_edict_t *traceowner, *touch;
110 // bounding box of entire move area
111 vec3_t clipboxmins, clipboxmaxs;
112 // size when clipping against monsters
113 vec3_t clipmins2, clipmaxs2;
114 // start and end origin of move
118 // matrices to transform into/out of other entity's space
119 matrix4x4_t matrix, imatrix;
120 // model of other entity
122 // list of entities to test for collisions
124 static prvm_edict_t *touchedicts[MAX_EDICTS];
126 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
128 VectorCopy(start, clipstart);
129 VectorClear(clipmins2);
130 VectorClear(clipmaxs2);
131 #if COLLISIONPARANOID >= 3
132 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
136 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
137 cliptrace.bmodelstartsolid = cliptrace.startsolid;
138 if (cliptrace.startsolid || cliptrace.fraction < 1)
139 cliptrace.ent = prog->edicts;
140 if (type == MOVE_WORLDONLY)
143 if (type == MOVE_MISSILE)
145 // LordHavoc: modified this, was = -15, now -= 15
146 for (i = 0;i < 3;i++)
153 // create the bounding box of the entire move
154 for (i = 0;i < 3;i++)
156 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
157 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
160 // debug override to test against everything
161 if (sv_debugmove.integer)
163 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
164 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
167 // if the passedict is world, make it NULL (to avoid two checks each time)
168 if (passedict == prog->edicts)
170 // precalculate prog value for passedict for comparisons
171 passedictprog = PRVM_EDICT_TO_PROG(passedict);
172 // precalculate passedict's owner edict pointer for comparisons
173 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
176 // because this uses World_EntitiestoBox, we know all entity boxes overlap
177 // the clip region, so we can skip culling checks in the loop below
178 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
179 if (numtouchedicts > MAX_EDICTS)
181 // this never happens
182 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
183 numtouchedicts = MAX_EDICTS;
185 for (i = 0;i < numtouchedicts;i++)
187 touch = touchedicts[i];
189 if (touch->fields.server->solid < SOLID_BBOX)
191 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
196 // don't clip against self
197 if (passedict == touch)
199 // don't clip owned entities against owner
200 if (traceowner == touch)
202 // don't clip owner against owned entities
203 if (passedictprog == touch->fields.server->owner)
205 // don't clip points against points (they can't collide)
206 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
210 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
212 // might interact, so do an exact clip
214 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
216 model = SV_GetModelFromEdict(touch);
217 pitchsign = SV_GetPitchSign(touch);
220 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
222 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
223 Matrix4x4_Invert_Simple(&imatrix, &matrix);
224 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
225 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
226 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
227 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
228 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
230 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
232 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
244 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
245 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
247 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
250 int i, bodysupercontents;
253 prvm_edict_t *traceowner, *touch;
255 // bounding box of entire move area
256 vec3_t clipboxmins, clipboxmaxs;
257 // size when clipping against monsters
258 vec3_t clipmins2, clipmaxs2;
259 // start and end origin of move
260 vec3_t clipstart, clipend;
263 // matrices to transform into/out of other entity's space
264 matrix4x4_t matrix, imatrix;
265 // model of other entity
267 // list of entities to test for collisions
269 static prvm_edict_t *touchedicts[MAX_EDICTS];
270 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
274 if (VectorCompare(start, pEnd))
275 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
277 if(collision_endposnudge.value > 0)
279 // TRICK: make the trace 1 qu longer!
280 VectorSubtract(pEnd, start, end);
281 len = VectorNormalizeLength(end);
282 VectorMA(pEnd, collision_endposnudge.value, end, end);
285 VectorCopy(pEnd, end);
287 if (VectorCompare(start, end))
288 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
291 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
293 VectorCopy(start, clipstart);
294 VectorCopy(end, clipend);
295 VectorClear(clipmins2);
296 VectorClear(clipmaxs2);
297 #if COLLISIONPARANOID >= 3
298 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
302 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, false);
303 cliptrace.bmodelstartsolid = cliptrace.startsolid;
304 if (cliptrace.startsolid || cliptrace.fraction < 1)
305 cliptrace.ent = prog->edicts;
306 if (type == MOVE_WORLDONLY)
309 if (type == MOVE_MISSILE)
311 // LordHavoc: modified this, was = -15, now -= 15
312 for (i = 0;i < 3;i++)
319 // create the bounding box of the entire move
320 for (i = 0;i < 3;i++)
322 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
323 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
326 // debug override to test against everything
327 if (sv_debugmove.integer)
329 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
330 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
333 // if the passedict is world, make it NULL (to avoid two checks each time)
334 if (passedict == prog->edicts)
336 // precalculate prog value for passedict for comparisons
337 passedictprog = PRVM_EDICT_TO_PROG(passedict);
338 // precalculate passedict's owner edict pointer for comparisons
339 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
342 // because this uses World_EntitiestoBox, we know all entity boxes overlap
343 // the clip region, so we can skip culling checks in the loop below
344 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
345 if (numtouchedicts > MAX_EDICTS)
347 // this never happens
348 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
349 numtouchedicts = MAX_EDICTS;
351 for (i = 0;i < numtouchedicts;i++)
353 touch = touchedicts[i];
355 if (touch->fields.server->solid < SOLID_BBOX)
357 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
362 // don't clip against self
363 if (passedict == touch)
365 // don't clip owned entities against owner
366 if (traceowner == touch)
368 // don't clip owner against owned entities
369 if (passedictprog == touch->fields.server->owner)
371 // don't clip points against points (they can't collide)
372 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
376 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
378 // might interact, so do an exact clip
380 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
382 model = SV_GetModelFromEdict(touch);
383 pitchsign = SV_GetPitchSign(touch);
386 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
388 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
389 Matrix4x4_Invert_Simple(&imatrix, &matrix);
390 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
391 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
392 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
393 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
394 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
396 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, false);
398 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
402 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
403 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
404 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
414 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
415 #if COLLISIONPARANOID >= 1
416 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
418 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
421 #if COLLISIONPARANOID >= 1
422 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
424 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
428 vec3_t hullmins, hullmaxs;
429 int i, bodysupercontents;
433 prvm_edict_t *traceowner, *touch;
435 // bounding box of entire move area
436 vec3_t clipboxmins, clipboxmaxs;
437 // size of the moving object
438 vec3_t clipmins, clipmaxs;
439 // size when clipping against monsters
440 vec3_t clipmins2, clipmaxs2;
441 // start and end origin of move
442 vec3_t clipstart, clipend;
445 // matrices to transform into/out of other entity's space
446 matrix4x4_t matrix, imatrix;
447 // model of other entity
449 // list of entities to test for collisions
451 static prvm_edict_t *touchedicts[MAX_EDICTS];
452 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
456 if (VectorCompare(mins, maxs))
458 vec3_t shiftstart, shiftend;
459 VectorAdd(start, mins, shiftstart);
460 VectorAdd(pEnd, mins, shiftend);
461 if (VectorCompare(start, pEnd))
462 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
464 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
465 VectorSubtract(trace.endpos, mins, trace.endpos);
469 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
471 // TRICK: make the trace 1 qu longer!
472 VectorSubtract(pEnd, start, end);
473 len = VectorNormalizeLength(end);
474 VectorMA(pEnd, collision_endposnudge.value, end, end);
477 VectorCopy(pEnd, end);
479 if (VectorCompare(mins, maxs))
481 vec3_t shiftstart, shiftend;
482 VectorAdd(start, mins, shiftstart);
483 VectorAdd(end, mins, shiftend);
484 if (VectorCompare(start, end))
485 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
487 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
488 VectorSubtract(trace.endpos, mins, trace.endpos);
493 VectorCopy(start, clipstart);
494 VectorCopy(end, clipend);
495 VectorCopy(mins, clipmins);
496 VectorCopy(maxs, clipmaxs);
497 VectorCopy(mins, clipmins2);
498 VectorCopy(maxs, clipmaxs2);
499 #if COLLISIONPARANOID >= 3
500 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
504 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
505 cliptrace.bmodelstartsolid = cliptrace.startsolid;
506 if (cliptrace.startsolid || cliptrace.fraction < 1)
507 cliptrace.ent = prog->edicts;
508 if (type == MOVE_WORLDONLY)
511 if (type == MOVE_MISSILE)
513 // LordHavoc: modified this, was = -15, now -= 15
514 for (i = 0;i < 3;i++)
521 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
522 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
523 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
526 VectorCopy(clipmins, hullmins);
527 VectorCopy(clipmaxs, hullmaxs);
530 // create the bounding box of the entire move
531 for (i = 0;i < 3;i++)
533 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
534 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
537 // debug override to test against everything
538 if (sv_debugmove.integer)
540 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
541 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
544 // if the passedict is world, make it NULL (to avoid two checks each time)
545 if (passedict == prog->edicts)
547 // precalculate prog value for passedict for comparisons
548 passedictprog = PRVM_EDICT_TO_PROG(passedict);
549 // figure out whether this is a point trace for comparisons
550 pointtrace = VectorCompare(clipmins, clipmaxs);
551 // precalculate passedict's owner edict pointer for comparisons
552 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
555 // because this uses World_EntitiestoBox, we know all entity boxes overlap
556 // the clip region, so we can skip culling checks in the loop below
557 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
558 if (numtouchedicts > MAX_EDICTS)
560 // this never happens
561 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
562 numtouchedicts = MAX_EDICTS;
564 for (i = 0;i < numtouchedicts;i++)
566 touch = touchedicts[i];
568 if (touch->fields.server->solid < SOLID_BBOX)
570 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
575 // don't clip against self
576 if (passedict == touch)
578 // don't clip owned entities against owner
579 if (traceowner == touch)
581 // don't clip owner against owned entities
582 if (passedictprog == touch->fields.server->owner)
584 // don't clip points against points (they can't collide)
585 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
589 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
591 // might interact, so do an exact clip
593 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
595 model = SV_GetModelFromEdict(touch);
596 pitchsign = SV_GetPitchSign(touch);
599 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
601 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
602 Matrix4x4_Invert_Simple(&imatrix, &matrix);
603 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
604 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
605 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
606 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
607 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
609 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
611 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
615 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
616 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
617 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
622 #if COLLISIONPARANOID >= 1
623 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
628 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
631 VectorCopy(trace.endpos, temp);
632 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
633 #if COLLISIONPARANOID < 3
634 if (trace.startsolid || endstuck)
636 Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "^3" : "", passedict ? (int)(passedict - prog->edicts) : -1, passedict->fields.server->origin[0], passedict->fields.server->origin[1], passedict->fields.server->origin[2], end[0] - passedict->fields.server->origin[0], end[1] - passedict->fields.server->origin[1], end[2] - passedict->fields.server->origin[2], trace.fraction, trace.endpos[0] - passedict->fields.server->origin[0], trace.endpos[1] - passedict->fields.server->origin[1], trace.endpos[2] - passedict->fields.server->origin[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
642 int SV_PointSuperContents(const vec3_t point)
644 int supercontents = 0;
648 // matrices to transform into/out of other entity's space
649 matrix4x4_t matrix, imatrix;
650 // model of other entity
653 // list of entities to test for collisions
655 static prvm_edict_t *touchedicts[MAX_EDICTS];
657 // get world supercontents at this point
658 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
659 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
661 // if sv_gameplayfix_swiminbmodels is off we're done
662 if (!sv_gameplayfix_swiminbmodels.integer)
663 return supercontents;
665 // get list of entities at this point
666 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
667 if (numtouchedicts > MAX_EDICTS)
669 // this never happens
670 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
671 numtouchedicts = MAX_EDICTS;
673 for (i = 0;i < numtouchedicts;i++)
675 touch = touchedicts[i];
677 // we only care about SOLID_BSP for pointcontents
678 if (touch->fields.server->solid != SOLID_BSP)
681 // might interact, so do an exact clip
682 model = SV_GetModelFromEdict(touch);
683 if (!model || !model->PointSuperContents)
685 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
686 Matrix4x4_Invert_Simple(&imatrix, &matrix);
687 Matrix4x4_Transform(&imatrix, point, transformed);
688 frame = (int)touch->fields.server->frame;
689 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
692 return supercontents;
696 ===============================================================================
698 Linking entities into the world culling system
700 ===============================================================================
703 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
706 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
707 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
708 prog->globals.server->time = sv.time;
709 prog->globals.server->trace_allsolid = false;
710 prog->globals.server->trace_startsolid = false;
711 prog->globals.server->trace_fraction = 1;
712 prog->globals.server->trace_inwater = false;
713 prog->globals.server->trace_inopen = true;
714 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
715 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
716 prog->globals.server->trace_plane_dist = 0;
717 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
718 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
720 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
722 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
724 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
726 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
729 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
731 int i, numtouchedicts, old_self, old_other;
733 static prvm_edict_t *touchedicts[MAX_EDICTS];
735 if (ent == prog->edicts)
736 return; // don't add the world
738 if (ent->priv.server->free)
741 if (ent->fields.server->solid == SOLID_NOT)
744 // build a list of edicts to touch, because the link loop can be corrupted
745 // by IncreaseEdicts called during touch functions
746 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
747 if (numtouchedicts > MAX_EDICTS)
749 // this never happens
750 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
751 numtouchedicts = MAX_EDICTS;
754 old_self = prog->globals.server->self;
755 old_other = prog->globals.server->other;
756 for (i = 0;i < numtouchedicts;i++)
758 touch = touchedicts[i];
759 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
761 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
764 prog->globals.server->self = old_self;
765 prog->globals.server->other = old_other;
768 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
772 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
774 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
775 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
776 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
777 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
778 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
779 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
780 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
781 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
782 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
783 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
784 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
785 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
786 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
787 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
788 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
789 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
790 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
791 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
792 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
793 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
794 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
795 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
796 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
805 void SV_LinkEdict (prvm_edict_t *ent)
811 if (ent == prog->edicts)
812 return; // don't add the world
814 if (ent->priv.server->free)
817 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_GetModelByIndex(modelindex);
825 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
826 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
827 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
831 if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
833 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
834 // TODO special handling for spheres?
835 RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs);
836 VectorAdd(ent->fields.server->origin, mins, mins);
837 VectorAdd(ent->fields.server->origin, maxs, maxs);
839 else if (ent->fields.server->solid == SOLID_BSP)
843 if (!model->TraceBox)
844 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
846 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
848 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
849 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
851 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
853 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
854 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
858 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
859 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
864 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
865 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
866 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
871 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
872 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
876 // to make items easier to pick up and allow them to be grabbed off
877 // of shelves, the abs sizes are expanded
879 if ((int)ent->fields.server->flags & FL_ITEM)
890 // because movement is clipped an epsilon away from an actual edge,
891 // we must fully check even when bounding boxes don't quite touch
900 VectorCopy(mins, ent->fields.server->absmin);
901 VectorCopy(maxs, ent->fields.server->absmax);
903 World_LinkEdict(&sv.world, ent, mins, maxs);
907 ===============================================================================
911 ===============================================================================
916 SV_TestEntityPosition
918 returns true if the entity is in solid currently
921 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
926 contents = SV_GenericHitSuperContentsMask(ent);
927 VectorAdd(ent->fields.server->origin, offset, org);
928 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
929 if (trace.startsupercontents & contents)
933 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
935 // q1bsp/hlbsp use hulls and if the entity does not exactly match
936 // a hull size it is incorrectly tested, so this code tries to
937 // 'fix' it slightly...
938 // FIXME: this breaks entities larger than the hull size
941 VectorAdd(org, ent->fields.server->mins, m1);
942 VectorAdd(org, ent->fields.server->maxs, m2);
943 VectorSubtract(m2, m1, s);
944 #define EPSILON (1.0f / 32.0f)
945 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
946 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
947 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
948 for (i = 0;i < 8;i++)
950 v[0] = (i & 1) ? m2[0] : m1[0];
951 v[1] = (i & 2) ? m2[1] : m1[1];
952 v[2] = (i & 4) ? m2[2] : m1[2];
953 if (SV_PointSuperContents(v) & contents)
958 // if the trace found a better position for the entity, move it there
959 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
962 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
963 VectorCopy(trace.endpos, ent->fields.server->origin);
965 // verify if the endpos is REALLY outside solid
966 VectorCopy(trace.endpos, org);
967 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
969 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
971 VectorCopy(org, ent->fields.server->origin);
982 void SV_CheckAllEnts (void)
987 // see if any solid entities are inside the final position
988 check = PRVM_NEXT_EDICT(prog->edicts);
989 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
991 if (check->priv.server->free)
993 if (check->fields.server->movetype == MOVETYPE_PUSH
994 || check->fields.server->movetype == MOVETYPE_NONE
995 || check->fields.server->movetype == MOVETYPE_FOLLOW
996 || check->fields.server->movetype == MOVETYPE_NOCLIP)
999 if (SV_TestEntityPosition (check, vec3_origin))
1000 Con_Print("entity in invalid position\n");
1004 // DRESK - Support for Entity Contents Transition Event
1007 SV_CheckContentsTransition
1009 returns true if entity had a valid contentstransition function call
1012 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
1014 int bValidFunctionCall;
1015 prvm_eval_t *contentstransition;
1017 // Default Valid Function Call to False
1018 bValidFunctionCall = false;
1020 if(ent->fields.server->watertype != nContents)
1021 { // Changed Contents
1022 // Acquire Contents Transition Function from QC
1023 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
1025 if(contentstransition->function)
1026 { // Valid Function; Execute
1027 // Assign Valid Function
1028 bValidFunctionCall = true;
1029 // Prepare Parameters (Original Contents, New Contents)
1030 // Original Contents
1031 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
1033 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1035 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1036 // Execute VM Function
1037 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
1041 // Return if Function Call was Valid
1042 return bValidFunctionCall;
1051 void SV_CheckVelocity (prvm_edict_t *ent)
1059 for (i=0 ; i<3 ; i++)
1061 if (IS_NAN(ent->fields.server->velocity[i]))
1063 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1064 ent->fields.server->velocity[i] = 0;
1066 if (IS_NAN(ent->fields.server->origin[i]))
1068 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1069 ent->fields.server->origin[i] = 0;
1073 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1074 // player_run/player_stand1 does not horribly malfunction if the
1075 // velocity becomes a denormalized float
1076 if (VectorLength2(ent->fields.server->velocity) < 0.0001)
1077 VectorClear(ent->fields.server->velocity);
1079 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1080 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1081 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1083 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1084 ent->fields.server->velocity[0] *= wishspeed;
1085 ent->fields.server->velocity[1] *= wishspeed;
1086 ent->fields.server->velocity[2] *= wishspeed;
1094 Runs thinking code if time. There is some play in the exact time the think
1095 function will be called, because it is called before any movement is done
1096 in a frame. Not used for pushmove objects, because they must be exact.
1097 Returns false if the entity removed itself.
1100 qboolean SV_RunThink (prvm_edict_t *ent)
1104 // don't let things stay in the past.
1105 // it is possible to start that way by a trigger with a local time.
1106 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1109 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1111 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1112 ent->fields.server->nextthink = 0;
1113 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1114 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1115 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1116 // mods often set nextthink to time to cause a think every frame,
1117 // we don't want to loop in that case, so exit if the new nextthink is
1118 // <= the time the qc was told, also exit if it is past the end of the
1120 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1123 return !ent->priv.server->free;
1130 Two entities have touched, so run their touch functions
1133 extern void VM_SetTraceGlobals(const trace_t *trace);
1134 extern sizebuf_t vm_tempstringsbuf;
1135 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1137 int restorevm_tempstringsbuf_cursize;
1138 int old_self, old_other;
1139 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1142 old_self = prog->globals.server->self;
1143 old_other = prog->globals.server->other;
1144 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1146 VM_SetTraceGlobals(trace);
1148 prog->globals.server->time = sv.time;
1149 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1151 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1152 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1153 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1156 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1158 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1159 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1160 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1161 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1162 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1163 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1164 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1166 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1168 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1170 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1172 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1175 prog->globals.server->self = old_self;
1176 prog->globals.server->other = old_other;
1177 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1185 Slide off of the impacting object
1186 returns the blocked flags (1 = floor, 2 = step / wall)
1189 #define STOP_EPSILON 0.1
1190 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1195 backoff = -DotProduct (in, normal) * overbounce;
1196 VectorMA(in, backoff, normal, out);
1198 for (i = 0;i < 3;i++)
1199 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1208 The basic solid body movement clip that slides along multiple planes
1209 Returns the clipflags if the velocity was modified (hit something solid)
1213 8 = teleported by touch method
1214 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1217 static float SV_Gravity (prvm_edict_t *ent);
1218 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1219 #define MAX_CLIP_PLANES 5
1220 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1222 int blocked, bumpcount;
1223 int i, j, numplanes;
1224 float d, time_left, gravity;
1225 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1234 if(sv_gameplayfix_nogravityonground.integer)
1235 if((int)ent->fields.server->flags & FL_ONGROUND)
1236 applygravity = false;
1240 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1242 gravity = SV_Gravity(ent) * 0.5f;
1243 ent->fields.server->velocity[2] -= gravity;
1247 applygravity = false;
1248 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1252 VectorCopy(ent->fields.server->velocity, original_velocity);
1253 VectorCopy(ent->fields.server->velocity, primal_velocity);
1256 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1258 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1261 VectorScale(ent->fields.server->velocity, time_left, push);
1262 if(!SV_PushEntity(&trace, ent, push, false, false))
1264 // we got teleported by a touch function
1265 // let's abort the move
1270 if (trace.fraction == 1)
1272 if (trace.plane.normal[2])
1274 if (trace.plane.normal[2] > 0.7)
1281 Con_Printf ("SV_FlyMove: !trace.ent");
1282 trace.ent = prog->edicts;
1285 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1286 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1289 else if (stepheight)
1291 // step - handle it immediately
1297 //Con_Printf("step %f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1298 VectorSet(steppush, 0, 0, stepheight);
1299 VectorCopy(ent->fields.server->origin, org);
1300 if(!SV_PushEntity(&steptrace, ent, steppush, false, false))
1305 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1306 if(!SV_PushEntity(&steptrace2, ent, push, false, false))
1311 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1312 VectorSet(steppush, 0, 0, org[2] - ent->fields.server->origin[2]);
1313 if(!SV_PushEntity(&steptrace3, ent, steppush, false, false))
1318 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1319 // accept the new position if it made some progress...
1320 if (fabs(ent->fields.server->origin[0] - org[0]) >= 0.03125 || fabs(ent->fields.server->origin[1] - org[1]) >= 0.03125)
1322 //Con_Printf("accepted (delta %f %f %f)\n", ent->fields.server->origin[0] - org[0], ent->fields.server->origin[1] - org[1], ent->fields.server->origin[2] - org[2]);
1324 VectorCopy(ent->fields.server->origin, trace.endpos);
1325 time_left *= 1 - trace.fraction;
1331 //Con_Printf("REJECTED (delta %f %f %f)\n", ent->fields.server->origin[0] - org[0], ent->fields.server->origin[1] - org[1], ent->fields.server->origin[2] - org[2]);
1332 VectorCopy(org, ent->fields.server->origin);
1337 // step - return it to caller
1339 // save the trace for player extrafriction
1341 VectorCopy(trace.plane.normal, stepnormal);
1343 if (trace.fraction >= 0.001)
1345 // actually covered some distance
1346 VectorCopy(ent->fields.server->velocity, original_velocity);
1350 time_left *= 1 - trace.fraction;
1352 // clipped to another plane
1353 if (numplanes >= MAX_CLIP_PLANES)
1355 // this shouldn't really happen
1356 VectorClear(ent->fields.server->velocity);
1362 for (i = 0;i < numplanes;i++)
1363 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1367 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1372 VectorCopy(trace.plane.normal, planes[numplanes]);
1375 // modify original_velocity so it parallels all of the clip planes
1376 for (i = 0;i < numplanes;i++)
1378 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1379 for (j = 0;j < numplanes;j++)
1384 if (DotProduct(new_velocity, planes[j]) < 0)
1394 // go along this plane
1395 VectorCopy(new_velocity, ent->fields.server->velocity);
1399 // go along the crease
1402 VectorClear(ent->fields.server->velocity);
1406 CrossProduct(planes[0], planes[1], dir);
1407 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1408 VectorNormalize(dir);
1409 d = DotProduct(dir, ent->fields.server->velocity);
1410 VectorScale(dir, d, ent->fields.server->velocity);
1413 // if current velocity is against the original velocity,
1414 // stop dead to avoid tiny occilations in sloping corners
1415 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1417 VectorClear(ent->fields.server->velocity);
1422 //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2]);
1425 if ((blocked & 1) == 0 && bumpcount > 1)
1427 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1428 // flag ONGROUND if there's ground under it
1429 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1433 // LordHavoc: this came from QW and allows you to get out of water more easily
1434 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1435 VectorCopy(primal_velocity, ent->fields.server->velocity);
1436 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1437 ent->fields.server->velocity[2] -= gravity;
1447 static float SV_Gravity (prvm_edict_t *ent)
1452 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1453 if (val!=0 && val->_float)
1454 ent_gravity = val->_float;
1457 return ent_gravity * sv_gravity.value * sv.frametime;
1462 ===============================================================================
1466 ===============================================================================
1473 Does not change the entities velocity at all
1474 The trace struct is filled with the trace that has been done.
1475 Returns true if the push did not result in the entity being teleported by QC code.
1478 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1482 vec3_t original, original_velocity;
1485 VectorCopy(ent->fields.server->origin, original);
1486 VectorAdd (ent->fields.server->origin, push, end);
1488 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1489 type = MOVE_MISSILE;
1490 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1491 type = MOVE_NOMONSTERS; // only clip against bmodels
1495 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1497 while (trace->bmodelstartsolid && sv_gameplayfix_nudgeoutofsolid.integer)
1499 vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
1500 VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
1501 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1505 VectorCopy(original, ent->fields.server->origin);
1509 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1512 VectorCopy (trace->endpos, ent->fields.server->origin);
1514 VectorCopy(ent->fields.server->origin, original);
1515 VectorCopy(ent->fields.server->velocity, original_velocity);
1520 if(!trace->startsolid)
1521 if(SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, type, ent, SV_GenericHitSuperContentsMask(ent)).startsolid)
1523 Con_Printf("something eeeeevil happened\n");
1528 SV_LinkEdict_TouchAreaGrid(ent);
1530 if((ent->fields.server->solid >= SOLID_TRIGGER && trace->ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace->ent))))
1531 SV_Impact (ent, trace);
1533 return VectorCompare(ent->fields.server->origin, original) && VectorCompare(ent->fields.server->velocity, original_velocity);
1543 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1546 int pusherowner, pusherprog;
1549 float savesolid, movetime2, pushltime;
1550 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1552 int numcheckentities;
1553 static prvm_edict_t *checkentities[MAX_EDICTS];
1554 dp_model_t *pushermodel;
1555 trace_t trace, trace2;
1556 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1557 static unsigned short moved_edicts[MAX_EDICTS];
1559 if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
1561 pusher->fields.server->ltime += movetime;
1565 switch ((int) pusher->fields.server->solid)
1567 // LordHavoc: valid pusher types
1570 case SOLID_SLIDEBOX:
1571 case SOLID_CORPSE: // LordHavoc: this would be weird...
1573 // LordHavoc: no collisions
1576 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1577 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1578 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1579 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1580 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1581 pusher->fields.server->ltime += movetime;
1582 SV_LinkEdict(pusher);
1585 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1588 index = (int) pusher->fields.server->modelindex;
1589 if (index < 1 || index >= MAX_MODELS)
1591 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1594 pushermodel = SV_GetModelByIndex(index);
1595 pusherowner = pusher->fields.server->owner;
1596 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1598 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1600 movetime2 = movetime;
1601 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1602 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1603 if (moveangle[0] || moveangle[2])
1605 for (i = 0;i < 3;i++)
1609 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1610 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1614 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1615 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1619 else if (moveangle[1])
1621 for (i = 0;i < 3;i++)
1625 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1626 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1630 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1631 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1637 for (i = 0;i < 3;i++)
1641 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1642 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1646 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1647 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1652 VectorNegate (moveangle, a);
1653 AngleVectorsFLU (a, forward, left, up);
1655 VectorCopy (pusher->fields.server->origin, pushorig);
1656 VectorCopy (pusher->fields.server->angles, pushang);
1657 pushltime = pusher->fields.server->ltime;
1659 // move the pusher to its final position
1661 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1662 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1663 pusher->fields.server->ltime += movetime;
1664 SV_LinkEdict(pusher);
1666 pushermodel = SV_GetModelFromEdict(pusher);
1667 Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, pusher->fields.server->origin[0], pusher->fields.server->origin[1], pusher->fields.server->origin[2], pusher->fields.server->angles[0], pusher->fields.server->angles[1], pusher->fields.server->angles[2], 1);
1668 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1670 savesolid = pusher->fields.server->solid;
1672 // see if any solid entities are inside the final position
1675 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1676 for (e = 0;e < numcheckentities;e++)
1678 prvm_edict_t *check = checkentities[e];
1679 int movetype = (int)check->fields.server->movetype;
1684 case MOVETYPE_FOLLOW:
1685 case MOVETYPE_NOCLIP:
1686 case MOVETYPE_FAKEPUSH:
1692 if (check->fields.server->owner == pusherprog)
1695 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1698 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1700 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1701 check->priv.server->waterposition_forceupdate = true;
1703 checkcontents = SV_GenericHitSuperContentsMask(check);
1705 // if the entity is standing on the pusher, it will definitely be moved
1706 // if the entity is not standing on the pusher, but is in the pusher's
1707 // final position, move it
1708 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1710 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1711 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1712 if (!trace.startsolid)
1714 //Con_Printf("- not in solid\n");
1722 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1723 org2[0] = DotProduct (org, forward);
1724 org2[1] = DotProduct (org, left);
1725 org2[2] = DotProduct (org, up);
1726 VectorSubtract (org2, org, move);
1727 VectorAdd (move, move1, move);
1730 VectorCopy (move1, move);
1732 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1734 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1735 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1736 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1738 // physics objects need better collisions than this code can do
1739 if (movetype == MOVETYPE_PHYSICS)
1741 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1742 SV_LinkEdict(check);
1743 SV_LinkEdict_TouchAreaGrid(check);
1747 // try moving the contacted entity
1748 pusher->fields.server->solid = SOLID_NOT;
1749 if(!SV_PushEntity (&trace, check, move, true, true))
1751 // entity "check" got teleported
1752 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1753 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1754 continue; // pushed enough
1756 // FIXME: turn players specially
1757 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1758 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1759 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1761 // this trace.fraction < 1 check causes items to fall off of pushers
1762 // if they pass under or through a wall
1763 // the groundentity check causes items to fall off of ledges
1764 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1765 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1767 // if it is still inside the pusher, block
1768 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1769 if (trace.startsolid)
1771 // try moving the contacted entity a tiny bit further to account for precision errors
1773 pusher->fields.server->solid = SOLID_NOT;
1774 VectorScale(move, 1.1, move2);
1775 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1776 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1777 if(!SV_PushEntity (&trace2, check, move2, true, true))
1779 // entity "check" got teleported
1782 pusher->fields.server->solid = savesolid;
1783 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1784 if (trace.startsolid)
1786 // try moving the contacted entity a tiny bit less to account for precision errors
1787 pusher->fields.server->solid = SOLID_NOT;
1788 VectorScale(move, 0.9, move2);
1789 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1790 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1791 if(!SV_PushEntity (&trace2, check, move2, true, true))
1793 // entity "check" got teleported
1796 pusher->fields.server->solid = savesolid;
1797 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1798 if (trace.startsolid)
1800 // still inside pusher, so it's really blocked
1803 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1805 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1808 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1809 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1813 VectorCopy (pushorig, pusher->fields.server->origin);
1814 VectorCopy (pushang, pusher->fields.server->angles);
1815 pusher->fields.server->ltime = pushltime;
1816 SV_LinkEdict(pusher);
1818 // move back any entities we already moved
1819 for (i = 0;i < num_moved;i++)
1821 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1822 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1823 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1827 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1828 if (pusher->fields.server->blocked)
1830 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1831 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1832 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1839 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1840 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1841 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1850 void SV_Physics_Pusher (prvm_edict_t *ent)
1852 float thinktime, oldltime, movetime;
1854 oldltime = ent->fields.server->ltime;
1856 thinktime = ent->fields.server->nextthink;
1857 if (thinktime < ent->fields.server->ltime + sv.frametime)
1859 movetime = thinktime - ent->fields.server->ltime;
1864 movetime = sv.frametime;
1867 // advances ent->fields.server->ltime if not blocked
1868 SV_PushMove (ent, movetime);
1870 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1872 ent->fields.server->nextthink = 0;
1873 prog->globals.server->time = sv.time;
1874 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1875 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1876 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1882 ===============================================================================
1886 ===============================================================================
1889 static float unstickoffsets[] =
1891 // poutting -/+z changes first as they are least weird
1906 typedef enum unstickresult_e
1914 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1918 // if not stuck in a bmodel, just return
1919 if (!SV_TestEntityPosition(ent, vec3_origin))
1920 return UNSTICK_GOOD;
1922 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1924 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1926 VectorCopy(unstickoffsets + i, offset);
1928 //SV_LinkEdict_TouchAreaGrid(ent);
1929 return UNSTICK_UNSTUCK;
1933 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1934 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1936 for(i = 2; i <= maxunstick; ++i)
1938 VectorClear(offset);
1940 if (!SV_TestEntityPosition(ent, offset))
1943 //SV_LinkEdict_TouchAreaGrid(ent);
1944 return UNSTICK_UNSTUCK;
1947 if (!SV_TestEntityPosition(ent, offset))
1950 //SV_LinkEdict_TouchAreaGrid(ent);
1951 return UNSTICK_UNSTUCK;
1955 return UNSTICK_STUCK;
1958 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1961 switch(SV_UnstickEntityReturnOffset(ent, offset))
1965 case UNSTICK_UNSTUCK:
1966 Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), offset[0], offset[1], offset[2]);
1969 if (developer_extra.integer)
1970 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1973 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1982 This is a big hack to try and fix the rare case of getting stuck in the world
1986 void SV_CheckStuck (prvm_edict_t *ent)
1990 switch(SV_UnstickEntityReturnOffset(ent, offset))
1993 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1995 case UNSTICK_UNSTUCK:
1996 Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), offset[0], offset[1], offset[2]);
1999 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
2000 if (!SV_TestEntityPosition(ent, offset))
2002 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2004 //SV_LinkEdict_TouchAreaGrid(ent);
2007 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2010 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2020 qboolean SV_CheckWater (prvm_edict_t *ent)
2023 int nNativeContents;
2026 point[0] = ent->fields.server->origin[0];
2027 point[1] = ent->fields.server->origin[1];
2028 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2030 // DRESK - Support for Entity Contents Transition Event
2031 // NOTE: Some logic needed to be slightly re-ordered
2032 // to not affect performance and allow for the feature.
2034 // Acquire Super Contents Prior to Resets
2035 cont = SV_PointSuperContents(point);
2036 // Acquire Native Contents Here
2037 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2039 // DRESK - Support for Entity Contents Transition Event
2040 if(ent->fields.server->watertype)
2041 // Entity did NOT Spawn; Check
2042 SV_CheckContentsTransition(ent, nNativeContents);
2045 ent->fields.server->waterlevel = 0;
2046 ent->fields.server->watertype = CONTENTS_EMPTY;
2047 cont = SV_PointSuperContents(point);
2048 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2050 ent->fields.server->watertype = nNativeContents;
2051 ent->fields.server->waterlevel = 1;
2052 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2053 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2055 ent->fields.server->waterlevel = 2;
2056 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2057 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2058 ent->fields.server->waterlevel = 3;
2062 return ent->fields.server->waterlevel > 1;
2071 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2074 vec3_t forward, into, side;
2076 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2077 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2079 // cut the tangential velocity
2080 i = DotProduct (stepnormal, ent->fields.server->velocity);
2081 VectorScale (stepnormal, i, into);
2082 VectorSubtract (ent->fields.server->velocity, into, side);
2083 ent->fields.server->velocity[0] = side[0] * (1 + d);
2084 ent->fields.server->velocity[1] = side[1] * (1 + d);
2090 =====================
2093 Player has come to a dead stop, possibly due to the problem with limited
2094 float precision at some angle joins in the BSP hull.
2096 Try fixing by pushing one pixel in each direction.
2098 This is a hack, but in the interest of good gameplay...
2099 ======================
2101 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2106 VectorCopy (ent->fields.server->origin, oldorg);
2109 for (i=0 ; i<8 ; i++)
2111 // try pushing a little in an axial direction
2114 case 0: dir[0] = 2; dir[1] = 0; break;
2115 case 1: dir[0] = 0; dir[1] = 2; break;
2116 case 2: dir[0] = -2; dir[1] = 0; break;
2117 case 3: dir[0] = 0; dir[1] = -2; break;
2118 case 4: dir[0] = 2; dir[1] = 2; break;
2119 case 5: dir[0] = -2; dir[1] = 2; break;
2120 case 6: dir[0] = 2; dir[1] = -2; break;
2121 case 7: dir[0] = -2; dir[1] = -2; break;
2124 SV_PushEntity (&trace, ent, dir, false, true);
2126 // retry the original move
2127 ent->fields.server->velocity[0] = oldvel[0];
2128 ent->fields.server->velocity[1] = oldvel[1];
2129 ent->fields.server->velocity[2] = 0;
2130 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2132 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2133 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2135 Con_DPrint("TryUnstick - success.\n");
2139 // go back to the original pos and try again
2140 VectorCopy (oldorg, ent->fields.server->origin);
2144 VectorClear (ent->fields.server->velocity);
2145 Con_DPrint("TryUnstick - failure.\n");
2151 =====================
2154 Only used by players
2155 ======================
2157 void SV_WalkMove (prvm_edict_t *ent)
2161 //int originalmove_clip;
2162 int originalmove_flags;
2163 int originalmove_groundentity;
2164 int hitsupercontentsmask;
2166 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2167 trace_t downtrace, trace;
2168 qboolean applygravity;
2170 // if frametime is 0 (due to client sending the same timestamp twice),
2172 if (sv.frametime <= 0)
2175 SV_CheckStuck (ent);
2177 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2179 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2181 SV_CheckVelocity(ent);
2183 // do a regular slide move unless it looks like you ran into a step
2184 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2186 VectorCopy (ent->fields.server->origin, start_origin);
2187 VectorCopy (ent->fields.server->velocity, start_velocity);
2189 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2191 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2194 // only try this if there was no floor in the way in the trace (no,
2195 // this check seems to be not REALLY necessary, because if clip & 1,
2196 // our trace will hit that thing too)
2197 VectorSet(upmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + 1);
2198 VectorSet(downmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
2199 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
2200 type = MOVE_MISSILE;
2201 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
2202 type = MOVE_NOMONSTERS; // only clip against bmodels
2205 trace = SV_TraceBox(upmove, ent->fields.server->mins, ent->fields.server->maxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2206 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2207 clip |= 1; // but we HAVE found a floor
2210 // if the move did not hit the ground at any point, we're not on ground
2212 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2214 SV_CheckVelocity(ent);
2216 SV_LinkEdict_TouchAreaGrid(ent);
2218 if(clip & 8) // teleport
2221 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2224 if (sv_nostep.integer)
2227 VectorCopy(ent->fields.server->origin, originalmove_origin);
2228 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2229 //originalmove_clip = clip;
2230 originalmove_flags = (int)ent->fields.server->flags;
2231 originalmove_groundentity = ent->fields.server->groundentity;
2233 // if move didn't block on a step, return
2236 // if move was not trying to move into the step, return
2237 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2240 if (ent->fields.server->movetype != MOVETYPE_FLY)
2242 // return if gibbed by a trigger
2243 if (ent->fields.server->movetype != MOVETYPE_WALK)
2246 // only step up while jumping if that is enabled
2247 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2248 if (!oldonground && ent->fields.server->waterlevel == 0)
2252 // try moving up and forward to go up a step
2253 // back to start pos
2254 VectorCopy (start_origin, ent->fields.server->origin);
2255 VectorCopy (start_velocity, ent->fields.server->velocity);
2258 VectorClear (upmove);
2259 upmove[2] = sv_stepheight.value;
2260 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2262 // we got teleported when upstepping... must abort the move
2267 ent->fields.server->velocity[2] = 0;
2268 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2269 ent->fields.server->velocity[2] += start_velocity[2];
2272 // we got teleported when upstepping... must abort the move
2273 // note that z velocity handling may not be what QC expects here, but we cannot help it
2277 SV_CheckVelocity(ent);
2279 SV_LinkEdict_TouchAreaGrid(ent);
2281 // check for stuckness, possibly due to the limited precision of floats
2282 // in the clipping hulls
2284 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2285 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2287 //Con_Printf("wall\n");
2288 // stepping up didn't make any progress, revert to original move
2289 VectorCopy(originalmove_origin, ent->fields.server->origin);
2290 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2291 //clip = originalmove_clip;
2292 ent->fields.server->flags = originalmove_flags;
2293 ent->fields.server->groundentity = originalmove_groundentity;
2294 // now try to unstick if needed
2295 //clip = SV_TryUnstick (ent, oldvel);
2299 //Con_Printf("step - ");
2301 // extra friction based on view angle
2302 if (clip & 2 && sv_wallfriction.integer)
2303 SV_WallFriction (ent, stepnormal);
2305 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2306 else if (!sv_gameplayfix_stepdown.integer || ent->fields.server->waterlevel >= 3 || start_velocity[2] >= (1.0 / 32.0) || !oldonground || ((int)ent->fields.server->flags & FL_ONGROUND))
2310 VectorClear (downmove);
2311 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2312 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2314 // we got teleported when downstepping... must abort the move
2318 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2320 // this has been disabled so that you can't jump when you are stepping
2321 // up while already jumping (also known as the Quake2 double jump bug)
2323 // LordHavoc: disabled this check so you can walk on monsters/players
2324 //if (ent->fields.server->solid == SOLID_BSP)
2326 //Con_Printf("onground\n");
2327 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2328 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2334 //Con_Printf("slope\n");
2335 // if the push down didn't end up on good ground, use the move without
2336 // the step up. This happens near wall / slope combinations, and can
2337 // cause the player to hop up higher on a slope too steep to climb
2338 VectorCopy(originalmove_origin, ent->fields.server->origin);
2339 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2340 //clip = originalmove_clip;
2341 ent->fields.server->flags = originalmove_flags;
2342 ent->fields.server->groundentity = originalmove_groundentity;
2345 SV_CheckVelocity(ent);
2347 SV_LinkEdict_TouchAreaGrid(ent);
2350 //============================================================================
2356 Entities that are "stuck" to another entity
2359 void SV_Physics_Follow (prvm_edict_t *ent)
2361 vec3_t vf, vr, vu, angles, v;
2365 if (!SV_RunThink (ent))
2368 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2369 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2370 if (e->fields.server->angles[0] == ent->fields.server->punchangle[0] && e->fields.server->angles[1] == ent->fields.server->punchangle[1] && e->fields.server->angles[2] == ent->fields.server->punchangle[2])
2372 // quick case for no rotation
2373 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2377 angles[0] = -ent->fields.server->punchangle[0];
2378 angles[1] = ent->fields.server->punchangle[1];
2379 angles[2] = ent->fields.server->punchangle[2];
2380 AngleVectors (angles, vf, vr, vu);
2381 v[0] = ent->fields.server->view_ofs[0] * vf[0] + ent->fields.server->view_ofs[1] * vr[0] + ent->fields.server->view_ofs[2] * vu[0];
2382 v[1] = ent->fields.server->view_ofs[0] * vf[1] + ent->fields.server->view_ofs[1] * vr[1] + ent->fields.server->view_ofs[2] * vu[1];
2383 v[2] = ent->fields.server->view_ofs[0] * vf[2] + ent->fields.server->view_ofs[1] * vr[2] + ent->fields.server->view_ofs[2] * vu[2];
2384 angles[0] = -e->fields.server->angles[0];
2385 angles[1] = e->fields.server->angles[1];
2386 angles[2] = e->fields.server->angles[2];
2387 AngleVectors (angles, vf, vr, vu);
2388 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2389 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2390 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2392 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2394 //SV_LinkEdict_TouchAreaGrid(ent);
2398 ==============================================================================
2402 ==============================================================================
2407 SV_CheckWaterTransition
2411 void SV_CheckWaterTransition (prvm_edict_t *ent)
2414 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2415 if (!ent->fields.server->watertype)
2417 // just spawned here
2418 ent->fields.server->watertype = cont;
2419 ent->fields.server->waterlevel = 1;
2423 // DRESK - Support for Entity Contents Transition Event
2424 // NOTE: Call here BEFORE updating the watertype below,
2425 // and suppress watersplash sound if a valid function
2426 // call was made to allow for custom "splash" sounds.
2427 if( !SV_CheckContentsTransition(ent, cont) )
2428 { // Contents Transition Function Invalid; Potentially Play Water Sound
2429 // check if the entity crossed into or out of water
2430 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2431 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2434 if (cont <= CONTENTS_WATER)
2436 ent->fields.server->watertype = cont;
2437 ent->fields.server->waterlevel = 1;
2441 ent->fields.server->watertype = CONTENTS_EMPTY;
2442 ent->fields.server->waterlevel = 0;
2450 Toss, bounce, and fly movement. When onground, do nothing.
2453 void SV_Physics_Toss (prvm_edict_t *ent)
2459 prvm_edict_t *groundentity;
2461 // if onground, return without moving
2462 if ((int)ent->fields.server->flags & FL_ONGROUND)
2464 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2465 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2467 // don't stick to ground if onground and moving upward
2468 ent->fields.server->flags -= FL_ONGROUND;
2470 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2472 // we can trust FL_ONGROUND if groundentity is world because it never moves
2475 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2477 // if ent was supported by a brush model on previous frame,
2478 // and groundentity is now freed, set groundentity to 0 (world)
2479 // which leaves it suspended in the air
2480 ent->fields.server->groundentity = 0;
2481 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2484 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2486 // don't slide if still touching the groundentity
2490 ent->priv.server->suspendedinairflag = false;
2492 SV_CheckVelocity (ent);
2495 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2496 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2499 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2501 movetime = sv.frametime;
2502 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2505 VectorScale (ent->fields.server->velocity, movetime, move);
2506 if(!SV_PushEntity (&trace, ent, move, true, true))
2507 return; // teleported
2508 if (ent->priv.server->free)
2510 if (trace.bmodelstartsolid)
2512 // try to unstick the entity
2513 SV_UnstickEntity(ent);
2514 if(!SV_PushEntity (&trace, ent, move, false, true))
2515 return; // teleported
2516 if (ent->priv.server->free)
2519 if (trace.fraction == 1)
2521 movetime *= 1 - min(1, trace.fraction);
2522 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2525 float bouncefactor = 1.0f;
2526 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2527 if (val!=0 && val->_float)
2528 bouncefactor = val->_float;
2530 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2531 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2533 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2535 float d, ent_gravity;
2537 float bouncefactor = 0.5f;
2538 float bouncestop = 60.0f / 800.0f;
2540 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2541 if (val!=0 && val->_float)
2542 bouncefactor = val->_float;
2544 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2545 if (val!=0 && val->_float)
2546 bouncestop = val->_float;
2548 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2549 // LordHavoc: fixed grenades not bouncing when fired down a slope
2550 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2551 if (val!=0 && val->_float)
2552 ent_gravity = val->_float;
2555 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2557 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2558 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2560 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2561 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2562 VectorClear (ent->fields.server->velocity);
2563 VectorClear (ent->fields.server->avelocity);
2566 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2570 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2572 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2573 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2574 VectorClear (ent->fields.server->velocity);
2575 VectorClear (ent->fields.server->avelocity);
2578 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2583 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2584 if (trace.plane.normal[2] > 0.7)
2586 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2587 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2588 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2589 ent->priv.server->suspendedinairflag = true;
2590 VectorClear (ent->fields.server->velocity);
2591 VectorClear (ent->fields.server->avelocity);
2594 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2596 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2600 // check for in water
2601 SV_CheckWaterTransition (ent);
2605 ===============================================================================
2609 ===============================================================================
2616 Monsters freefall when they don't have a ground entity, otherwise
2617 all movement is done with discrete steps.
2619 This is also used for objects that have become still on the ground, but
2620 will fall if the floor is pulled out from under them.
2623 void SV_Physics_Step (prvm_edict_t *ent)
2625 int flags = (int)ent->fields.server->flags;
2628 // Backup Velocity in the event that movetypesteplandevent is called,
2629 // to provide a parameter with the entity's velocity at impact.
2630 prvm_eval_t *movetypesteplandevent;
2631 vec3_t backupVelocity;
2632 VectorCopy(ent->fields.server->velocity, backupVelocity);
2633 // don't fall at all if fly/swim
2634 if (!(flags & (FL_FLY | FL_SWIM)))
2636 if (flags & FL_ONGROUND)
2638 // freefall if onground and moving upward
2639 // freefall if not standing on a world surface (it may be a lift or trap door)
2640 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2642 ent->fields.server->flags -= FL_ONGROUND;
2643 SV_CheckVelocity(ent);
2644 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2646 SV_LinkEdict_TouchAreaGrid(ent);
2647 ent->priv.server->waterposition_forceupdate = true;
2652 // freefall if not onground
2653 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2655 SV_CheckVelocity(ent);
2656 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2658 SV_LinkEdict_TouchAreaGrid(ent);
2661 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2663 // DRESK - Check for Entity Land Event Function
2664 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2666 if(movetypesteplandevent->function)
2667 { // Valid Function; Execute
2668 // Prepare Parameters
2669 // Assign Velocity at Impact
2670 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2671 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2672 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2674 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2675 // Execute VM Function
2676 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2679 // Check for Engine Landing Sound
2680 if(sv_sound_land.string)
2681 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2683 ent->priv.server->waterposition_forceupdate = true;
2688 if (!SV_RunThink(ent))
2691 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2693 ent->priv.server->waterposition_forceupdate = false;
2694 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2695 SV_CheckWaterTransition(ent);
2699 //============================================================================
2701 static void SV_Physics_Entity (prvm_edict_t *ent)
2703 // don't run think/move on newly spawned projectiles as it messes up
2704 // movement interpolation and rocket trails, and is inconsistent with
2705 // respect to entities spawned in the same frame
2706 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2707 // but if it spawns a lower numbered ent, it doesn't - this never moves
2708 // ents in the first frame regardless)
2709 qboolean runmove = ent->priv.server->move;
2710 ent->priv.server->move = true;
2711 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2713 switch ((int) ent->fields.server->movetype)
2716 case MOVETYPE_FAKEPUSH:
2717 SV_Physics_Pusher (ent);
2720 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2721 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2724 case MOVETYPE_FOLLOW:
2725 SV_Physics_Follow (ent);
2727 case MOVETYPE_NOCLIP:
2728 if (SV_RunThink(ent))
2731 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2732 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2737 SV_Physics_Step (ent);
2740 if (SV_RunThink (ent))
2744 case MOVETYPE_BOUNCE:
2745 case MOVETYPE_BOUNCEMISSILE:
2746 case MOVETYPE_FLYMISSILE:
2749 if (SV_RunThink (ent))
2750 SV_Physics_Toss (ent);
2752 case MOVETYPE_PHYSICS:
2753 if (SV_RunThink(ent))
2756 SV_LinkEdict_TouchAreaGrid(ent);
2760 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2765 void SV_Physics_ClientMove(void)
2768 ent = host_client->edict;
2770 // call player physics, this needs the proper frametime
2771 prog->globals.server->frametime = sv.frametime;
2774 // call standard client pre-think, with frametime = 0
2775 prog->globals.server->time = sv.time;
2776 prog->globals.server->frametime = 0;
2777 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2778 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2779 prog->globals.server->frametime = sv.frametime;
2781 // make sure the velocity is sane (not a NaN)
2782 SV_CheckVelocity(ent);
2784 // perform MOVETYPE_WALK behavior
2787 // call standard player post-think, with frametime = 0
2788 prog->globals.server->time = sv.time;
2789 prog->globals.server->frametime = 0;
2790 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2791 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2792 prog->globals.server->frametime = sv.frametime;
2794 if(ent->fields.server->fixangle)
2796 // angle fixing was requested by physics code...
2797 // so store the current angles for later use
2798 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2799 host_client->fixangle_angles_set = TRUE;
2801 // and clear fixangle for the next frame
2802 ent->fields.server->fixangle = 0;
2806 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2808 // don't do physics on disconnected clients, FrikBot relies on this
2809 if (!host_client->spawned)
2812 // make sure the velocity is sane (not a NaN)
2813 SV_CheckVelocity(ent);
2815 // don't run physics here if running asynchronously
2816 if (host_client->clmovement_inputtimeout <= 0)
2819 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2822 // make sure the velocity is still sane (not a NaN)
2823 SV_CheckVelocity(ent);
2825 // call standard client pre-think
2826 prog->globals.server->time = sv.time;
2827 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2828 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2830 // make sure the velocity is still sane (not a NaN)
2831 SV_CheckVelocity(ent);
2834 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2836 // don't do physics on disconnected clients, FrikBot relies on this
2837 if (!host_client->spawned)
2840 // make sure the velocity is sane (not a NaN)
2841 SV_CheckVelocity(ent);
2843 // call standard player post-think
2844 prog->globals.server->time = sv.time;
2845 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2846 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2848 // make sure the velocity is still sane (not a NaN)
2849 SV_CheckVelocity(ent);
2851 if(ent->fields.server->fixangle)
2853 // angle fixing was requested by physics code...
2854 // so store the current angles for later use
2855 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2856 host_client->fixangle_angles_set = TRUE;
2858 // and clear fixangle for the next frame
2859 ent->fields.server->fixangle = 0;
2862 // decrement the countdown variable used to decide when to go back to
2863 // synchronous physics
2864 if (host_client->clmovement_inputtimeout > sv.frametime)
2865 host_client->clmovement_inputtimeout -= sv.frametime;
2867 host_client->clmovement_inputtimeout = 0;
2870 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2872 // don't do physics on disconnected clients, FrikBot relies on this
2873 if (!host_client->spawned)
2875 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2879 // make sure the velocity is sane (not a NaN)
2880 SV_CheckVelocity(ent);
2882 switch ((int) ent->fields.server->movetype)
2885 case MOVETYPE_FAKEPUSH:
2886 SV_Physics_Pusher (ent);
2889 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2890 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2893 case MOVETYPE_FOLLOW:
2894 SV_Physics_Follow (ent);
2896 case MOVETYPE_NOCLIP:
2899 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2900 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2903 SV_Physics_Step (ent);
2907 // don't run physics here if running asynchronously
2908 if (host_client->clmovement_inputtimeout <= 0)
2912 case MOVETYPE_BOUNCE:
2913 case MOVETYPE_BOUNCEMISSILE:
2914 case MOVETYPE_FLYMISSILE:
2917 SV_Physics_Toss (ent);
2923 case MOVETYPE_PHYSICS:
2927 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2931 SV_CheckVelocity (ent);
2934 SV_LinkEdict_TouchAreaGrid(ent);
2936 SV_CheckVelocity (ent);
2945 void SV_Physics (void)
2950 // let the progs know that a new frame has started
2951 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2952 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2953 prog->globals.server->time = sv.time;
2954 prog->globals.server->frametime = sv.frametime;
2955 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2957 // run physics engine
2958 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2961 // treat each object in turn
2964 // if force_retouch, relink all the entities
2965 if (prog->globals.server->force_retouch > 0)
2966 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2967 if (!ent->priv.server->free)
2968 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2970 if (sv_gameplayfix_consistentplayerprethink.integer)
2972 // run physics on the client entities in 3 stages
2973 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2974 if (!ent->priv.server->free)
2975 SV_Physics_ClientEntity_PreThink(ent);
2977 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2978 if (!ent->priv.server->free)
2979 SV_Physics_ClientEntity(ent);
2981 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2982 if (!ent->priv.server->free)
2983 SV_Physics_ClientEntity_PostThink(ent);
2987 // run physics on the client entities
2988 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2990 if (!ent->priv.server->free)
2992 SV_Physics_ClientEntity_PreThink(ent);
2993 SV_Physics_ClientEntity(ent);
2994 SV_Physics_ClientEntity_PostThink(ent);
2999 // run physics on all the non-client entities
3000 if (!sv_freezenonclients.integer)
3002 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3003 if (!ent->priv.server->free)
3004 SV_Physics_Entity(ent);
3005 // make a second pass to see if any ents spawned this frame and make
3006 // sure they run their move/think
3007 if (sv_gameplayfix_delayprojectiles.integer < 0)
3008 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3009 if (!ent->priv.server->move && !ent->priv.server->free)
3010 SV_Physics_Entity(ent);
3013 if (prog->globals.server->force_retouch > 0)
3014 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
3016 // LordHavoc: endframe support
3017 if (prog->funcoffsets.EndFrame)
3019 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
3020 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
3021 prog->globals.server->time = sv.time;
3022 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
3025 // decrement prog->num_edicts if the highest number entities died
3026 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3028 if (!sv_freezenonclients.integer)
3029 sv.time += sv.frametime;