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->startsolid && 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)
2177 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask, type;
2178 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2179 trace_t downtrace, trace;
2180 qboolean applygravity;
2182 // if frametime is 0 (due to client sending the same timestamp twice),
2184 if (sv.frametime <= 0)
2187 SV_CheckStuck (ent);
2189 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2191 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2193 SV_CheckVelocity(ent);
2195 // do a regular slide move unless it looks like you ran into a step
2196 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2198 VectorCopy (ent->fields.server->origin, start_origin);
2199 VectorCopy (ent->fields.server->velocity, start_velocity);
2201 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2203 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2206 // only try this if there was no floor in the way in the trace (no,
2207 // this check seems to be not REALLY necessary, because if clip & 1,
2208 // our trace will hit that thing too)
2209 VectorSet(upmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + 1);
2210 VectorSet(downmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
2211 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
2212 type = MOVE_MISSILE;
2213 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
2214 type = MOVE_NOMONSTERS; // only clip against bmodels
2217 trace = SV_TraceBox(upmove, ent->fields.server->mins, ent->fields.server->maxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2218 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2219 clip |= 1; // but we HAVE found a floor
2222 // if the move did not hit the ground at any point, we're not on ground
2224 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2226 SV_CheckVelocity(ent);
2228 SV_LinkEdict_TouchAreaGrid(ent);
2230 if(clip & 8) // teleport
2233 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2236 if (sv_nostep.integer)
2239 VectorCopy(ent->fields.server->origin, originalmove_origin);
2240 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2241 originalmove_clip = clip;
2242 originalmove_flags = (int)ent->fields.server->flags;
2243 originalmove_groundentity = ent->fields.server->groundentity;
2245 // if move didn't block on a step, return
2248 // if move was not trying to move into the step, return
2249 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2252 if (ent->fields.server->movetype != MOVETYPE_FLY)
2254 // return if gibbed by a trigger
2255 if (ent->fields.server->movetype != MOVETYPE_WALK)
2258 // only step up while jumping if that is enabled
2259 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2260 if (!oldonground && ent->fields.server->waterlevel == 0)
2264 // try moving up and forward to go up a step
2265 // back to start pos
2266 VectorCopy (start_origin, ent->fields.server->origin);
2267 VectorCopy (start_velocity, ent->fields.server->velocity);
2270 VectorClear (upmove);
2271 upmove[2] = sv_stepheight.value;
2272 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2274 // we got teleported when upstepping... must abort the move
2279 ent->fields.server->velocity[2] = 0;
2280 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2281 ent->fields.server->velocity[2] += start_velocity[2];
2284 // we got teleported when upstepping... must abort the move
2285 // note that z velocity handling may not be what QC expects here, but we cannot help it
2289 SV_CheckVelocity(ent);
2291 SV_LinkEdict_TouchAreaGrid(ent);
2293 // check for stuckness, possibly due to the limited precision of floats
2294 // in the clipping hulls
2296 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2297 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2299 //Con_Printf("wall\n");
2300 // stepping up didn't make any progress, revert to original move
2301 VectorCopy(originalmove_origin, ent->fields.server->origin);
2302 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2303 //clip = originalmove_clip;
2304 ent->fields.server->flags = originalmove_flags;
2305 ent->fields.server->groundentity = originalmove_groundentity;
2306 // now try to unstick if needed
2307 //clip = SV_TryUnstick (ent, oldvel);
2311 //Con_Printf("step - ");
2313 // extra friction based on view angle
2314 if (clip & 2 && sv_wallfriction.integer)
2315 SV_WallFriction (ent, stepnormal);
2317 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2318 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))
2322 VectorClear (downmove);
2323 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2324 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2326 // we got teleported when downstepping... must abort the move
2330 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2332 // this has been disabled so that you can't jump when you are stepping
2333 // up while already jumping (also known as the Quake2 double jump bug)
2335 // LordHavoc: disabled this check so you can walk on monsters/players
2336 //if (ent->fields.server->solid == SOLID_BSP)
2338 //Con_Printf("onground\n");
2339 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2340 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2346 //Con_Printf("slope\n");
2347 // if the push down didn't end up on good ground, use the move without
2348 // the step up. This happens near wall / slope combinations, and can
2349 // cause the player to hop up higher on a slope too steep to climb
2350 VectorCopy(originalmove_origin, ent->fields.server->origin);
2351 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2352 //clip = originalmove_clip;
2353 ent->fields.server->flags = originalmove_flags;
2354 ent->fields.server->groundentity = originalmove_groundentity;
2357 SV_CheckVelocity(ent);
2359 SV_LinkEdict_TouchAreaGrid(ent);
2362 //============================================================================
2368 Entities that are "stuck" to another entity
2371 void SV_Physics_Follow (prvm_edict_t *ent)
2373 vec3_t vf, vr, vu, angles, v;
2377 if (!SV_RunThink (ent))
2380 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2381 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2382 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])
2384 // quick case for no rotation
2385 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2389 angles[0] = -ent->fields.server->punchangle[0];
2390 angles[1] = ent->fields.server->punchangle[1];
2391 angles[2] = ent->fields.server->punchangle[2];
2392 AngleVectors (angles, vf, vr, vu);
2393 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];
2394 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];
2395 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];
2396 angles[0] = -e->fields.server->angles[0];
2397 angles[1] = e->fields.server->angles[1];
2398 angles[2] = e->fields.server->angles[2];
2399 AngleVectors (angles, vf, vr, vu);
2400 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2401 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2402 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2404 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2406 //SV_LinkEdict_TouchAreaGrid(ent);
2410 ==============================================================================
2414 ==============================================================================
2419 SV_CheckWaterTransition
2423 void SV_CheckWaterTransition (prvm_edict_t *ent)
2426 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2427 if (!ent->fields.server->watertype)
2429 // just spawned here
2430 ent->fields.server->watertype = cont;
2431 ent->fields.server->waterlevel = 1;
2435 // DRESK - Support for Entity Contents Transition Event
2436 // NOTE: Call here BEFORE updating the watertype below,
2437 // and suppress watersplash sound if a valid function
2438 // call was made to allow for custom "splash" sounds.
2439 if( !SV_CheckContentsTransition(ent, cont) )
2440 { // Contents Transition Function Invalid; Potentially Play Water Sound
2441 // check if the entity crossed into or out of water
2442 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2443 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2446 if (cont <= CONTENTS_WATER)
2448 ent->fields.server->watertype = cont;
2449 ent->fields.server->waterlevel = 1;
2453 ent->fields.server->watertype = CONTENTS_EMPTY;
2454 ent->fields.server->waterlevel = 0;
2462 Toss, bounce, and fly movement. When onground, do nothing.
2465 void SV_Physics_Toss (prvm_edict_t *ent)
2471 prvm_edict_t *groundentity;
2473 // if onground, return without moving
2474 if ((int)ent->fields.server->flags & FL_ONGROUND)
2476 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2477 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2479 // don't stick to ground if onground and moving upward
2480 ent->fields.server->flags -= FL_ONGROUND;
2482 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2484 // we can trust FL_ONGROUND if groundentity is world because it never moves
2487 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2489 // if ent was supported by a brush model on previous frame,
2490 // and groundentity is now freed, set groundentity to 0 (world)
2491 // which leaves it suspended in the air
2492 ent->fields.server->groundentity = 0;
2493 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2496 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2498 // don't slide if still touching the groundentity
2502 ent->priv.server->suspendedinairflag = false;
2504 SV_CheckVelocity (ent);
2507 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2508 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2511 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2513 movetime = sv.frametime;
2514 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2517 VectorScale (ent->fields.server->velocity, movetime, move);
2518 if(!SV_PushEntity (&trace, ent, move, true, true))
2519 return; // teleported
2520 if (ent->priv.server->free)
2522 if (trace.bmodelstartsolid)
2524 // try to unstick the entity
2525 SV_UnstickEntity(ent);
2526 if(!SV_PushEntity (&trace, ent, move, false, true))
2527 return; // teleported
2528 if (ent->priv.server->free)
2531 if (trace.fraction == 1)
2533 movetime *= 1 - min(1, trace.fraction);
2534 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2537 float bouncefactor = 1.0f;
2538 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2539 if (val!=0 && val->_float)
2540 bouncefactor = val->_float;
2542 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2543 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2545 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2547 float d, ent_gravity;
2549 float bouncefactor = 0.5f;
2550 float bouncestop = 60.0f / 800.0f;
2552 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2553 if (val!=0 && val->_float)
2554 bouncefactor = val->_float;
2556 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2557 if (val!=0 && val->_float)
2558 bouncestop = val->_float;
2560 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2561 // LordHavoc: fixed grenades not bouncing when fired down a slope
2562 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2563 if (val!=0 && val->_float)
2564 ent_gravity = val->_float;
2567 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2569 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2570 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2572 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2573 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2574 VectorClear (ent->fields.server->velocity);
2575 VectorClear (ent->fields.server->avelocity);
2578 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2582 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2584 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2585 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2586 VectorClear (ent->fields.server->velocity);
2587 VectorClear (ent->fields.server->avelocity);
2590 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2595 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2596 if (trace.plane.normal[2] > 0.7)
2598 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2599 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2600 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2601 ent->priv.server->suspendedinairflag = true;
2602 VectorClear (ent->fields.server->velocity);
2603 VectorClear (ent->fields.server->avelocity);
2606 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2608 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2612 // check for in water
2613 SV_CheckWaterTransition (ent);
2617 ===============================================================================
2621 ===============================================================================
2628 Monsters freefall when they don't have a ground entity, otherwise
2629 all movement is done with discrete steps.
2631 This is also used for objects that have become still on the ground, but
2632 will fall if the floor is pulled out from under them.
2635 void SV_Physics_Step (prvm_edict_t *ent)
2637 int flags = (int)ent->fields.server->flags;
2640 // Backup Velocity in the event that movetypesteplandevent is called,
2641 // to provide a parameter with the entity's velocity at impact.
2642 prvm_eval_t *movetypesteplandevent;
2643 vec3_t backupVelocity;
2644 VectorCopy(ent->fields.server->velocity, backupVelocity);
2645 // don't fall at all if fly/swim
2646 if (!(flags & (FL_FLY | FL_SWIM)))
2648 if (flags & FL_ONGROUND)
2650 // freefall if onground and moving upward
2651 // freefall if not standing on a world surface (it may be a lift or trap door)
2652 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2654 ent->fields.server->flags -= FL_ONGROUND;
2655 SV_CheckVelocity(ent);
2656 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2658 SV_LinkEdict_TouchAreaGrid(ent);
2659 ent->priv.server->waterposition_forceupdate = true;
2664 // freefall if not onground
2665 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2667 SV_CheckVelocity(ent);
2668 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2670 SV_LinkEdict_TouchAreaGrid(ent);
2673 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2675 // DRESK - Check for Entity Land Event Function
2676 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2678 if(movetypesteplandevent->function)
2679 { // Valid Function; Execute
2680 // Prepare Parameters
2681 // Assign Velocity at Impact
2682 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2683 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2684 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2686 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2687 // Execute VM Function
2688 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2691 // Check for Engine Landing Sound
2692 if(sv_sound_land.string)
2693 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2695 ent->priv.server->waterposition_forceupdate = true;
2700 if (!SV_RunThink(ent))
2703 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2705 ent->priv.server->waterposition_forceupdate = false;
2706 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2707 SV_CheckWaterTransition(ent);
2711 //============================================================================
2713 static void SV_Physics_Entity (prvm_edict_t *ent)
2715 // don't run think/move on newly spawned projectiles as it messes up
2716 // movement interpolation and rocket trails, and is inconsistent with
2717 // respect to entities spawned in the same frame
2718 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2719 // but if it spawns a lower numbered ent, it doesn't - this never moves
2720 // ents in the first frame regardless)
2721 qboolean runmove = ent->priv.server->move;
2722 ent->priv.server->move = true;
2723 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2725 switch ((int) ent->fields.server->movetype)
2728 case MOVETYPE_FAKEPUSH:
2729 SV_Physics_Pusher (ent);
2732 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2733 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2736 case MOVETYPE_FOLLOW:
2737 SV_Physics_Follow (ent);
2739 case MOVETYPE_NOCLIP:
2740 if (SV_RunThink(ent))
2743 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2744 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2749 SV_Physics_Step (ent);
2752 if (SV_RunThink (ent))
2756 case MOVETYPE_BOUNCE:
2757 case MOVETYPE_BOUNCEMISSILE:
2758 case MOVETYPE_FLYMISSILE:
2761 if (SV_RunThink (ent))
2762 SV_Physics_Toss (ent);
2764 case MOVETYPE_PHYSICS:
2765 if (SV_RunThink(ent))
2768 SV_LinkEdict_TouchAreaGrid(ent);
2772 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2777 void SV_Physics_ClientMove(void)
2780 ent = host_client->edict;
2782 // call player physics, this needs the proper frametime
2783 prog->globals.server->frametime = sv.frametime;
2786 // call standard client pre-think, with frametime = 0
2787 prog->globals.server->time = sv.time;
2788 prog->globals.server->frametime = 0;
2789 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2790 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2791 prog->globals.server->frametime = sv.frametime;
2793 // make sure the velocity is sane (not a NaN)
2794 SV_CheckVelocity(ent);
2796 // perform MOVETYPE_WALK behavior
2799 // call standard player post-think, with frametime = 0
2800 prog->globals.server->time = sv.time;
2801 prog->globals.server->frametime = 0;
2802 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2803 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2804 prog->globals.server->frametime = sv.frametime;
2806 if(ent->fields.server->fixangle)
2808 // angle fixing was requested by physics code...
2809 // so store the current angles for later use
2810 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2811 host_client->fixangle_angles_set = TRUE;
2813 // and clear fixangle for the next frame
2814 ent->fields.server->fixangle = 0;
2818 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2820 // don't do physics on disconnected clients, FrikBot relies on this
2821 if (!host_client->spawned)
2824 // make sure the velocity is sane (not a NaN)
2825 SV_CheckVelocity(ent);
2827 // don't run physics here if running asynchronously
2828 if (host_client->clmovement_inputtimeout <= 0)
2831 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2834 // make sure the velocity is still sane (not a NaN)
2835 SV_CheckVelocity(ent);
2837 // call standard client pre-think
2838 prog->globals.server->time = sv.time;
2839 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2840 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2842 // make sure the velocity is still sane (not a NaN)
2843 SV_CheckVelocity(ent);
2846 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2848 // don't do physics on disconnected clients, FrikBot relies on this
2849 if (!host_client->spawned)
2852 // make sure the velocity is sane (not a NaN)
2853 SV_CheckVelocity(ent);
2855 // call standard player post-think
2856 prog->globals.server->time = sv.time;
2857 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2858 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2860 // make sure the velocity is still sane (not a NaN)
2861 SV_CheckVelocity(ent);
2863 if(ent->fields.server->fixangle)
2865 // angle fixing was requested by physics code...
2866 // so store the current angles for later use
2867 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2868 host_client->fixangle_angles_set = TRUE;
2870 // and clear fixangle for the next frame
2871 ent->fields.server->fixangle = 0;
2874 // decrement the countdown variable used to decide when to go back to
2875 // synchronous physics
2876 if (host_client->clmovement_inputtimeout > sv.frametime)
2877 host_client->clmovement_inputtimeout -= sv.frametime;
2879 host_client->clmovement_inputtimeout = 0;
2882 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2884 // don't do physics on disconnected clients, FrikBot relies on this
2885 if (!host_client->spawned)
2887 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2891 // make sure the velocity is sane (not a NaN)
2892 SV_CheckVelocity(ent);
2894 switch ((int) ent->fields.server->movetype)
2897 case MOVETYPE_FAKEPUSH:
2898 SV_Physics_Pusher (ent);
2901 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2902 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2905 case MOVETYPE_FOLLOW:
2906 SV_Physics_Follow (ent);
2908 case MOVETYPE_NOCLIP:
2911 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2912 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2915 SV_Physics_Step (ent);
2919 // don't run physics here if running asynchronously
2920 if (host_client->clmovement_inputtimeout <= 0)
2924 case MOVETYPE_BOUNCE:
2925 case MOVETYPE_BOUNCEMISSILE:
2926 case MOVETYPE_FLYMISSILE:
2929 SV_Physics_Toss (ent);
2935 case MOVETYPE_PHYSICS:
2939 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2943 SV_CheckVelocity (ent);
2946 SV_LinkEdict_TouchAreaGrid(ent);
2948 SV_CheckVelocity (ent);
2957 void SV_Physics (void)
2962 // let the progs know that a new frame has started
2963 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2964 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2965 prog->globals.server->time = sv.time;
2966 prog->globals.server->frametime = sv.frametime;
2967 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2969 // run physics engine
2970 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2973 // treat each object in turn
2976 // if force_retouch, relink all the entities
2977 if (prog->globals.server->force_retouch > 0)
2978 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2979 if (!ent->priv.server->free)
2980 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2982 if (sv_gameplayfix_consistentplayerprethink.integer)
2984 // run physics on the client entities in 3 stages
2985 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2986 if (!ent->priv.server->free)
2987 SV_Physics_ClientEntity_PreThink(ent);
2989 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2990 if (!ent->priv.server->free)
2991 SV_Physics_ClientEntity(ent);
2993 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2994 if (!ent->priv.server->free)
2995 SV_Physics_ClientEntity_PostThink(ent);
2999 // run physics on the client entities
3000 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3002 if (!ent->priv.server->free)
3004 SV_Physics_ClientEntity_PreThink(ent);
3005 SV_Physics_ClientEntity(ent);
3006 SV_Physics_ClientEntity_PostThink(ent);
3011 // run physics on all the non-client entities
3012 if (!sv_freezenonclients.integer)
3014 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3015 if (!ent->priv.server->free)
3016 SV_Physics_Entity(ent);
3017 // make a second pass to see if any ents spawned this frame and make
3018 // sure they run their move/think
3019 if (sv_gameplayfix_delayprojectiles.integer < 0)
3020 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3021 if (!ent->priv.server->move && !ent->priv.server->free)
3022 SV_Physics_Entity(ent);
3025 if (prog->globals.server->force_retouch > 0)
3026 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
3028 // LordHavoc: endframe support
3029 if (prog->funcoffsets.EndFrame)
3031 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
3032 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
3033 prog->globals.server->time = sv.time;
3034 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
3037 // decrement prog->num_edicts if the highest number entities died
3038 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3040 if (!sv_freezenonclients.integer)
3041 sv.time += sv.frametime;