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
1131 returns true if the impact kept the origin of the touching entity intact
1134 extern void VM_SetTraceGlobals(const trace_t *trace);
1135 extern sizebuf_t vm_tempstringsbuf;
1136 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1138 int restorevm_tempstringsbuf_cursize;
1139 int old_self, old_other;
1141 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1144 old_self = prog->globals.server->self;
1145 old_other = prog->globals.server->other;
1146 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1148 VectorCopy(e1->fields.server->origin, org);
1150 VM_SetTraceGlobals(trace);
1152 prog->globals.server->time = sv.time;
1153 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1155 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1156 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1157 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1160 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1162 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1163 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1164 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1165 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1166 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1167 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1168 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1170 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1172 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1174 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1176 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1179 prog->globals.server->self = old_self;
1180 prog->globals.server->other = old_other;
1181 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1183 return VectorCompare(e1->fields.server->origin, org);
1191 Slide off of the impacting object
1192 returns the blocked flags (1 = floor, 2 = step / wall)
1195 #define STOP_EPSILON 0.1
1196 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1201 backoff = -DotProduct (in, normal) * overbounce;
1202 VectorMA(in, backoff, normal, out);
1204 for (i = 0;i < 3;i++)
1205 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1214 The basic solid body movement clip that slides along multiple planes
1215 Returns the clipflags if the velocity was modified (hit something solid)
1219 8 = teleported by touch method
1220 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1223 static float SV_Gravity (prvm_edict_t *ent);
1224 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1225 #define MAX_CLIP_PLANES 5
1226 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
1228 int blocked, bumpcount;
1229 int i, j, numplanes;
1230 float d, time_left, gravity;
1231 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1241 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1243 gravity = SV_Gravity(ent) * 0.5f;
1244 ent->fields.server->velocity[2] -= gravity;
1248 applygravity = false;
1249 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1253 VectorCopy(ent->fields.server->velocity, original_velocity);
1254 VectorCopy(ent->fields.server->velocity, primal_velocity);
1257 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1259 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1262 VectorScale(ent->fields.server->velocity, time_left, push);
1264 VectorAdd(ent->fields.server->origin, push, end);
1266 if(!SV_PushEntity(&trace, ent, push, false, false))
1268 // we got teleported by a touch function
1269 // let's abort the move
1275 //if (trace.fraction < 0.002)
1280 VectorCopy(ent->fields.server->origin, start);
1281 start[2] += 3;//0.03125;
1282 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1283 end[2] += 3;//0.03125;
1284 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1285 if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
1287 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
1293 for (i = 0;i < numplanes;i++)
1295 VectorCopy(ent->fields.server->origin, start);
1296 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1297 VectorMA(start, 3, planes[i], start);
1298 VectorMA(end, 3, planes[i], end);
1299 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1300 if (trace.fraction < testtrace.fraction)
1303 VectorCopy(start, ent->fields.server->origin);
1308 // VectorAdd(ent->fields.server->origin, planes[j], start);
1314 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
1315 if (trace.fraction < 1)
1316 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
1321 if (trace.bmodelstartsolid)
1323 // LordHavoc: note: this code is what makes entities stick in place
1324 // if embedded in world only (you can walk through other objects if
1326 // entity is trapped in another solid
1327 VectorClear(ent->fields.server->velocity);
1332 if (trace.fraction == 1)
1334 if (trace.plane.normal[2])
1336 if (trace.plane.normal[2] > 0.7)
1343 Con_Printf ("SV_FlyMove: !trace.ent");
1344 trace.ent = prog->edicts;
1347 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1348 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1355 // save the trace for player extrafriction
1357 VectorCopy(trace.plane.normal, stepnormal);
1359 if (trace.fraction >= 0.001)
1361 // actually covered some distance
1362 VectorCopy(ent->fields.server->velocity, original_velocity);
1366 time_left *= 1 - trace.fraction;
1368 // clipped to another plane
1369 if (numplanes >= MAX_CLIP_PLANES)
1371 // this shouldn't really happen
1372 VectorClear(ent->fields.server->velocity);
1378 for (i = 0;i < numplanes;i++)
1379 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1383 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1388 VectorCopy(trace.plane.normal, planes[numplanes]);
1391 if (sv_newflymove.integer)
1392 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1395 // modify original_velocity so it parallels all of the clip planes
1396 for (i = 0;i < numplanes;i++)
1398 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1399 for (j = 0;j < numplanes;j++)
1404 if (DotProduct(new_velocity, planes[j]) < 0)
1414 // go along this plane
1415 VectorCopy(new_velocity, ent->fields.server->velocity);
1419 // go along the crease
1422 VectorClear(ent->fields.server->velocity);
1426 CrossProduct(planes[0], planes[1], dir);
1427 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1428 VectorNormalize(dir);
1429 d = DotProduct(dir, ent->fields.server->velocity);
1430 VectorScale(dir, d, ent->fields.server->velocity);
1434 // if current velocity is against the original velocity,
1435 // stop dead to avoid tiny occilations in sloping corners
1436 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1438 VectorClear(ent->fields.server->velocity);
1443 //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]);
1446 if ((blocked & 1) == 0 && bumpcount > 1)
1448 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1449 // flag ONGROUND if there's ground under it
1450 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1454 // LordHavoc: this came from QW and allows you to get out of water more easily
1455 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1456 VectorCopy(primal_velocity, ent->fields.server->velocity);
1457 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1458 ent->fields.server->velocity[2] -= gravity;
1468 static float SV_Gravity (prvm_edict_t *ent)
1473 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1474 if (val!=0 && val->_float)
1475 ent_gravity = val->_float;
1478 return ent_gravity * sv_gravity.value * sv.frametime;
1483 ===============================================================================
1487 ===============================================================================
1494 Does not change the entities velocity at all
1495 The trace struct is filled with the trace that has been done.
1496 Returns true if the push did not result in the entity being teleported by QC code.
1499 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1506 VectorCopy(ent->fields.server->origin, original);
1507 VectorAdd (ent->fields.server->origin, push, end);
1509 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1510 type = MOVE_MISSILE;
1511 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1512 type = MOVE_NOMONSTERS; // only clip against bmodels
1516 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1518 while (trace->bmodelstartsolid && sv_gameplayfix_nudgeoutofsolid.integer)
1520 vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
1521 VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
1522 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1526 VectorCopy(original, ent->fields.server->origin);
1530 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1534 VectorCopy (trace->endpos, ent->fields.server->origin);
1538 if(!trace->startsolid)
1539 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)
1541 Con_Printf("something eeeeevil happened\n");
1546 SV_LinkEdict_TouchAreaGrid(ent);
1548 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))))
1549 return SV_Impact (ent, trace);
1561 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1564 int pusherowner, pusherprog;
1567 float savesolid, movetime2, pushltime;
1568 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1570 int numcheckentities;
1571 static prvm_edict_t *checkentities[MAX_EDICTS];
1572 dp_model_t *pushermodel;
1573 trace_t trace, trace2;
1574 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1575 static unsigned short moved_edicts[MAX_EDICTS];
1577 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])
1579 pusher->fields.server->ltime += movetime;
1583 switch ((int) pusher->fields.server->solid)
1585 // LordHavoc: valid pusher types
1588 case SOLID_SLIDEBOX:
1589 case SOLID_CORPSE: // LordHavoc: this would be weird...
1591 // LordHavoc: no collisions
1594 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1595 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1596 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1597 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1598 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1599 pusher->fields.server->ltime += movetime;
1600 SV_LinkEdict(pusher);
1603 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1606 index = (int) pusher->fields.server->modelindex;
1607 if (index < 1 || index >= MAX_MODELS)
1609 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1612 pushermodel = SV_GetModelByIndex(index);
1613 pusherowner = pusher->fields.server->owner;
1614 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1616 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1618 movetime2 = movetime;
1619 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1620 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1621 if (moveangle[0] || moveangle[2])
1623 for (i = 0;i < 3;i++)
1627 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1628 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1632 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1633 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1637 else if (moveangle[1])
1639 for (i = 0;i < 3;i++)
1643 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1644 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1648 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1649 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1655 for (i = 0;i < 3;i++)
1659 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1660 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1664 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1665 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1670 VectorNegate (moveangle, a);
1671 AngleVectorsFLU (a, forward, left, up);
1673 VectorCopy (pusher->fields.server->origin, pushorig);
1674 VectorCopy (pusher->fields.server->angles, pushang);
1675 pushltime = pusher->fields.server->ltime;
1677 // move the pusher to its final position
1679 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1680 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1681 pusher->fields.server->ltime += movetime;
1682 SV_LinkEdict(pusher);
1684 pushermodel = SV_GetModelFromEdict(pusher);
1685 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);
1686 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1688 savesolid = pusher->fields.server->solid;
1690 // see if any solid entities are inside the final position
1693 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1694 for (e = 0;e < numcheckentities;e++)
1696 prvm_edict_t *check = checkentities[e];
1697 int movetype = (int)check->fields.server->movetype;
1702 case MOVETYPE_FOLLOW:
1703 case MOVETYPE_NOCLIP:
1704 case MOVETYPE_FAKEPUSH:
1710 if (check->fields.server->owner == pusherprog)
1713 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1716 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1718 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1719 check->priv.server->waterposition_forceupdate = true;
1721 checkcontents = SV_GenericHitSuperContentsMask(check);
1723 // if the entity is standing on the pusher, it will definitely be moved
1724 // if the entity is not standing on the pusher, but is in the pusher's
1725 // final position, move it
1726 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1728 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);
1729 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1730 if (!trace.startsolid)
1732 //Con_Printf("- not in solid\n");
1740 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1741 org2[0] = DotProduct (org, forward);
1742 org2[1] = DotProduct (org, left);
1743 org2[2] = DotProduct (org, up);
1744 VectorSubtract (org2, org, move);
1745 VectorAdd (move, move1, move);
1748 VectorCopy (move1, move);
1750 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1752 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1753 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1754 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1756 // physics objects need better collisions than this code can do
1757 if (movetype == MOVETYPE_PHYSICS)
1759 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1760 SV_LinkEdict(check);
1761 SV_LinkEdict_TouchAreaGrid(check);
1765 // try moving the contacted entity
1766 pusher->fields.server->solid = SOLID_NOT;
1767 if(!SV_PushEntity (&trace, check, move, true, true))
1769 // entity "check" got teleported
1770 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1771 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1772 continue; // pushed enough
1774 // FIXME: turn players specially
1775 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1776 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1777 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1779 // this trace.fraction < 1 check causes items to fall off of pushers
1780 // if they pass under or through a wall
1781 // the groundentity check causes items to fall off of ledges
1782 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1783 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1785 // if it is still inside the pusher, block
1786 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);
1787 if (trace.startsolid)
1789 // try moving the contacted entity a tiny bit further to account for precision errors
1791 pusher->fields.server->solid = SOLID_NOT;
1792 VectorScale(move, 1.1, move2);
1793 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1794 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1795 if(!SV_PushEntity (&trace2, check, move2, true, true))
1797 // entity "check" got teleported
1800 pusher->fields.server->solid = savesolid;
1801 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);
1802 if (trace.startsolid)
1804 // try moving the contacted entity a tiny bit less to account for precision errors
1805 pusher->fields.server->solid = SOLID_NOT;
1806 VectorScale(move, 0.9, move2);
1807 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1808 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1809 if(!SV_PushEntity (&trace2, check, move2, true, true))
1811 // entity "check" got teleported
1814 pusher->fields.server->solid = savesolid;
1815 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);
1816 if (trace.startsolid)
1818 // still inside pusher, so it's really blocked
1821 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1823 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1826 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1827 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1831 VectorCopy (pushorig, pusher->fields.server->origin);
1832 VectorCopy (pushang, pusher->fields.server->angles);
1833 pusher->fields.server->ltime = pushltime;
1834 SV_LinkEdict(pusher);
1836 // move back any entities we already moved
1837 for (i = 0;i < num_moved;i++)
1839 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1840 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1841 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1845 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1846 if (pusher->fields.server->blocked)
1848 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1849 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1850 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1857 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1858 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1859 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1868 void SV_Physics_Pusher (prvm_edict_t *ent)
1870 float thinktime, oldltime, movetime;
1872 oldltime = ent->fields.server->ltime;
1874 thinktime = ent->fields.server->nextthink;
1875 if (thinktime < ent->fields.server->ltime + sv.frametime)
1877 movetime = thinktime - ent->fields.server->ltime;
1882 movetime = sv.frametime;
1885 // advances ent->fields.server->ltime if not blocked
1886 SV_PushMove (ent, movetime);
1888 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1890 ent->fields.server->nextthink = 0;
1891 prog->globals.server->time = sv.time;
1892 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1893 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1894 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1900 ===============================================================================
1904 ===============================================================================
1907 static float unstickoffsets[] =
1909 // poutting -/+z changes first as they are least weird
1924 typedef enum unstickresult_e
1932 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1936 // if not stuck in a bmodel, just return
1937 if (!SV_TestEntityPosition(ent, vec3_origin))
1938 return UNSTICK_GOOD;
1940 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1942 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1944 VectorCopy(unstickoffsets + i, offset);
1946 //SV_LinkEdict_TouchAreaGrid(ent);
1947 return UNSTICK_UNSTUCK;
1951 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1952 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1954 for(i = 2; i <= maxunstick; ++i)
1956 VectorClear(offset);
1958 if (!SV_TestEntityPosition(ent, offset))
1961 //SV_LinkEdict_TouchAreaGrid(ent);
1962 return UNSTICK_UNSTUCK;
1965 if (!SV_TestEntityPosition(ent, offset))
1968 //SV_LinkEdict_TouchAreaGrid(ent);
1969 return UNSTICK_UNSTUCK;
1973 return UNSTICK_STUCK;
1976 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1979 switch(SV_UnstickEntityReturnOffset(ent, offset))
1983 case UNSTICK_UNSTUCK:
1984 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]);
1987 if (developer_extra.integer)
1988 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1991 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2000 This is a big hack to try and fix the rare case of getting stuck in the world
2004 void SV_CheckStuck (prvm_edict_t *ent)
2008 switch(SV_UnstickEntityReturnOffset(ent, offset))
2011 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
2013 case UNSTICK_UNSTUCK:
2014 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]);
2017 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
2018 if (!SV_TestEntityPosition(ent, offset))
2020 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2022 //SV_LinkEdict_TouchAreaGrid(ent);
2025 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2028 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2038 qboolean SV_CheckWater (prvm_edict_t *ent)
2041 int nNativeContents;
2044 point[0] = ent->fields.server->origin[0];
2045 point[1] = ent->fields.server->origin[1];
2046 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2048 // DRESK - Support for Entity Contents Transition Event
2049 // NOTE: Some logic needed to be slightly re-ordered
2050 // to not affect performance and allow for the feature.
2052 // Acquire Super Contents Prior to Resets
2053 cont = SV_PointSuperContents(point);
2054 // Acquire Native Contents Here
2055 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2057 // DRESK - Support for Entity Contents Transition Event
2058 if(ent->fields.server->watertype)
2059 // Entity did NOT Spawn; Check
2060 SV_CheckContentsTransition(ent, nNativeContents);
2063 ent->fields.server->waterlevel = 0;
2064 ent->fields.server->watertype = CONTENTS_EMPTY;
2065 cont = SV_PointSuperContents(point);
2066 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2068 ent->fields.server->watertype = nNativeContents;
2069 ent->fields.server->waterlevel = 1;
2070 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2071 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2073 ent->fields.server->waterlevel = 2;
2074 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2075 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2076 ent->fields.server->waterlevel = 3;
2080 return ent->fields.server->waterlevel > 1;
2089 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2092 vec3_t forward, into, side;
2094 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2095 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2097 // cut the tangential velocity
2098 i = DotProduct (stepnormal, ent->fields.server->velocity);
2099 VectorScale (stepnormal, i, into);
2100 VectorSubtract (ent->fields.server->velocity, into, side);
2101 ent->fields.server->velocity[0] = side[0] * (1 + d);
2102 ent->fields.server->velocity[1] = side[1] * (1 + d);
2108 =====================
2111 Player has come to a dead stop, possibly due to the problem with limited
2112 float precision at some angle joins in the BSP hull.
2114 Try fixing by pushing one pixel in each direction.
2116 This is a hack, but in the interest of good gameplay...
2117 ======================
2119 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2124 VectorCopy (ent->fields.server->origin, oldorg);
2127 for (i=0 ; i<8 ; i++)
2129 // try pushing a little in an axial direction
2132 case 0: dir[0] = 2; dir[1] = 0; break;
2133 case 1: dir[0] = 0; dir[1] = 2; break;
2134 case 2: dir[0] = -2; dir[1] = 0; break;
2135 case 3: dir[0] = 0; dir[1] = -2; break;
2136 case 4: dir[0] = 2; dir[1] = 2; break;
2137 case 5: dir[0] = -2; dir[1] = 2; break;
2138 case 6: dir[0] = 2; dir[1] = -2; break;
2139 case 7: dir[0] = -2; dir[1] = -2; break;
2142 SV_PushEntity (&trace, ent, dir, false, true);
2144 // retry the original move
2145 ent->fields.server->velocity[0] = oldvel[0];
2146 ent->fields.server->velocity[1] = oldvel[1];
2147 ent->fields.server->velocity[2] = 0;
2148 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2150 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2151 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2153 Con_DPrint("TryUnstick - success.\n");
2157 // go back to the original pos and try again
2158 VectorCopy (oldorg, ent->fields.server->origin);
2162 VectorClear (ent->fields.server->velocity);
2163 Con_DPrint("TryUnstick - failure.\n");
2169 =====================
2172 Only used by players
2173 ======================
2175 void SV_WalkMove (prvm_edict_t *ent)
2179 //int originalmove_clip;
2180 int originalmove_flags;
2181 int originalmove_groundentity;
2182 int hitsupercontentsmask;
2184 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2185 trace_t downtrace, trace;
2186 qboolean applygravity;
2188 // if frametime is 0 (due to client sending the same timestamp twice),
2190 if (sv.frametime <= 0)
2193 SV_CheckStuck (ent);
2195 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2197 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2199 SV_CheckVelocity(ent);
2201 // do a regular slide move unless it looks like you ran into a step
2202 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2204 VectorCopy (ent->fields.server->origin, start_origin);
2205 VectorCopy (ent->fields.server->velocity, start_velocity);
2207 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2209 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2212 // only try this if there was no floor in the way in the trace (no,
2213 // this check seems to be not REALLY necessary, because if clip & 1,
2214 // our trace will hit that thing too)
2215 VectorSet(upmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + 1);
2216 VectorSet(downmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
2217 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
2218 type = MOVE_MISSILE;
2219 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
2220 type = MOVE_NOMONSTERS; // only clip against bmodels
2223 trace = SV_TraceBox(upmove, ent->fields.server->mins, ent->fields.server->maxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2224 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2225 clip |= 1; // but we HAVE found a floor
2228 // if the move did not hit the ground at any point, we're not on ground
2230 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2232 SV_CheckVelocity(ent);
2234 SV_LinkEdict_TouchAreaGrid(ent);
2236 if(clip & 8) // teleport
2239 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2242 if (sv_nostep.integer)
2245 VectorCopy(ent->fields.server->origin, originalmove_origin);
2246 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2247 //originalmove_clip = clip;
2248 originalmove_flags = (int)ent->fields.server->flags;
2249 originalmove_groundentity = ent->fields.server->groundentity;
2251 // if move didn't block on a step, return
2254 // if move was not trying to move into the step, return
2255 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2258 if (ent->fields.server->movetype != MOVETYPE_FLY)
2260 // return if gibbed by a trigger
2261 if (ent->fields.server->movetype != MOVETYPE_WALK)
2264 // only step up while jumping if that is enabled
2265 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2266 if (!oldonground && ent->fields.server->waterlevel == 0)
2270 // try moving up and forward to go up a step
2271 // back to start pos
2272 VectorCopy (start_origin, ent->fields.server->origin);
2273 VectorCopy (start_velocity, ent->fields.server->velocity);
2276 VectorClear (upmove);
2277 upmove[2] = sv_stepheight.value;
2278 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2280 // we got teleported when upstepping... must abort the move
2285 ent->fields.server->velocity[2] = 0;
2286 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2287 ent->fields.server->velocity[2] += start_velocity[2];
2290 // we got teleported when upstepping... must abort the move
2291 // note that z velocity handling may not be what QC expects here, but we cannot help it
2295 SV_CheckVelocity(ent);
2297 SV_LinkEdict_TouchAreaGrid(ent);
2299 // check for stuckness, possibly due to the limited precision of floats
2300 // in the clipping hulls
2302 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2303 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2305 //Con_Printf("wall\n");
2306 // stepping up didn't make any progress, revert to original move
2307 VectorCopy(originalmove_origin, ent->fields.server->origin);
2308 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2309 //clip = originalmove_clip;
2310 ent->fields.server->flags = originalmove_flags;
2311 ent->fields.server->groundentity = originalmove_groundentity;
2312 // now try to unstick if needed
2313 //clip = SV_TryUnstick (ent, oldvel);
2317 //Con_Printf("step - ");
2319 // extra friction based on view angle
2320 if (clip & 2 && sv_wallfriction.integer)
2321 SV_WallFriction (ent, stepnormal);
2323 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2324 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))
2328 VectorClear (downmove);
2329 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2330 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2332 // we got teleported when downstepping... must abort the move
2336 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2338 // this has been disabled so that you can't jump when you are stepping
2339 // up while already jumping (also known as the Quake2 double jump bug)
2341 // LordHavoc: disabled this check so you can walk on monsters/players
2342 //if (ent->fields.server->solid == SOLID_BSP)
2344 //Con_Printf("onground\n");
2345 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2346 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2352 //Con_Printf("slope\n");
2353 // if the push down didn't end up on good ground, use the move without
2354 // the step up. This happens near wall / slope combinations, and can
2355 // cause the player to hop up higher on a slope too steep to climb
2356 VectorCopy(originalmove_origin, ent->fields.server->origin);
2357 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2358 //clip = originalmove_clip;
2359 ent->fields.server->flags = originalmove_flags;
2360 ent->fields.server->groundentity = originalmove_groundentity;
2363 SV_CheckVelocity(ent);
2365 SV_LinkEdict_TouchAreaGrid(ent);
2368 //============================================================================
2374 Entities that are "stuck" to another entity
2377 void SV_Physics_Follow (prvm_edict_t *ent)
2379 vec3_t vf, vr, vu, angles, v;
2383 if (!SV_RunThink (ent))
2386 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2387 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2388 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])
2390 // quick case for no rotation
2391 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2395 angles[0] = -ent->fields.server->punchangle[0];
2396 angles[1] = ent->fields.server->punchangle[1];
2397 angles[2] = ent->fields.server->punchangle[2];
2398 AngleVectors (angles, vf, vr, vu);
2399 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];
2400 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];
2401 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];
2402 angles[0] = -e->fields.server->angles[0];
2403 angles[1] = e->fields.server->angles[1];
2404 angles[2] = e->fields.server->angles[2];
2405 AngleVectors (angles, vf, vr, vu);
2406 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2407 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2408 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2410 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2412 //SV_LinkEdict_TouchAreaGrid(ent);
2416 ==============================================================================
2420 ==============================================================================
2425 SV_CheckWaterTransition
2429 void SV_CheckWaterTransition (prvm_edict_t *ent)
2432 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2433 if (!ent->fields.server->watertype)
2435 // just spawned here
2436 ent->fields.server->watertype = cont;
2437 ent->fields.server->waterlevel = 1;
2441 // DRESK - Support for Entity Contents Transition Event
2442 // NOTE: Call here BEFORE updating the watertype below,
2443 // and suppress watersplash sound if a valid function
2444 // call was made to allow for custom "splash" sounds.
2445 if( !SV_CheckContentsTransition(ent, cont) )
2446 { // Contents Transition Function Invalid; Potentially Play Water Sound
2447 // check if the entity crossed into or out of water
2448 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2449 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2452 if (cont <= CONTENTS_WATER)
2454 ent->fields.server->watertype = cont;
2455 ent->fields.server->waterlevel = 1;
2459 ent->fields.server->watertype = CONTENTS_EMPTY;
2460 ent->fields.server->waterlevel = 0;
2468 Toss, bounce, and fly movement. When onground, do nothing.
2471 void SV_Physics_Toss (prvm_edict_t *ent)
2477 prvm_edict_t *groundentity;
2479 // if onground, return without moving
2480 if ((int)ent->fields.server->flags & FL_ONGROUND)
2482 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2483 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2485 // don't stick to ground if onground and moving upward
2486 ent->fields.server->flags -= FL_ONGROUND;
2488 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2490 // we can trust FL_ONGROUND if groundentity is world because it never moves
2493 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2495 // if ent was supported by a brush model on previous frame,
2496 // and groundentity is now freed, set groundentity to 0 (world)
2497 // which leaves it suspended in the air
2498 ent->fields.server->groundentity = 0;
2499 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2502 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2504 // don't slide if still touching the groundentity
2508 ent->priv.server->suspendedinairflag = false;
2510 SV_CheckVelocity (ent);
2513 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2514 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2517 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2519 movetime = sv.frametime;
2520 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2523 VectorScale (ent->fields.server->velocity, movetime, move);
2524 if(!SV_PushEntity (&trace, ent, move, true, true))
2525 return; // teleported
2526 if (ent->priv.server->free)
2528 if (trace.bmodelstartsolid)
2530 // try to unstick the entity
2531 SV_UnstickEntity(ent);
2532 if(!SV_PushEntity (&trace, ent, move, false, true))
2533 return; // teleported
2534 if (ent->priv.server->free)
2537 if (trace.fraction == 1)
2539 movetime *= 1 - min(1, trace.fraction);
2540 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2543 float bouncefactor = 1.0f;
2544 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2545 if (val!=0 && val->_float)
2546 bouncefactor = val->_float;
2548 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2549 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2551 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2553 float d, ent_gravity;
2555 float bouncefactor = 0.5f;
2556 float bouncestop = 60.0f / 800.0f;
2558 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2559 if (val!=0 && val->_float)
2560 bouncefactor = val->_float;
2562 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2563 if (val!=0 && val->_float)
2564 bouncestop = val->_float;
2566 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2567 // LordHavoc: fixed grenades not bouncing when fired down a slope
2568 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2569 if (val!=0 && val->_float)
2570 ent_gravity = val->_float;
2573 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2575 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2576 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2578 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2579 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2580 VectorClear (ent->fields.server->velocity);
2581 VectorClear (ent->fields.server->avelocity);
2584 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2588 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2590 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2591 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2592 VectorClear (ent->fields.server->velocity);
2593 VectorClear (ent->fields.server->avelocity);
2596 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2601 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2602 if (trace.plane.normal[2] > 0.7)
2604 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2605 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2606 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2607 ent->priv.server->suspendedinairflag = true;
2608 VectorClear (ent->fields.server->velocity);
2609 VectorClear (ent->fields.server->avelocity);
2612 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2614 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2618 // check for in water
2619 SV_CheckWaterTransition (ent);
2623 ===============================================================================
2627 ===============================================================================
2634 Monsters freefall when they don't have a ground entity, otherwise
2635 all movement is done with discrete steps.
2637 This is also used for objects that have become still on the ground, but
2638 will fall if the floor is pulled out from under them.
2641 void SV_Physics_Step (prvm_edict_t *ent)
2643 int flags = (int)ent->fields.server->flags;
2646 // Backup Velocity in the event that movetypesteplandevent is called,
2647 // to provide a parameter with the entity's velocity at impact.
2648 prvm_eval_t *movetypesteplandevent;
2649 vec3_t backupVelocity;
2650 VectorCopy(ent->fields.server->velocity, backupVelocity);
2651 // don't fall at all if fly/swim
2652 if (!(flags & (FL_FLY | FL_SWIM)))
2654 if (flags & FL_ONGROUND)
2656 // freefall if onground and moving upward
2657 // freefall if not standing on a world surface (it may be a lift or trap door)
2658 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2660 ent->fields.server->flags -= FL_ONGROUND;
2661 SV_CheckVelocity(ent);
2662 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2664 SV_LinkEdict_TouchAreaGrid(ent);
2665 ent->priv.server->waterposition_forceupdate = true;
2670 // freefall if not onground
2671 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2673 SV_CheckVelocity(ent);
2674 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2676 SV_LinkEdict_TouchAreaGrid(ent);
2679 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2681 // DRESK - Check for Entity Land Event Function
2682 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2684 if(movetypesteplandevent->function)
2685 { // Valid Function; Execute
2686 // Prepare Parameters
2687 // Assign Velocity at Impact
2688 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2689 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2690 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2692 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2693 // Execute VM Function
2694 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2697 // Check for Engine Landing Sound
2698 if(sv_sound_land.string)
2699 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2701 ent->priv.server->waterposition_forceupdate = true;
2706 if (!SV_RunThink(ent))
2709 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2711 ent->priv.server->waterposition_forceupdate = false;
2712 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2713 SV_CheckWaterTransition(ent);
2717 //============================================================================
2719 static void SV_Physics_Entity (prvm_edict_t *ent)
2721 // don't run think/move on newly spawned projectiles as it messes up
2722 // movement interpolation and rocket trails, and is inconsistent with
2723 // respect to entities spawned in the same frame
2724 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2725 // but if it spawns a lower numbered ent, it doesn't - this never moves
2726 // ents in the first frame regardless)
2727 qboolean runmove = ent->priv.server->move;
2728 ent->priv.server->move = true;
2729 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2731 switch ((int) ent->fields.server->movetype)
2734 case MOVETYPE_FAKEPUSH:
2735 SV_Physics_Pusher (ent);
2738 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2739 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2742 case MOVETYPE_FOLLOW:
2743 SV_Physics_Follow (ent);
2745 case MOVETYPE_NOCLIP:
2746 if (SV_RunThink(ent))
2749 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2750 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2755 SV_Physics_Step (ent);
2758 if (SV_RunThink (ent))
2762 case MOVETYPE_BOUNCE:
2763 case MOVETYPE_BOUNCEMISSILE:
2764 case MOVETYPE_FLYMISSILE:
2767 if (SV_RunThink (ent))
2768 SV_Physics_Toss (ent);
2770 case MOVETYPE_PHYSICS:
2771 if (SV_RunThink(ent))
2774 SV_LinkEdict_TouchAreaGrid(ent);
2778 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2783 void SV_Physics_ClientMove(void)
2786 ent = host_client->edict;
2788 // call player physics, this needs the proper frametime
2789 prog->globals.server->frametime = sv.frametime;
2792 // call standard client pre-think, with frametime = 0
2793 prog->globals.server->time = sv.time;
2794 prog->globals.server->frametime = 0;
2795 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2796 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2797 prog->globals.server->frametime = sv.frametime;
2799 // make sure the velocity is sane (not a NaN)
2800 SV_CheckVelocity(ent);
2802 // perform MOVETYPE_WALK behavior
2805 // call standard player post-think, with frametime = 0
2806 prog->globals.server->time = sv.time;
2807 prog->globals.server->frametime = 0;
2808 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2809 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2810 prog->globals.server->frametime = sv.frametime;
2812 if(ent->fields.server->fixangle)
2814 // angle fixing was requested by physics code...
2815 // so store the current angles for later use
2816 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2817 host_client->fixangle_angles_set = TRUE;
2819 // and clear fixangle for the next frame
2820 ent->fields.server->fixangle = 0;
2824 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2826 // don't do physics on disconnected clients, FrikBot relies on this
2827 if (!host_client->spawned)
2830 // make sure the velocity is sane (not a NaN)
2831 SV_CheckVelocity(ent);
2833 // don't run physics here if running asynchronously
2834 if (host_client->clmovement_inputtimeout <= 0)
2837 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2840 // make sure the velocity is still sane (not a NaN)
2841 SV_CheckVelocity(ent);
2843 // call standard client pre-think
2844 prog->globals.server->time = sv.time;
2845 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2846 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2848 // make sure the velocity is still sane (not a NaN)
2849 SV_CheckVelocity(ent);
2852 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2854 // don't do physics on disconnected clients, FrikBot relies on this
2855 if (!host_client->spawned)
2858 // make sure the velocity is sane (not a NaN)
2859 SV_CheckVelocity(ent);
2861 // call standard player post-think
2862 prog->globals.server->time = sv.time;
2863 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2864 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2866 // make sure the velocity is still sane (not a NaN)
2867 SV_CheckVelocity(ent);
2869 if(ent->fields.server->fixangle)
2871 // angle fixing was requested by physics code...
2872 // so store the current angles for later use
2873 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2874 host_client->fixangle_angles_set = TRUE;
2876 // and clear fixangle for the next frame
2877 ent->fields.server->fixangle = 0;
2880 // decrement the countdown variable used to decide when to go back to
2881 // synchronous physics
2882 if (host_client->clmovement_inputtimeout > sv.frametime)
2883 host_client->clmovement_inputtimeout -= sv.frametime;
2885 host_client->clmovement_inputtimeout = 0;
2888 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2890 // don't do physics on disconnected clients, FrikBot relies on this
2891 if (!host_client->spawned)
2893 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2897 // make sure the velocity is sane (not a NaN)
2898 SV_CheckVelocity(ent);
2900 switch ((int) ent->fields.server->movetype)
2903 case MOVETYPE_FAKEPUSH:
2904 SV_Physics_Pusher (ent);
2907 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2908 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2911 case MOVETYPE_FOLLOW:
2912 SV_Physics_Follow (ent);
2914 case MOVETYPE_NOCLIP:
2917 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2918 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2921 SV_Physics_Step (ent);
2925 // don't run physics here if running asynchronously
2926 if (host_client->clmovement_inputtimeout <= 0)
2930 case MOVETYPE_BOUNCE:
2931 case MOVETYPE_BOUNCEMISSILE:
2932 case MOVETYPE_FLYMISSILE:
2935 SV_Physics_Toss (ent);
2941 case MOVETYPE_PHYSICS:
2945 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2949 SV_CheckVelocity (ent);
2952 SV_LinkEdict_TouchAreaGrid(ent);
2954 SV_CheckVelocity (ent);
2963 void SV_Physics (void)
2968 // let the progs know that a new frame has started
2969 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2970 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2971 prog->globals.server->time = sv.time;
2972 prog->globals.server->frametime = sv.frametime;
2973 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2975 // run physics engine
2976 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2979 // treat each object in turn
2982 // if force_retouch, relink all the entities
2983 if (prog->globals.server->force_retouch > 0)
2984 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2985 if (!ent->priv.server->free)
2986 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2988 if (sv_gameplayfix_consistentplayerprethink.integer)
2990 // run physics on the client entities in 3 stages
2991 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2992 if (!ent->priv.server->free)
2993 SV_Physics_ClientEntity_PreThink(ent);
2995 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2996 if (!ent->priv.server->free)
2997 SV_Physics_ClientEntity(ent);
2999 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3000 if (!ent->priv.server->free)
3001 SV_Physics_ClientEntity_PostThink(ent);
3005 // run physics on the client entities
3006 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3008 if (!ent->priv.server->free)
3010 SV_Physics_ClientEntity_PreThink(ent);
3011 SV_Physics_ClientEntity(ent);
3012 SV_Physics_ClientEntity_PostThink(ent);
3017 // run physics on all the non-client entities
3018 if (!sv_freezenonclients.integer)
3020 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3021 if (!ent->priv.server->free)
3022 SV_Physics_Entity(ent);
3023 // make a second pass to see if any ents spawned this frame and make
3024 // sure they run their move/think
3025 if (sv_gameplayfix_delayprojectiles.integer < 0)
3026 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3027 if (!ent->priv.server->move && !ent->priv.server->free)
3028 SV_Physics_Entity(ent);
3031 if (prog->globals.server->force_retouch > 0)
3032 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
3034 // LordHavoc: endframe support
3035 if (prog->funcoffsets.EndFrame)
3037 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
3038 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
3039 prog->globals.server->time = sv.time;
3040 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
3043 // decrement prog->num_edicts if the highest number entities died
3044 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3046 if (!sv_freezenonclients.integer)
3047 sv.time += sv.frametime;