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);
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);
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 SV_PushEntity(&steptrace, ent, steppush, false, false);
1301 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1302 SV_PushEntity(&steptrace2, ent, push, false, false);
1303 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1304 VectorSet(steppush, 0, 0, org[2] - ent->fields.server->origin[2]);
1305 SV_PushEntity(&steptrace3, ent, steppush, false, false);
1306 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1307 // accept the new position if it made some progress...
1308 if (fabs(ent->fields.server->origin[0] - org[0]) >= 0.03125 || fabs(ent->fields.server->origin[1] - org[1]) >= 0.03125)
1310 //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]);
1312 VectorCopy(ent->fields.server->origin, trace.endpos);
1313 time_left *= 1 - trace.fraction;
1319 //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]);
1320 VectorCopy(org, ent->fields.server->origin);
1325 // step - return it to caller
1327 // save the trace for player extrafriction
1329 VectorCopy(trace.plane.normal, stepnormal);
1331 if (trace.fraction >= 0.001)
1333 // actually covered some distance
1334 VectorCopy(ent->fields.server->velocity, original_velocity);
1338 time_left *= 1 - trace.fraction;
1340 // clipped to another plane
1341 if (numplanes >= MAX_CLIP_PLANES)
1343 // this shouldn't really happen
1344 VectorClear(ent->fields.server->velocity);
1350 for (i = 0;i < numplanes;i++)
1351 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1355 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1360 VectorCopy(trace.plane.normal, planes[numplanes]);
1363 // modify original_velocity so it parallels all of the clip planes
1364 for (i = 0;i < numplanes;i++)
1366 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1367 for (j = 0;j < numplanes;j++)
1372 if (DotProduct(new_velocity, planes[j]) < 0)
1382 // go along this plane
1383 VectorCopy(new_velocity, ent->fields.server->velocity);
1387 // go along the crease
1390 VectorClear(ent->fields.server->velocity);
1394 CrossProduct(planes[0], planes[1], dir);
1395 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1396 VectorNormalize(dir);
1397 d = DotProduct(dir, ent->fields.server->velocity);
1398 VectorScale(dir, d, ent->fields.server->velocity);
1401 // if current velocity is against the original velocity,
1402 // stop dead to avoid tiny occilations in sloping corners
1403 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1405 VectorClear(ent->fields.server->velocity);
1410 //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]);
1413 if ((blocked & 1) == 0 && bumpcount > 1)
1415 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1416 // flag ONGROUND if there's ground under it
1417 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1421 // LordHavoc: this came from QW and allows you to get out of water more easily
1422 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1423 VectorCopy(primal_velocity, ent->fields.server->velocity);
1424 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1425 ent->fields.server->velocity[2] -= gravity;
1435 static float SV_Gravity (prvm_edict_t *ent)
1440 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1441 if (val!=0 && val->_float)
1442 ent_gravity = val->_float;
1445 return ent_gravity * sv_gravity.value * sv.frametime;
1450 ===============================================================================
1454 ===============================================================================
1461 Does not change the entities velocity at all
1462 The trace struct is filled with the trace that has been done.
1463 Returns true if the push did not result in the entity being teleported by QC code.
1466 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1470 vec3_t original, original_velocity;
1473 VectorCopy(ent->fields.server->origin, original);
1474 VectorAdd (ent->fields.server->origin, push, end);
1476 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1477 type = MOVE_MISSILE;
1478 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1479 type = MOVE_NOMONSTERS; // only clip against bmodels
1483 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1485 while (trace->bmodelstartsolid && sv_gameplayfix_nudgeoutofsolid.integer)
1487 vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
1488 VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
1489 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1493 VectorCopy(original, ent->fields.server->origin);
1497 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1500 VectorCopy (trace->endpos, ent->fields.server->origin);
1502 VectorCopy(ent->fields.server->origin, original);
1503 VectorCopy(ent->fields.server->velocity, original_velocity);
1508 if(!trace->startsolid)
1509 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)
1511 Con_Printf("something eeeeevil happened\n");
1516 SV_LinkEdict_TouchAreaGrid(ent);
1518 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))))
1519 SV_Impact (ent, trace);
1521 return VectorCompare(ent->fields.server->origin, original) && VectorCompare(ent->fields.server->velocity, original_velocity);
1531 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1534 int pusherowner, pusherprog;
1537 float savesolid, movetime2, pushltime;
1538 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1540 int numcheckentities;
1541 static prvm_edict_t *checkentities[MAX_EDICTS];
1542 dp_model_t *pushermodel;
1543 trace_t trace, trace2;
1544 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1545 static unsigned short moved_edicts[MAX_EDICTS];
1547 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])
1549 pusher->fields.server->ltime += movetime;
1553 switch ((int) pusher->fields.server->solid)
1555 // LordHavoc: valid pusher types
1558 case SOLID_SLIDEBOX:
1559 case SOLID_CORPSE: // LordHavoc: this would be weird...
1561 // LordHavoc: no collisions
1564 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1565 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1566 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1567 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1568 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1569 pusher->fields.server->ltime += movetime;
1570 SV_LinkEdict(pusher);
1573 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1576 index = (int) pusher->fields.server->modelindex;
1577 if (index < 1 || index >= MAX_MODELS)
1579 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1582 pushermodel = SV_GetModelByIndex(index);
1583 pusherowner = pusher->fields.server->owner;
1584 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1586 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1588 movetime2 = movetime;
1589 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1590 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1591 if (moveangle[0] || moveangle[2])
1593 for (i = 0;i < 3;i++)
1597 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1598 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1602 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1603 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1607 else if (moveangle[1])
1609 for (i = 0;i < 3;i++)
1613 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1614 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1618 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1619 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1625 for (i = 0;i < 3;i++)
1629 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1630 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1634 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1635 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1640 VectorNegate (moveangle, a);
1641 AngleVectorsFLU (a, forward, left, up);
1643 VectorCopy (pusher->fields.server->origin, pushorig);
1644 VectorCopy (pusher->fields.server->angles, pushang);
1645 pushltime = pusher->fields.server->ltime;
1647 // move the pusher to its final position
1649 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1650 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1651 pusher->fields.server->ltime += movetime;
1652 SV_LinkEdict(pusher);
1654 pushermodel = SV_GetModelFromEdict(pusher);
1655 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);
1656 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1658 savesolid = pusher->fields.server->solid;
1660 // see if any solid entities are inside the final position
1663 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1664 for (e = 0;e < numcheckentities;e++)
1666 prvm_edict_t *check = checkentities[e];
1667 int movetype = (int)check->fields.server->movetype;
1672 case MOVETYPE_FOLLOW:
1673 case MOVETYPE_NOCLIP:
1674 case MOVETYPE_FAKEPUSH:
1680 if (check->fields.server->owner == pusherprog)
1683 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1686 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1688 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1689 check->priv.server->waterposition_forceupdate = true;
1691 checkcontents = SV_GenericHitSuperContentsMask(check);
1693 // if the entity is standing on the pusher, it will definitely be moved
1694 // if the entity is not standing on the pusher, but is in the pusher's
1695 // final position, move it
1696 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1698 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);
1699 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1700 if (!trace.startsolid)
1702 //Con_Printf("- not in solid\n");
1710 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1711 org2[0] = DotProduct (org, forward);
1712 org2[1] = DotProduct (org, left);
1713 org2[2] = DotProduct (org, up);
1714 VectorSubtract (org2, org, move);
1715 VectorAdd (move, move1, move);
1718 VectorCopy (move1, move);
1720 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1722 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1723 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1724 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1726 // physics objects need better collisions than this code can do
1727 if (movetype == MOVETYPE_PHYSICS)
1729 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1730 SV_LinkEdict(check);
1731 SV_LinkEdict_TouchAreaGrid(check);
1735 // try moving the contacted entity
1736 pusher->fields.server->solid = SOLID_NOT;
1737 if(!SV_PushEntity (&trace, check, move, true, true))
1739 // entity "check" got teleported
1740 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1741 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1742 continue; // pushed enough
1744 // FIXME: turn players specially
1745 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1746 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1747 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1749 // this trace.fraction < 1 check causes items to fall off of pushers
1750 // if they pass under or through a wall
1751 // the groundentity check causes items to fall off of ledges
1752 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1753 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1755 // if it is still inside the pusher, block
1756 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);
1757 if (trace.startsolid)
1759 // try moving the contacted entity a tiny bit further to account for precision errors
1761 pusher->fields.server->solid = SOLID_NOT;
1762 VectorScale(move, 1.1, move2);
1763 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1764 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1765 if(!SV_PushEntity (&trace2, check, move2, true, true))
1767 // entity "check" got teleported
1770 pusher->fields.server->solid = savesolid;
1771 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);
1772 if (trace.startsolid)
1774 // try moving the contacted entity a tiny bit less to account for precision errors
1775 pusher->fields.server->solid = SOLID_NOT;
1776 VectorScale(move, 0.9, move2);
1777 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1778 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1779 if(!SV_PushEntity (&trace2, check, move2, true, true))
1781 // entity "check" got teleported
1784 pusher->fields.server->solid = savesolid;
1785 Collision_ClipToGenericEntity(&trace, pushermodel, 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);
1786 if (trace.startsolid)
1788 // still inside pusher, so it's really blocked
1791 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1793 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1796 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1797 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1801 VectorCopy (pushorig, pusher->fields.server->origin);
1802 VectorCopy (pushang, pusher->fields.server->angles);
1803 pusher->fields.server->ltime = pushltime;
1804 SV_LinkEdict(pusher);
1806 // move back any entities we already moved
1807 for (i = 0;i < num_moved;i++)
1809 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1810 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1811 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1815 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1816 if (pusher->fields.server->blocked)
1818 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1819 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1820 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1827 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1828 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1829 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1838 void SV_Physics_Pusher (prvm_edict_t *ent)
1840 float thinktime, oldltime, movetime;
1842 oldltime = ent->fields.server->ltime;
1844 thinktime = ent->fields.server->nextthink;
1845 if (thinktime < ent->fields.server->ltime + sv.frametime)
1847 movetime = thinktime - ent->fields.server->ltime;
1852 movetime = sv.frametime;
1855 // advances ent->fields.server->ltime if not blocked
1856 SV_PushMove (ent, movetime);
1858 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1860 ent->fields.server->nextthink = 0;
1861 prog->globals.server->time = sv.time;
1862 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1863 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1864 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1870 ===============================================================================
1874 ===============================================================================
1877 static float unstickoffsets[] =
1879 // poutting -/+z changes first as they are least weird
1894 typedef enum unstickresult_e
1902 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1906 // if not stuck in a bmodel, just return
1907 if (!SV_TestEntityPosition(ent, vec3_origin))
1908 return UNSTICK_GOOD;
1910 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1912 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1914 VectorCopy(unstickoffsets + i, offset);
1916 //SV_LinkEdict_TouchAreaGrid(ent);
1917 return UNSTICK_UNSTUCK;
1921 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1922 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1924 for(i = 2; i <= maxunstick; ++i)
1926 VectorClear(offset);
1928 if (!SV_TestEntityPosition(ent, offset))
1931 //SV_LinkEdict_TouchAreaGrid(ent);
1932 return UNSTICK_UNSTUCK;
1935 if (!SV_TestEntityPosition(ent, offset))
1938 //SV_LinkEdict_TouchAreaGrid(ent);
1939 return UNSTICK_UNSTUCK;
1943 return UNSTICK_STUCK;
1946 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1949 switch(SV_UnstickEntityReturnOffset(ent, offset))
1953 case UNSTICK_UNSTUCK:
1954 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]);
1957 if (developer_extra.integer)
1958 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1961 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1970 This is a big hack to try and fix the rare case of getting stuck in the world
1974 void SV_CheckStuck (prvm_edict_t *ent)
1978 switch(SV_UnstickEntityReturnOffset(ent, offset))
1981 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1983 case UNSTICK_UNSTUCK:
1984 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]);
1987 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1988 if (!SV_TestEntityPosition(ent, offset))
1990 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1992 //SV_LinkEdict_TouchAreaGrid(ent);
1995 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1998 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2008 qboolean SV_CheckWater (prvm_edict_t *ent)
2011 int nNativeContents;
2014 point[0] = ent->fields.server->origin[0];
2015 point[1] = ent->fields.server->origin[1];
2016 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2018 // DRESK - Support for Entity Contents Transition Event
2019 // NOTE: Some logic needed to be slightly re-ordered
2020 // to not affect performance and allow for the feature.
2022 // Acquire Super Contents Prior to Resets
2023 cont = SV_PointSuperContents(point);
2024 // Acquire Native Contents Here
2025 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2027 // DRESK - Support for Entity Contents Transition Event
2028 if(ent->fields.server->watertype)
2029 // Entity did NOT Spawn; Check
2030 SV_CheckContentsTransition(ent, nNativeContents);
2033 ent->fields.server->waterlevel = 0;
2034 ent->fields.server->watertype = CONTENTS_EMPTY;
2035 cont = SV_PointSuperContents(point);
2036 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2038 ent->fields.server->watertype = nNativeContents;
2039 ent->fields.server->waterlevel = 1;
2040 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2041 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2043 ent->fields.server->waterlevel = 2;
2044 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2045 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2046 ent->fields.server->waterlevel = 3;
2050 return ent->fields.server->waterlevel > 1;
2059 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2062 vec3_t forward, into, side;
2064 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2065 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2067 // cut the tangential velocity
2068 i = DotProduct (stepnormal, ent->fields.server->velocity);
2069 VectorScale (stepnormal, i, into);
2070 VectorSubtract (ent->fields.server->velocity, into, side);
2071 ent->fields.server->velocity[0] = side[0] * (1 + d);
2072 ent->fields.server->velocity[1] = side[1] * (1 + d);
2078 =====================
2081 Player has come to a dead stop, possibly due to the problem with limited
2082 float precision at some angle joins in the BSP hull.
2084 Try fixing by pushing one pixel in each direction.
2086 This is a hack, but in the interest of good gameplay...
2087 ======================
2089 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2094 VectorCopy (ent->fields.server->origin, oldorg);
2097 for (i=0 ; i<8 ; i++)
2099 // try pushing a little in an axial direction
2102 case 0: dir[0] = 2; dir[1] = 0; break;
2103 case 1: dir[0] = 0; dir[1] = 2; break;
2104 case 2: dir[0] = -2; dir[1] = 0; break;
2105 case 3: dir[0] = 0; dir[1] = -2; break;
2106 case 4: dir[0] = 2; dir[1] = 2; break;
2107 case 5: dir[0] = -2; dir[1] = 2; break;
2108 case 6: dir[0] = 2; dir[1] = -2; break;
2109 case 7: dir[0] = -2; dir[1] = -2; break;
2112 SV_PushEntity (&trace, ent, dir, false, true);
2114 // retry the original move
2115 ent->fields.server->velocity[0] = oldvel[0];
2116 ent->fields.server->velocity[1] = oldvel[1];
2117 ent->fields.server->velocity[2] = 0;
2118 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2120 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2121 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2123 Con_DPrint("TryUnstick - success.\n");
2127 // go back to the original pos and try again
2128 VectorCopy (oldorg, ent->fields.server->origin);
2132 VectorClear (ent->fields.server->velocity);
2133 Con_DPrint("TryUnstick - failure.\n");
2139 =====================
2142 Only used by players
2143 ======================
2145 void SV_WalkMove (prvm_edict_t *ent)
2149 //int originalmove_clip;
2150 int originalmove_flags;
2151 int originalmove_groundentity;
2152 int hitsupercontentsmask;
2154 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2155 trace_t downtrace, trace;
2156 qboolean applygravity;
2158 // if frametime is 0 (due to client sending the same timestamp twice),
2160 if (sv.frametime <= 0)
2163 SV_CheckStuck (ent);
2165 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2167 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2169 SV_CheckVelocity(ent);
2171 // do a regular slide move unless it looks like you ran into a step
2172 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2174 VectorCopy (ent->fields.server->origin, start_origin);
2175 VectorCopy (ent->fields.server->velocity, start_velocity);
2177 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2179 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2182 // only try this if there was no floor in the way in the trace (no,
2183 // this check seems to be not REALLY necessary, because if clip & 1,
2184 // our trace will hit that thing too)
2185 VectorSet(upmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + 1);
2186 VectorSet(downmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
2187 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
2188 type = MOVE_MISSILE;
2189 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
2190 type = MOVE_NOMONSTERS; // only clip against bmodels
2193 trace = SV_TraceBox(upmove, ent->fields.server->mins, ent->fields.server->maxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2194 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2195 clip |= 1; // but we HAVE found a floor
2198 // if the move did not hit the ground at any point, we're not on ground
2200 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2202 SV_CheckVelocity(ent);
2204 SV_LinkEdict_TouchAreaGrid(ent);
2206 if(clip & 8) // teleport
2209 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2212 if (sv_nostep.integer)
2215 VectorCopy(ent->fields.server->origin, originalmove_origin);
2216 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2217 //originalmove_clip = clip;
2218 originalmove_flags = (int)ent->fields.server->flags;
2219 originalmove_groundentity = ent->fields.server->groundentity;
2221 // if move didn't block on a step, return
2224 // if move was not trying to move into the step, return
2225 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2228 if (ent->fields.server->movetype != MOVETYPE_FLY)
2230 // return if gibbed by a trigger
2231 if (ent->fields.server->movetype != MOVETYPE_WALK)
2234 // only step up while jumping if that is enabled
2235 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2236 if (!oldonground && ent->fields.server->waterlevel == 0)
2240 // try moving up and forward to go up a step
2241 // back to start pos
2242 VectorCopy (start_origin, ent->fields.server->origin);
2243 VectorCopy (start_velocity, ent->fields.server->velocity);
2246 VectorClear (upmove);
2247 upmove[2] = sv_stepheight.value;
2248 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2250 // we got teleported when upstepping... must abort the move
2255 ent->fields.server->velocity[2] = 0;
2256 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2257 ent->fields.server->velocity[2] += start_velocity[2];
2260 // we got teleported when upstepping... must abort the move
2261 // note that z velocity handling may not be what QC expects here, but we cannot help it
2265 SV_CheckVelocity(ent);
2267 SV_LinkEdict_TouchAreaGrid(ent);
2269 // check for stuckness, possibly due to the limited precision of floats
2270 // in the clipping hulls
2272 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2273 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2275 //Con_Printf("wall\n");
2276 // stepping up didn't make any progress, revert to original move
2277 VectorCopy(originalmove_origin, ent->fields.server->origin);
2278 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2279 //clip = originalmove_clip;
2280 ent->fields.server->flags = originalmove_flags;
2281 ent->fields.server->groundentity = originalmove_groundentity;
2282 // now try to unstick if needed
2283 //clip = SV_TryUnstick (ent, oldvel);
2287 //Con_Printf("step - ");
2289 // extra friction based on view angle
2290 if (clip & 2 && sv_wallfriction.integer)
2291 SV_WallFriction (ent, stepnormal);
2293 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2294 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))
2298 VectorClear (downmove);
2299 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2300 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2302 // we got teleported when downstepping... must abort the move
2306 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2308 // this has been disabled so that you can't jump when you are stepping
2309 // up while already jumping (also known as the Quake2 double jump bug)
2311 // LordHavoc: disabled this check so you can walk on monsters/players
2312 //if (ent->fields.server->solid == SOLID_BSP)
2314 //Con_Printf("onground\n");
2315 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2316 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2322 //Con_Printf("slope\n");
2323 // if the push down didn't end up on good ground, use the move without
2324 // the step up. This happens near wall / slope combinations, and can
2325 // cause the player to hop up higher on a slope too steep to climb
2326 VectorCopy(originalmove_origin, ent->fields.server->origin);
2327 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2328 //clip = originalmove_clip;
2329 ent->fields.server->flags = originalmove_flags;
2330 ent->fields.server->groundentity = originalmove_groundentity;
2333 SV_CheckVelocity(ent);
2335 SV_LinkEdict_TouchAreaGrid(ent);
2338 //============================================================================
2344 Entities that are "stuck" to another entity
2347 void SV_Physics_Follow (prvm_edict_t *ent)
2349 vec3_t vf, vr, vu, angles, v;
2353 if (!SV_RunThink (ent))
2356 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2357 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2358 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])
2360 // quick case for no rotation
2361 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2365 angles[0] = -ent->fields.server->punchangle[0];
2366 angles[1] = ent->fields.server->punchangle[1];
2367 angles[2] = ent->fields.server->punchangle[2];
2368 AngleVectors (angles, vf, vr, vu);
2369 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];
2370 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];
2371 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];
2372 angles[0] = -e->fields.server->angles[0];
2373 angles[1] = e->fields.server->angles[1];
2374 angles[2] = e->fields.server->angles[2];
2375 AngleVectors (angles, vf, vr, vu);
2376 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2377 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2378 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2380 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2382 //SV_LinkEdict_TouchAreaGrid(ent);
2386 ==============================================================================
2390 ==============================================================================
2395 SV_CheckWaterTransition
2399 void SV_CheckWaterTransition (prvm_edict_t *ent)
2402 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2403 if (!ent->fields.server->watertype)
2405 // just spawned here
2406 ent->fields.server->watertype = cont;
2407 ent->fields.server->waterlevel = 1;
2411 // DRESK - Support for Entity Contents Transition Event
2412 // NOTE: Call here BEFORE updating the watertype below,
2413 // and suppress watersplash sound if a valid function
2414 // call was made to allow for custom "splash" sounds.
2415 if( !SV_CheckContentsTransition(ent, cont) )
2416 { // Contents Transition Function Invalid; Potentially Play Water Sound
2417 // check if the entity crossed into or out of water
2418 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2419 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2422 if (cont <= CONTENTS_WATER)
2424 ent->fields.server->watertype = cont;
2425 ent->fields.server->waterlevel = 1;
2429 ent->fields.server->watertype = CONTENTS_EMPTY;
2430 ent->fields.server->waterlevel = 0;
2438 Toss, bounce, and fly movement. When onground, do nothing.
2441 void SV_Physics_Toss (prvm_edict_t *ent)
2447 prvm_edict_t *groundentity;
2449 // if onground, return without moving
2450 if ((int)ent->fields.server->flags & FL_ONGROUND)
2452 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2453 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2455 // don't stick to ground if onground and moving upward
2456 ent->fields.server->flags -= FL_ONGROUND;
2458 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2460 // we can trust FL_ONGROUND if groundentity is world because it never moves
2463 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2465 // if ent was supported by a brush model on previous frame,
2466 // and groundentity is now freed, set groundentity to 0 (world)
2467 // which leaves it suspended in the air
2468 ent->fields.server->groundentity = 0;
2469 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2472 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2474 // don't slide if still touching the groundentity
2478 ent->priv.server->suspendedinairflag = false;
2480 SV_CheckVelocity (ent);
2483 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2484 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2487 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2489 movetime = sv.frametime;
2490 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2493 VectorScale (ent->fields.server->velocity, movetime, move);
2494 if(!SV_PushEntity (&trace, ent, move, true, true))
2495 return; // teleported
2496 if (ent->priv.server->free)
2498 if (trace.bmodelstartsolid)
2500 // try to unstick the entity
2501 SV_UnstickEntity(ent);
2502 if(!SV_PushEntity (&trace, ent, move, false, true))
2503 return; // teleported
2504 if (ent->priv.server->free)
2507 if (trace.fraction == 1)
2509 movetime *= 1 - min(1, trace.fraction);
2510 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2513 float bouncefactor = 1.0f;
2514 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2515 if (val!=0 && val->_float)
2516 bouncefactor = val->_float;
2518 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2519 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2521 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2523 float d, ent_gravity;
2525 float bouncefactor = 0.5f;
2526 float bouncestop = 60.0f / 800.0f;
2528 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2529 if (val!=0 && val->_float)
2530 bouncefactor = val->_float;
2532 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2533 if (val!=0 && val->_float)
2534 bouncestop = val->_float;
2536 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2537 // LordHavoc: fixed grenades not bouncing when fired down a slope
2538 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2539 if (val!=0 && val->_float)
2540 ent_gravity = val->_float;
2543 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2545 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2546 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2548 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2549 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2550 VectorClear (ent->fields.server->velocity);
2551 VectorClear (ent->fields.server->avelocity);
2554 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2558 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 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;
2571 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2572 if (trace.plane.normal[2] > 0.7)
2574 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2575 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2576 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2577 ent->priv.server->suspendedinairflag = true;
2578 VectorClear (ent->fields.server->velocity);
2579 VectorClear (ent->fields.server->avelocity);
2582 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2584 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2588 // check for in water
2589 SV_CheckWaterTransition (ent);
2593 ===============================================================================
2597 ===============================================================================
2604 Monsters freefall when they don't have a ground entity, otherwise
2605 all movement is done with discrete steps.
2607 This is also used for objects that have become still on the ground, but
2608 will fall if the floor is pulled out from under them.
2611 void SV_Physics_Step (prvm_edict_t *ent)
2613 int flags = (int)ent->fields.server->flags;
2616 // Backup Velocity in the event that movetypesteplandevent is called,
2617 // to provide a parameter with the entity's velocity at impact.
2618 prvm_eval_t *movetypesteplandevent;
2619 vec3_t backupVelocity;
2620 VectorCopy(ent->fields.server->velocity, backupVelocity);
2621 // don't fall at all if fly/swim
2622 if (!(flags & (FL_FLY | FL_SWIM)))
2624 if (flags & FL_ONGROUND)
2626 // freefall if onground and moving upward
2627 // freefall if not standing on a world surface (it may be a lift or trap door)
2628 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2630 ent->fields.server->flags -= FL_ONGROUND;
2631 SV_CheckVelocity(ent);
2632 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2634 SV_LinkEdict_TouchAreaGrid(ent);
2635 ent->priv.server->waterposition_forceupdate = true;
2640 // freefall if not onground
2641 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2643 SV_CheckVelocity(ent);
2644 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2646 SV_LinkEdict_TouchAreaGrid(ent);
2649 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2651 // DRESK - Check for Entity Land Event Function
2652 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2654 if(movetypesteplandevent->function)
2655 { // Valid Function; Execute
2656 // Prepare Parameters
2657 // Assign Velocity at Impact
2658 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2659 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2660 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2662 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2663 // Execute VM Function
2664 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2667 // Check for Engine Landing Sound
2668 if(sv_sound_land.string)
2669 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2671 ent->priv.server->waterposition_forceupdate = true;
2676 if (!SV_RunThink(ent))
2679 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2681 ent->priv.server->waterposition_forceupdate = false;
2682 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2683 SV_CheckWaterTransition(ent);
2687 //============================================================================
2689 static void SV_Physics_Entity (prvm_edict_t *ent)
2691 // don't run think/move on newly spawned projectiles as it messes up
2692 // movement interpolation and rocket trails, and is inconsistent with
2693 // respect to entities spawned in the same frame
2694 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2695 // but if it spawns a lower numbered ent, it doesn't - this never moves
2696 // ents in the first frame regardless)
2697 qboolean runmove = ent->priv.server->move;
2698 ent->priv.server->move = true;
2699 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2701 switch ((int) ent->fields.server->movetype)
2704 case MOVETYPE_FAKEPUSH:
2705 SV_Physics_Pusher (ent);
2708 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2709 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2712 case MOVETYPE_FOLLOW:
2713 SV_Physics_Follow (ent);
2715 case MOVETYPE_NOCLIP:
2716 if (SV_RunThink(ent))
2719 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2720 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2725 SV_Physics_Step (ent);
2728 if (SV_RunThink (ent))
2732 case MOVETYPE_BOUNCE:
2733 case MOVETYPE_BOUNCEMISSILE:
2734 case MOVETYPE_FLYMISSILE:
2737 if (SV_RunThink (ent))
2738 SV_Physics_Toss (ent);
2740 case MOVETYPE_PHYSICS:
2741 if (SV_RunThink(ent))
2744 SV_LinkEdict_TouchAreaGrid(ent);
2748 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2753 void SV_Physics_ClientMove(void)
2756 ent = host_client->edict;
2758 // call player physics, this needs the proper frametime
2759 prog->globals.server->frametime = sv.frametime;
2762 // call standard client pre-think, with frametime = 0
2763 prog->globals.server->time = sv.time;
2764 prog->globals.server->frametime = 0;
2765 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2766 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2767 prog->globals.server->frametime = sv.frametime;
2769 // make sure the velocity is sane (not a NaN)
2770 SV_CheckVelocity(ent);
2772 // perform MOVETYPE_WALK behavior
2775 // call standard player post-think, with frametime = 0
2776 prog->globals.server->time = sv.time;
2777 prog->globals.server->frametime = 0;
2778 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2779 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2780 prog->globals.server->frametime = sv.frametime;
2782 if(ent->fields.server->fixangle)
2784 // angle fixing was requested by physics code...
2785 // so store the current angles for later use
2786 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2787 host_client->fixangle_angles_set = TRUE;
2789 // and clear fixangle for the next frame
2790 ent->fields.server->fixangle = 0;
2794 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2796 // don't do physics on disconnected clients, FrikBot relies on this
2797 if (!host_client->spawned)
2800 // make sure the velocity is sane (not a NaN)
2801 SV_CheckVelocity(ent);
2803 // don't run physics here if running asynchronously
2804 if (host_client->clmovement_inputtimeout <= 0)
2807 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2810 // make sure the velocity is still sane (not a NaN)
2811 SV_CheckVelocity(ent);
2813 // call standard client pre-think
2814 prog->globals.server->time = sv.time;
2815 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2816 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2818 // make sure the velocity is still sane (not a NaN)
2819 SV_CheckVelocity(ent);
2822 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2824 // don't do physics on disconnected clients, FrikBot relies on this
2825 if (!host_client->spawned)
2828 // make sure the velocity is sane (not a NaN)
2829 SV_CheckVelocity(ent);
2831 // call standard player post-think
2832 prog->globals.server->time = sv.time;
2833 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2834 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2836 // make sure the velocity is still sane (not a NaN)
2837 SV_CheckVelocity(ent);
2839 if(ent->fields.server->fixangle)
2841 // angle fixing was requested by physics code...
2842 // so store the current angles for later use
2843 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2844 host_client->fixangle_angles_set = TRUE;
2846 // and clear fixangle for the next frame
2847 ent->fields.server->fixangle = 0;
2850 // decrement the countdown variable used to decide when to go back to
2851 // synchronous physics
2852 if (host_client->clmovement_inputtimeout > sv.frametime)
2853 host_client->clmovement_inputtimeout -= sv.frametime;
2855 host_client->clmovement_inputtimeout = 0;
2858 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2860 // don't do physics on disconnected clients, FrikBot relies on this
2861 if (!host_client->spawned)
2863 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2867 // make sure the velocity is sane (not a NaN)
2868 SV_CheckVelocity(ent);
2870 switch ((int) ent->fields.server->movetype)
2873 case MOVETYPE_FAKEPUSH:
2874 SV_Physics_Pusher (ent);
2877 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2878 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2881 case MOVETYPE_FOLLOW:
2882 SV_Physics_Follow (ent);
2884 case MOVETYPE_NOCLIP:
2887 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2888 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2891 SV_Physics_Step (ent);
2895 // don't run physics here if running asynchronously
2896 if (host_client->clmovement_inputtimeout <= 0)
2900 case MOVETYPE_BOUNCE:
2901 case MOVETYPE_BOUNCEMISSILE:
2902 case MOVETYPE_FLYMISSILE:
2905 SV_Physics_Toss (ent);
2911 case MOVETYPE_PHYSICS:
2915 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2919 SV_CheckVelocity (ent);
2922 SV_LinkEdict_TouchAreaGrid(ent);
2924 SV_CheckVelocity (ent);
2933 void SV_Physics (void)
2938 // let the progs know that a new frame has started
2939 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2940 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2941 prog->globals.server->time = sv.time;
2942 prog->globals.server->frametime = sv.frametime;
2943 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2945 // run physics engine
2946 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2949 // treat each object in turn
2952 // if force_retouch, relink all the entities
2953 if (prog->globals.server->force_retouch > 0)
2954 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2955 if (!ent->priv.server->free)
2956 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2958 if (sv_gameplayfix_consistentplayerprethink.integer)
2960 // run physics on the client entities in 3 stages
2961 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2962 if (!ent->priv.server->free)
2963 SV_Physics_ClientEntity_PreThink(ent);
2965 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2966 if (!ent->priv.server->free)
2967 SV_Physics_ClientEntity(ent);
2969 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2970 if (!ent->priv.server->free)
2971 SV_Physics_ClientEntity_PostThink(ent);
2975 // run physics on the client entities
2976 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)
2980 SV_Physics_ClientEntity_PreThink(ent);
2981 SV_Physics_ClientEntity(ent);
2982 SV_Physics_ClientEntity_PostThink(ent);
2987 // run physics on all the non-client entities
2988 if (!sv_freezenonclients.integer)
2990 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2991 if (!ent->priv.server->free)
2992 SV_Physics_Entity(ent);
2993 // make a second pass to see if any ents spawned this frame and make
2994 // sure they run their move/think
2995 if (sv_gameplayfix_delayprojectiles.integer < 0)
2996 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2997 if (!ent->priv.server->move && !ent->priv.server->free)
2998 SV_Physics_Entity(ent);
3001 if (prog->globals.server->force_retouch > 0)
3002 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
3004 // LordHavoc: endframe support
3005 if (prog->funcoffsets.EndFrame)
3007 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
3008 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
3009 prog->globals.server->time = sv.time;
3010 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
3013 // decrement prog->num_edicts if the highest number entities died
3014 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3016 if (!sv_freezenonclients.integer)
3017 sv.time += sv.frametime;