2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
29 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38 solid_edge items only clip against bsp models.
42 #define MOVE_EPSILON 0.01
44 void SV_Physics_Toss (prvm_edict_t *ent);
46 int SV_GetPitchSign(prvm_edict_t *ent)
50 (model = SV_GetModelFromEdict(ent))
52 model->type == mod_alias
55 (((unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
57 ((gamemode == GAME_TENEBRAE) && ((unsigned int)ent->fields.server->effects & (16 | 32)))
65 ===============================================================================
69 ===============================================================================
72 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
77 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
78 if (val && val->_float)
79 return (int)val->_float;
80 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
82 if ((int)passedict->fields.server->flags & FL_MONSTER)
83 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
85 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
87 else if (passedict->fields.server->solid == SOLID_CORPSE)
88 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
89 else if (passedict->fields.server->solid == SOLID_TRIGGER)
90 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
92 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
95 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
103 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
105 int i, bodysupercontents;
108 prvm_edict_t *traceowner, *touch;
110 // bounding box of entire move area
111 vec3_t clipboxmins, clipboxmaxs;
112 // size when clipping against monsters
113 vec3_t clipmins2, clipmaxs2;
114 // start and end origin of move
118 // matrices to transform into/out of other entity's space
119 matrix4x4_t matrix, imatrix;
120 // model of other entity
122 // list of entities to test for collisions
124 static prvm_edict_t *touchedicts[MAX_EDICTS];
126 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
128 VectorCopy(start, clipstart);
129 VectorClear(clipmins2);
130 VectorClear(clipmaxs2);
131 #if COLLISIONPARANOID >= 3
132 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
136 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
137 cliptrace.bmodelstartsolid = cliptrace.startsolid;
138 if (cliptrace.startsolid || cliptrace.fraction < 1)
139 cliptrace.ent = prog->edicts;
140 if (type == MOVE_WORLDONLY)
143 if (type == MOVE_MISSILE)
145 // LordHavoc: modified this, was = -15, now -= 15
146 for (i = 0;i < 3;i++)
153 // create the bounding box of the entire move
154 for (i = 0;i < 3;i++)
156 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
157 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
160 // debug override to test against everything
161 if (sv_debugmove.integer)
163 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
164 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
167 // if the passedict is world, make it NULL (to avoid two checks each time)
168 if (passedict == prog->edicts)
170 // precalculate prog value for passedict for comparisons
171 passedictprog = PRVM_EDICT_TO_PROG(passedict);
172 // precalculate passedict's owner edict pointer for comparisons
173 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
176 // because this uses World_EntitiestoBox, we know all entity boxes overlap
177 // the clip region, so we can skip culling checks in the loop below
178 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
179 if (numtouchedicts > MAX_EDICTS)
181 // this never happens
182 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
183 numtouchedicts = MAX_EDICTS;
185 for (i = 0;i < numtouchedicts;i++)
187 touch = touchedicts[i];
189 if (touch->fields.server->solid < SOLID_BBOX)
191 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
196 // don't clip against self
197 if (passedict == touch)
199 // don't clip owned entities against owner
200 if (traceowner == touch)
202 // don't clip owner against owned entities
203 if (passedictprog == touch->fields.server->owner)
205 // don't clip points against points (they can't collide)
206 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
210 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
212 // might interact, so do an exact clip
214 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
216 model = SV_GetModelFromEdict(touch);
217 pitchsign = SV_GetPitchSign(touch);
220 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
222 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
223 Matrix4x4_Invert_Simple(&imatrix, &matrix);
224 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
225 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
226 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
227 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
228 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
230 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
232 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
244 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
245 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
247 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
250 int i, bodysupercontents;
253 prvm_edict_t *traceowner, *touch;
255 // bounding box of entire move area
256 vec3_t clipboxmins, clipboxmaxs;
257 // size when clipping against monsters
258 vec3_t clipmins2, clipmaxs2;
259 // start and end origin of move
260 vec3_t clipstart, clipend;
263 // matrices to transform into/out of other entity's space
264 matrix4x4_t matrix, imatrix;
265 // model of other entity
267 // list of entities to test for collisions
269 static prvm_edict_t *touchedicts[MAX_EDICTS];
270 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
274 if (VectorCompare(start, pEnd))
275 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
277 if(collision_endposnudge.value > 0)
279 // TRICK: make the trace 1 qu longer!
280 VectorSubtract(pEnd, start, end);
281 len = VectorNormalizeLength(end);
282 VectorMA(pEnd, collision_endposnudge.value, end, end);
285 VectorCopy(pEnd, end);
287 if (VectorCompare(start, end))
288 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
291 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
293 VectorCopy(start, clipstart);
294 VectorCopy(end, clipend);
295 VectorClear(clipmins2);
296 VectorClear(clipmaxs2);
297 #if COLLISIONPARANOID >= 3
298 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
302 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask);
303 cliptrace.bmodelstartsolid = cliptrace.startsolid;
304 if (cliptrace.startsolid || cliptrace.fraction < 1)
305 cliptrace.ent = prog->edicts;
306 if (type == MOVE_WORLDONLY)
309 if (type == MOVE_MISSILE)
311 // LordHavoc: modified this, was = -15, now -= 15
312 for (i = 0;i < 3;i++)
319 // create the bounding box of the entire move
320 for (i = 0;i < 3;i++)
322 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
323 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
326 // debug override to test against everything
327 if (sv_debugmove.integer)
329 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
330 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
333 // if the passedict is world, make it NULL (to avoid two checks each time)
334 if (passedict == prog->edicts)
336 // precalculate prog value for passedict for comparisons
337 passedictprog = PRVM_EDICT_TO_PROG(passedict);
338 // precalculate passedict's owner edict pointer for comparisons
339 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
342 // because this uses World_EntitiestoBox, we know all entity boxes overlap
343 // the clip region, so we can skip culling checks in the loop below
344 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
345 if (numtouchedicts > MAX_EDICTS)
347 // this never happens
348 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
349 numtouchedicts = MAX_EDICTS;
351 for (i = 0;i < numtouchedicts;i++)
353 touch = touchedicts[i];
355 if (touch->fields.server->solid < SOLID_BBOX)
357 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
362 // don't clip against self
363 if (passedict == touch)
365 // don't clip owned entities against owner
366 if (traceowner == touch)
368 // don't clip owner against owned entities
369 if (passedictprog == touch->fields.server->owner)
371 // don't clip points against points (they can't collide)
372 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
376 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
378 // might interact, so do an exact clip
380 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
382 model = SV_GetModelFromEdict(touch);
383 pitchsign = SV_GetPitchSign(touch);
386 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
388 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
389 Matrix4x4_Invert_Simple(&imatrix, &matrix);
390 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
391 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
392 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
393 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
394 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
396 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
398 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
402 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
403 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
404 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
414 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
415 #if COLLISIONPARANOID >= 1
416 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
418 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
421 #if COLLISIONPARANOID >= 1
422 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
424 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
428 vec3_t hullmins, hullmaxs;
429 int i, bodysupercontents;
433 prvm_edict_t *traceowner, *touch;
435 // bounding box of entire move area
436 vec3_t clipboxmins, clipboxmaxs;
437 // size of the moving object
438 vec3_t clipmins, clipmaxs;
439 // size when clipping against monsters
440 vec3_t clipmins2, clipmaxs2;
441 // start and end origin of move
442 vec3_t clipstart, clipend;
445 // matrices to transform into/out of other entity's space
446 matrix4x4_t matrix, imatrix;
447 // model of other entity
449 // list of entities to test for collisions
451 static prvm_edict_t *touchedicts[MAX_EDICTS];
452 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
456 if (VectorCompare(mins, maxs))
458 vec3_t shiftstart, shiftend;
459 VectorAdd(start, mins, shiftstart);
460 VectorAdd(pEnd, mins, shiftend);
461 if (VectorCompare(start, pEnd))
462 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
464 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
465 VectorSubtract(trace.endpos, mins, trace.endpos);
469 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
471 // TRICK: make the trace 1 qu longer!
472 VectorSubtract(pEnd, start, end);
473 len = VectorNormalizeLength(end);
474 VectorMA(pEnd, collision_endposnudge.value, end, end);
477 VectorCopy(pEnd, end);
479 if (VectorCompare(mins, maxs))
481 vec3_t shiftstart, shiftend;
482 VectorAdd(start, mins, shiftstart);
483 VectorAdd(end, mins, shiftend);
484 if (VectorCompare(start, end))
485 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
487 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
488 VectorSubtract(trace.endpos, mins, trace.endpos);
493 VectorCopy(start, clipstart);
494 VectorCopy(end, clipend);
495 VectorCopy(mins, clipmins);
496 VectorCopy(maxs, clipmaxs);
497 VectorCopy(mins, clipmins2);
498 VectorCopy(maxs, clipmaxs2);
499 #if COLLISIONPARANOID >= 3
500 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
504 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
505 cliptrace.bmodelstartsolid = cliptrace.startsolid;
506 if (cliptrace.startsolid || cliptrace.fraction < 1)
507 cliptrace.ent = prog->edicts;
508 if (type == MOVE_WORLDONLY)
511 if (type == MOVE_MISSILE)
513 // LordHavoc: modified this, was = -15, now -= 15
514 for (i = 0;i < 3;i++)
521 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
522 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
523 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
526 VectorCopy(clipmins, hullmins);
527 VectorCopy(clipmaxs, hullmaxs);
530 // create the bounding box of the entire move
531 for (i = 0;i < 3;i++)
533 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
534 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
537 // debug override to test against everything
538 if (sv_debugmove.integer)
540 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
541 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
544 // if the passedict is world, make it NULL (to avoid two checks each time)
545 if (passedict == prog->edicts)
547 // precalculate prog value for passedict for comparisons
548 passedictprog = PRVM_EDICT_TO_PROG(passedict);
549 // figure out whether this is a point trace for comparisons
550 pointtrace = VectorCompare(clipmins, clipmaxs);
551 // precalculate passedict's owner edict pointer for comparisons
552 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
555 // because this uses World_EntitiestoBox, we know all entity boxes overlap
556 // the clip region, so we can skip culling checks in the loop below
557 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
558 if (numtouchedicts > MAX_EDICTS)
560 // this never happens
561 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
562 numtouchedicts = MAX_EDICTS;
564 for (i = 0;i < numtouchedicts;i++)
566 touch = touchedicts[i];
568 if (touch->fields.server->solid < SOLID_BBOX)
570 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
575 // don't clip against self
576 if (passedict == touch)
578 // don't clip owned entities against owner
579 if (traceowner == touch)
581 // don't clip owner against owned entities
582 if (passedictprog == touch->fields.server->owner)
584 // don't clip points against points (they can't collide)
585 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
589 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
591 // might interact, so do an exact clip
593 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
595 model = SV_GetModelFromEdict(touch);
596 pitchsign = SV_GetPitchSign(touch);
599 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
601 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
602 Matrix4x4_Invert_Simple(&imatrix, &matrix);
603 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
604 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
605 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
606 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
607 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
609 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
611 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
615 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
616 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
617 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
622 #if COLLISIONPARANOID >= 1
623 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
628 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
631 VectorCopy(trace.endpos, temp);
632 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
633 #if COLLISIONPARANOID < 3
634 if (trace.startsolid || endstuck)
636 Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "^3" : "", passedict ? (int)(passedict - prog->edicts) : -1, passedict->fields.server->origin[0], passedict->fields.server->origin[1], passedict->fields.server->origin[2], end[0] - passedict->fields.server->origin[0], end[1] - passedict->fields.server->origin[1], end[2] - passedict->fields.server->origin[2], trace.fraction, trace.endpos[0] - passedict->fields.server->origin[0], trace.endpos[1] - passedict->fields.server->origin[1], trace.endpos[2] - passedict->fields.server->origin[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
642 int SV_PointSuperContents(const vec3_t point)
644 int supercontents = 0;
648 // matrices to transform into/out of other entity's space
649 matrix4x4_t matrix, imatrix;
650 // model of other entity
653 // list of entities to test for collisions
655 static prvm_edict_t *touchedicts[MAX_EDICTS];
657 // get world supercontents at this point
658 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
659 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
661 // if sv_gameplayfix_swiminbmodels is off we're done
662 if (!sv_gameplayfix_swiminbmodels.integer)
663 return supercontents;
665 // get list of entities at this point
666 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
667 if (numtouchedicts > MAX_EDICTS)
669 // this never happens
670 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
671 numtouchedicts = MAX_EDICTS;
673 for (i = 0;i < numtouchedicts;i++)
675 touch = touchedicts[i];
677 // we only care about SOLID_BSP for pointcontents
678 if (touch->fields.server->solid != SOLID_BSP)
681 // might interact, so do an exact clip
682 model = SV_GetModelFromEdict(touch);
683 if (!model || !model->PointSuperContents)
685 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
686 Matrix4x4_Invert_Simple(&imatrix, &matrix);
687 Matrix4x4_Transform(&imatrix, point, transformed);
688 frame = (int)touch->fields.server->frame;
689 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
692 return supercontents;
696 ===============================================================================
698 Linking entities into the world culling system
700 ===============================================================================
703 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
706 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
707 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
708 prog->globals.server->time = sv.time;
709 prog->globals.server->trace_allsolid = false;
710 prog->globals.server->trace_startsolid = false;
711 prog->globals.server->trace_fraction = 1;
712 prog->globals.server->trace_inwater = false;
713 prog->globals.server->trace_inopen = true;
714 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
715 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
716 prog->globals.server->trace_plane_dist = 0;
717 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
718 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
720 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
722 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
724 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
726 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
729 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
731 int i, numtouchedicts, old_self, old_other;
733 static prvm_edict_t *touchedicts[MAX_EDICTS];
735 if (ent == prog->edicts)
736 return; // don't add the world
738 if (ent->priv.server->free)
741 if (ent->fields.server->solid == SOLID_NOT)
744 // build a list of edicts to touch, because the link loop can be corrupted
745 // by IncreaseEdicts called during touch functions
746 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
747 if (numtouchedicts > MAX_EDICTS)
749 // this never happens
750 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
751 numtouchedicts = MAX_EDICTS;
754 old_self = prog->globals.server->self;
755 old_other = prog->globals.server->other;
756 for (i = 0;i < numtouchedicts;i++)
758 touch = touchedicts[i];
759 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
761 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
764 prog->globals.server->self = old_self;
765 prog->globals.server->other = old_other;
768 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
772 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
774 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
775 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
776 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
777 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
778 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
779 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
780 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
781 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
782 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
783 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
784 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
785 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
786 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
787 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
788 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
789 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
790 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
791 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
792 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
793 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
794 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
795 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
796 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
805 void SV_LinkEdict (prvm_edict_t *ent)
811 if (ent == prog->edicts)
812 return; // don't add the world
814 if (ent->priv.server->free)
817 modelindex = (int)ent->fields.server->modelindex;
818 if (modelindex < 0 || modelindex >= MAX_MODELS)
820 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
823 model = SV_GetModelByIndex(modelindex);
825 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
826 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
827 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
831 if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
833 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
834 // TODO special handling for spheres?
835 RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs);
836 VectorAdd(ent->fields.server->origin, mins, mins);
837 VectorAdd(ent->fields.server->origin, maxs, maxs);
839 else if (ent->fields.server->solid == SOLID_BSP)
843 if (!model->TraceBox)
844 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
846 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
848 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
849 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
851 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
853 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
854 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
858 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
859 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
864 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
865 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
866 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
871 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
872 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
876 // to make items easier to pick up and allow them to be grabbed off
877 // of shelves, the abs sizes are expanded
879 if ((int)ent->fields.server->flags & FL_ITEM)
890 // because movement is clipped an epsilon away from an actual edge,
891 // we must fully check even when bounding boxes don't quite touch
900 VectorCopy(mins, ent->fields.server->absmin);
901 VectorCopy(maxs, ent->fields.server->absmax);
903 World_LinkEdict(&sv.world, ent, mins, maxs);
907 ===============================================================================
911 ===============================================================================
916 SV_TestEntityPosition
918 returns true if the entity is in solid currently
921 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
926 contents = SV_GenericHitSuperContentsMask(ent);
927 VectorAdd(ent->fields.server->origin, offset, org);
928 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
929 if (trace.startsupercontents & contents)
933 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
935 // q1bsp/hlbsp use hulls and if the entity does not exactly match
936 // a hull size it is incorrectly tested, so this code tries to
937 // 'fix' it slightly...
938 // FIXME: this breaks entities larger than the hull size
941 VectorAdd(org, ent->fields.server->mins, m1);
942 VectorAdd(org, ent->fields.server->maxs, m2);
943 VectorSubtract(m2, m1, s);
944 #define EPSILON (1.0f / 32.0f)
945 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
946 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
947 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
948 for (i = 0;i < 8;i++)
950 v[0] = (i & 1) ? m2[0] : m1[0];
951 v[1] = (i & 2) ? m2[1] : m1[1];
952 v[2] = (i & 4) ? m2[2] : m1[2];
953 if (SV_PointSuperContents(v) & contents)
958 // if the trace found a better position for the entity, move it there
959 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
962 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
963 VectorCopy(trace.endpos, ent->fields.server->origin);
965 // verify if the endpos is REALLY outside solid
966 VectorCopy(trace.endpos, org);
967 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
969 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
971 VectorCopy(org, ent->fields.server->origin);
982 void SV_CheckAllEnts (void)
987 // see if any solid entities are inside the final position
988 check = PRVM_NEXT_EDICT(prog->edicts);
989 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
991 if (check->priv.server->free)
993 if (check->fields.server->movetype == MOVETYPE_PUSH
994 || check->fields.server->movetype == MOVETYPE_NONE
995 || check->fields.server->movetype == MOVETYPE_FOLLOW
996 || check->fields.server->movetype == MOVETYPE_NOCLIP)
999 if (SV_TestEntityPosition (check, vec3_origin))
1000 Con_Print("entity in invalid position\n");
1004 // DRESK - Support for Entity Contents Transition Event
1007 SV_CheckContentsTransition
1009 returns true if entity had a valid contentstransition function call
1012 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
1014 int bValidFunctionCall;
1015 prvm_eval_t *contentstransition;
1017 // Default Valid Function Call to False
1018 bValidFunctionCall = false;
1020 if(ent->fields.server->watertype != nContents)
1021 { // Changed Contents
1022 // Acquire Contents Transition Function from QC
1023 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
1025 if(contentstransition->function)
1026 { // Valid Function; Execute
1027 // Assign Valid Function
1028 bValidFunctionCall = true;
1029 // Prepare Parameters (Original Contents, New Contents)
1030 // Original Contents
1031 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
1033 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1035 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1036 // Execute VM Function
1037 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
1041 // Return if Function Call was Valid
1042 return bValidFunctionCall;
1051 void SV_CheckVelocity (prvm_edict_t *ent)
1059 for (i=0 ; i<3 ; i++)
1061 if (IS_NAN(ent->fields.server->velocity[i]))
1063 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1064 ent->fields.server->velocity[i] = 0;
1066 if (IS_NAN(ent->fields.server->origin[i]))
1068 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1069 ent->fields.server->origin[i] = 0;
1073 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1074 // player_run/player_stand1 does not horribly malfunction if the
1075 // velocity becomes a denormalized float
1076 if (VectorLength2(ent->fields.server->velocity) < 0.0001)
1077 VectorClear(ent->fields.server->velocity);
1079 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1080 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1081 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1083 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1084 ent->fields.server->velocity[0] *= wishspeed;
1085 ent->fields.server->velocity[1] *= wishspeed;
1086 ent->fields.server->velocity[2] *= wishspeed;
1094 Runs thinking code if time. There is some play in the exact time the think
1095 function will be called, because it is called before any movement is done
1096 in a frame. Not used for pushmove objects, because they must be exact.
1097 Returns false if the entity removed itself.
1100 qboolean SV_RunThink (prvm_edict_t *ent)
1104 // don't let things stay in the past.
1105 // it is possible to start that way by a trigger with a local time.
1106 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1109 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1111 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1112 ent->fields.server->nextthink = 0;
1113 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1114 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1115 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1116 // mods often set nextthink to time to cause a think every frame,
1117 // we don't want to loop in that case, so exit if the new nextthink is
1118 // <= the time the qc was told, also exit if it is past the end of the
1120 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1123 return !ent->priv.server->free;
1130 Two entities have touched, so run their touch functions
1133 extern void VM_SetTraceGlobals(const trace_t *trace);
1134 extern sizebuf_t vm_tempstringsbuf;
1135 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1137 int restorevm_tempstringsbuf_cursize;
1138 int old_self, old_other;
1139 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1142 old_self = prog->globals.server->self;
1143 old_other = prog->globals.server->other;
1144 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1146 VM_SetTraceGlobals(trace);
1148 prog->globals.server->time = sv.time;
1149 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1151 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1152 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1153 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1156 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1158 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1159 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1160 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1161 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1162 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1163 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1164 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1166 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1168 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1170 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1172 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1175 prog->globals.server->self = old_self;
1176 prog->globals.server->other = old_other;
1177 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1185 Slide off of the impacting object
1186 returns the blocked flags (1 = floor, 2 = step / wall)
1189 #define STOP_EPSILON 0.1
1190 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1195 backoff = -DotProduct (in, normal) * overbounce;
1196 VectorMA(in, backoff, normal, out);
1198 for (i = 0;i < 3;i++)
1199 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1208 The basic solid body movement clip that slides along multiple planes
1209 Returns the clipflags if the velocity was modified (hit something solid)
1213 8 = teleported by touch method
1214 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1217 static float SV_Gravity (prvm_edict_t *ent);
1218 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1219 #define MAX_CLIP_PLANES 5
1220 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1222 int blocked, bumpcount;
1223 int i, j, numplanes;
1224 float d, time_left, gravity;
1225 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1235 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1237 gravity = SV_Gravity(ent) * 0.5f;
1238 ent->fields.server->velocity[2] -= gravity;
1242 applygravity = false;
1243 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1247 VectorCopy(ent->fields.server->velocity, original_velocity);
1248 VectorCopy(ent->fields.server->velocity, primal_velocity);
1251 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1253 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1256 VectorScale(ent->fields.server->velocity, time_left, push);
1257 if(!SV_PushEntity(&trace, ent, push, false, false))
1259 // we got teleported by a touch function
1260 // let's abort the move
1265 if (trace.fraction == 1)
1267 if (trace.plane.normal[2])
1269 if (trace.plane.normal[2] > 0.7)
1276 Con_Printf ("SV_FlyMove: !trace.ent");
1277 trace.ent = prog->edicts;
1280 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1281 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1284 else if (stepheight)
1286 // step - handle it immediately
1292 //Con_Printf("step %f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1293 VectorSet(steppush, 0, 0, stepheight);
1294 VectorCopy(ent->fields.server->origin, org);
1295 SV_PushEntity(&steptrace, ent, steppush, false, false);
1296 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1297 SV_PushEntity(&steptrace2, ent, push, false, false);
1298 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1299 VectorSet(steppush, 0, 0, org[2] - ent->fields.server->origin[2]);
1300 SV_PushEntity(&steptrace3, ent, steppush, false, false);
1301 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1302 // accept the new position if it made some progress...
1303 if (fabs(ent->fields.server->origin[0] - org[0]) >= 0.03125 || fabs(ent->fields.server->origin[1] - org[1]) >= 0.03125)
1305 //Con_Printf("accepted (delta %f %f %f)\n", ent->fields.server->origin[0] - org[0], ent->fields.server->origin[1] - org[1], ent->fields.server->origin[2] - org[2]);
1307 VectorCopy(ent->fields.server->origin, trace.endpos);
1308 time_left *= 1 - trace.fraction;
1314 //Con_Printf("REJECTED (delta %f %f %f)\n", ent->fields.server->origin[0] - org[0], ent->fields.server->origin[1] - org[1], ent->fields.server->origin[2] - org[2]);
1315 VectorCopy(org, ent->fields.server->origin);
1320 // step - return it to caller
1322 // save the trace for player extrafriction
1324 VectorCopy(trace.plane.normal, stepnormal);
1326 if (trace.fraction >= 0.001)
1328 // actually covered some distance
1329 VectorCopy(ent->fields.server->velocity, original_velocity);
1333 time_left *= 1 - trace.fraction;
1335 // clipped to another plane
1336 if (numplanes >= MAX_CLIP_PLANES)
1338 // this shouldn't really happen
1339 VectorClear(ent->fields.server->velocity);
1345 for (i = 0;i < numplanes;i++)
1346 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1350 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1355 VectorCopy(trace.plane.normal, planes[numplanes]);
1358 // modify original_velocity so it parallels all of the clip planes
1359 for (i = 0;i < numplanes;i++)
1361 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1362 for (j = 0;j < numplanes;j++)
1367 if (DotProduct(new_velocity, planes[j]) < 0)
1377 // go along this plane
1378 VectorCopy(new_velocity, ent->fields.server->velocity);
1382 // go along the crease
1385 VectorClear(ent->fields.server->velocity);
1389 CrossProduct(planes[0], planes[1], dir);
1390 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1391 VectorNormalize(dir);
1392 d = DotProduct(dir, ent->fields.server->velocity);
1393 VectorScale(dir, d, ent->fields.server->velocity);
1396 // if current velocity is against the original velocity,
1397 // stop dead to avoid tiny occilations in sloping corners
1398 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1400 VectorClear(ent->fields.server->velocity);
1405 //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]);
1408 if ((blocked & 1) == 0 && bumpcount > 1)
1410 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1411 // flag ONGROUND if there's ground under it
1412 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1416 // LordHavoc: this came from QW and allows you to get out of water more easily
1417 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1418 VectorCopy(primal_velocity, ent->fields.server->velocity);
1419 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1420 ent->fields.server->velocity[2] -= gravity;
1430 static float SV_Gravity (prvm_edict_t *ent)
1435 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1436 if (val!=0 && val->_float)
1437 ent_gravity = val->_float;
1440 return ent_gravity * sv_gravity.value * sv.frametime;
1445 ===============================================================================
1449 ===============================================================================
1456 Does not change the entities velocity at all
1457 The trace struct is filled with the trace that has been done.
1458 Returns true if the push did not result in the entity being teleported by QC code.
1461 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1465 vec3_t original, original_velocity;
1468 VectorCopy(ent->fields.server->origin, original);
1469 VectorAdd (ent->fields.server->origin, push, end);
1471 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1472 type = MOVE_MISSILE;
1473 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1474 type = MOVE_NOMONSTERS; // only clip against bmodels
1478 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1480 while (trace->bmodelstartsolid && sv_gameplayfix_nudgeoutofsolid.integer)
1482 vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
1483 VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
1484 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1488 VectorCopy(original, ent->fields.server->origin);
1492 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1495 VectorCopy (trace->endpos, ent->fields.server->origin);
1497 VectorCopy(ent->fields.server->origin, original);
1498 VectorCopy(ent->fields.server->velocity, original_velocity);
1503 if(!trace->startsolid)
1504 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)
1506 Con_Printf("something eeeeevil happened\n");
1511 SV_LinkEdict_TouchAreaGrid(ent);
1513 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))))
1514 SV_Impact (ent, trace);
1516 return VectorCompare(ent->fields.server->origin, original) && VectorCompare(ent->fields.server->velocity, original_velocity);
1526 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1529 int pusherowner, pusherprog;
1532 float savesolid, movetime2, pushltime;
1533 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1535 int numcheckentities;
1536 static prvm_edict_t *checkentities[MAX_EDICTS];
1537 dp_model_t *pushermodel;
1538 trace_t trace, trace2;
1539 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1540 static unsigned short moved_edicts[MAX_EDICTS];
1542 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])
1544 pusher->fields.server->ltime += movetime;
1548 switch ((int) pusher->fields.server->solid)
1550 // LordHavoc: valid pusher types
1553 case SOLID_SLIDEBOX:
1554 case SOLID_CORPSE: // LordHavoc: this would be weird...
1556 // LordHavoc: no collisions
1559 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1560 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1561 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1562 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1563 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1564 pusher->fields.server->ltime += movetime;
1565 SV_LinkEdict(pusher);
1568 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1571 index = (int) pusher->fields.server->modelindex;
1572 if (index < 1 || index >= MAX_MODELS)
1574 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1577 pushermodel = SV_GetModelByIndex(index);
1578 pusherowner = pusher->fields.server->owner;
1579 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1581 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1583 movetime2 = movetime;
1584 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1585 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1586 if (moveangle[0] || moveangle[2])
1588 for (i = 0;i < 3;i++)
1592 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1593 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1597 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1598 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1602 else if (moveangle[1])
1604 for (i = 0;i < 3;i++)
1608 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1609 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1613 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1614 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1620 for (i = 0;i < 3;i++)
1624 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1625 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1629 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1630 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1635 VectorNegate (moveangle, a);
1636 AngleVectorsFLU (a, forward, left, up);
1638 VectorCopy (pusher->fields.server->origin, pushorig);
1639 VectorCopy (pusher->fields.server->angles, pushang);
1640 pushltime = pusher->fields.server->ltime;
1642 // move the pusher to its final position
1644 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1645 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1646 pusher->fields.server->ltime += movetime;
1647 SV_LinkEdict(pusher);
1649 pushermodel = SV_GetModelFromEdict(pusher);
1650 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);
1651 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1653 savesolid = pusher->fields.server->solid;
1655 // see if any solid entities are inside the final position
1658 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1659 for (e = 0;e < numcheckentities;e++)
1661 prvm_edict_t *check = checkentities[e];
1662 int movetype = (int)check->fields.server->movetype;
1667 case MOVETYPE_FOLLOW:
1668 case MOVETYPE_NOCLIP:
1669 case MOVETYPE_FAKEPUSH:
1675 if (check->fields.server->owner == pusherprog)
1678 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1681 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1683 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1684 check->priv.server->waterposition_forceupdate = true;
1686 checkcontents = SV_GenericHitSuperContentsMask(check);
1688 // if the entity is standing on the pusher, it will definitely be moved
1689 // if the entity is not standing on the pusher, but is in the pusher's
1690 // final position, move it
1691 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1693 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);
1694 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1695 if (!trace.startsolid)
1697 //Con_Printf("- not in solid\n");
1705 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1706 org2[0] = DotProduct (org, forward);
1707 org2[1] = DotProduct (org, left);
1708 org2[2] = DotProduct (org, up);
1709 VectorSubtract (org2, org, move);
1710 VectorAdd (move, move1, move);
1713 VectorCopy (move1, move);
1715 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1717 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1718 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1719 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1721 // physics objects need better collisions than this code can do
1722 if (movetype == MOVETYPE_PHYSICS)
1724 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1725 SV_LinkEdict(check);
1726 SV_LinkEdict_TouchAreaGrid(check);
1730 // try moving the contacted entity
1731 pusher->fields.server->solid = SOLID_NOT;
1732 if(!SV_PushEntity (&trace, check, move, true, true))
1734 // entity "check" got teleported
1735 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1736 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1737 continue; // pushed enough
1739 // FIXME: turn players specially
1740 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1741 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1742 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1744 // this trace.fraction < 1 check causes items to fall off of pushers
1745 // if they pass under or through a wall
1746 // the groundentity check causes items to fall off of ledges
1747 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1748 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1750 // if it is still inside the pusher, block
1751 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);
1752 if (trace.startsolid)
1754 // try moving the contacted entity a tiny bit further to account for precision errors
1756 pusher->fields.server->solid = SOLID_NOT;
1757 VectorScale(move, 1.1, move2);
1758 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1759 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1760 if(!SV_PushEntity (&trace2, check, move2, true, true))
1762 // entity "check" got teleported
1765 pusher->fields.server->solid = savesolid;
1766 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);
1767 if (trace.startsolid)
1769 // try moving the contacted entity a tiny bit less to account for precision errors
1770 pusher->fields.server->solid = SOLID_NOT;
1771 VectorScale(move, 0.9, move2);
1772 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1773 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1774 if(!SV_PushEntity (&trace2, check, move2, true, true))
1776 // entity "check" got teleported
1779 pusher->fields.server->solid = savesolid;
1780 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);
1781 if (trace.startsolid)
1783 // still inside pusher, so it's really blocked
1786 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1788 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1791 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1792 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1796 VectorCopy (pushorig, pusher->fields.server->origin);
1797 VectorCopy (pushang, pusher->fields.server->angles);
1798 pusher->fields.server->ltime = pushltime;
1799 SV_LinkEdict(pusher);
1801 // move back any entities we already moved
1802 for (i = 0;i < num_moved;i++)
1804 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1805 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1806 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1810 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1811 if (pusher->fields.server->blocked)
1813 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1814 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1815 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1822 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1823 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1824 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1833 void SV_Physics_Pusher (prvm_edict_t *ent)
1835 float thinktime, oldltime, movetime;
1837 oldltime = ent->fields.server->ltime;
1839 thinktime = ent->fields.server->nextthink;
1840 if (thinktime < ent->fields.server->ltime + sv.frametime)
1842 movetime = thinktime - ent->fields.server->ltime;
1847 movetime = sv.frametime;
1850 // advances ent->fields.server->ltime if not blocked
1851 SV_PushMove (ent, movetime);
1853 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1855 ent->fields.server->nextthink = 0;
1856 prog->globals.server->time = sv.time;
1857 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1858 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1859 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1865 ===============================================================================
1869 ===============================================================================
1872 static float unstickoffsets[] =
1874 // poutting -/+z changes first as they are least weird
1889 typedef enum unstickresult_e
1897 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1901 // if not stuck in a bmodel, just return
1902 if (!SV_TestEntityPosition(ent, vec3_origin))
1903 return UNSTICK_GOOD;
1905 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1907 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1909 VectorCopy(unstickoffsets + i, offset);
1911 //SV_LinkEdict_TouchAreaGrid(ent);
1912 return UNSTICK_UNSTUCK;
1916 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1917 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1919 for(i = 2; i <= maxunstick; ++i)
1921 VectorClear(offset);
1923 if (!SV_TestEntityPosition(ent, offset))
1926 //SV_LinkEdict_TouchAreaGrid(ent);
1927 return UNSTICK_UNSTUCK;
1930 if (!SV_TestEntityPosition(ent, offset))
1933 //SV_LinkEdict_TouchAreaGrid(ent);
1934 return UNSTICK_UNSTUCK;
1938 return UNSTICK_STUCK;
1941 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1944 switch(SV_UnstickEntityReturnOffset(ent, offset))
1948 case UNSTICK_UNSTUCK:
1949 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]);
1952 if (developer_extra.integer)
1953 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1956 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1965 This is a big hack to try and fix the rare case of getting stuck in the world
1969 void SV_CheckStuck (prvm_edict_t *ent)
1973 switch(SV_UnstickEntityReturnOffset(ent, offset))
1976 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1978 case UNSTICK_UNSTUCK:
1979 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]);
1982 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1983 if (!SV_TestEntityPosition(ent, offset))
1985 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1987 //SV_LinkEdict_TouchAreaGrid(ent);
1990 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1993 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2003 qboolean SV_CheckWater (prvm_edict_t *ent)
2006 int nNativeContents;
2009 point[0] = ent->fields.server->origin[0];
2010 point[1] = ent->fields.server->origin[1];
2011 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2013 // DRESK - Support for Entity Contents Transition Event
2014 // NOTE: Some logic needed to be slightly re-ordered
2015 // to not affect performance and allow for the feature.
2017 // Acquire Super Contents Prior to Resets
2018 cont = SV_PointSuperContents(point);
2019 // Acquire Native Contents Here
2020 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2022 // DRESK - Support for Entity Contents Transition Event
2023 if(ent->fields.server->watertype)
2024 // Entity did NOT Spawn; Check
2025 SV_CheckContentsTransition(ent, nNativeContents);
2028 ent->fields.server->waterlevel = 0;
2029 ent->fields.server->watertype = CONTENTS_EMPTY;
2030 cont = SV_PointSuperContents(point);
2031 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2033 ent->fields.server->watertype = nNativeContents;
2034 ent->fields.server->waterlevel = 1;
2035 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2036 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2038 ent->fields.server->waterlevel = 2;
2039 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2040 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2041 ent->fields.server->waterlevel = 3;
2045 return ent->fields.server->waterlevel > 1;
2054 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2057 vec3_t forward, into, side;
2059 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2060 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2062 // cut the tangential velocity
2063 i = DotProduct (stepnormal, ent->fields.server->velocity);
2064 VectorScale (stepnormal, i, into);
2065 VectorSubtract (ent->fields.server->velocity, into, side);
2066 ent->fields.server->velocity[0] = side[0] * (1 + d);
2067 ent->fields.server->velocity[1] = side[1] * (1 + d);
2073 =====================
2076 Player has come to a dead stop, possibly due to the problem with limited
2077 float precision at some angle joins in the BSP hull.
2079 Try fixing by pushing one pixel in each direction.
2081 This is a hack, but in the interest of good gameplay...
2082 ======================
2084 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2089 VectorCopy (ent->fields.server->origin, oldorg);
2092 for (i=0 ; i<8 ; i++)
2094 // try pushing a little in an axial direction
2097 case 0: dir[0] = 2; dir[1] = 0; break;
2098 case 1: dir[0] = 0; dir[1] = 2; break;
2099 case 2: dir[0] = -2; dir[1] = 0; break;
2100 case 3: dir[0] = 0; dir[1] = -2; break;
2101 case 4: dir[0] = 2; dir[1] = 2; break;
2102 case 5: dir[0] = -2; dir[1] = 2; break;
2103 case 6: dir[0] = 2; dir[1] = -2; break;
2104 case 7: dir[0] = -2; dir[1] = -2; break;
2107 SV_PushEntity (&trace, ent, dir, false, true);
2109 // retry the original move
2110 ent->fields.server->velocity[0] = oldvel[0];
2111 ent->fields.server->velocity[1] = oldvel[1];
2112 ent->fields.server->velocity[2] = 0;
2113 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2115 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2116 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2118 Con_DPrint("TryUnstick - success.\n");
2122 // go back to the original pos and try again
2123 VectorCopy (oldorg, ent->fields.server->origin);
2127 VectorClear (ent->fields.server->velocity);
2128 Con_DPrint("TryUnstick - failure.\n");
2134 =====================
2137 Only used by players
2138 ======================
2140 void SV_WalkMove (prvm_edict_t *ent)
2144 //int originalmove_clip;
2145 int originalmove_flags;
2146 int originalmove_groundentity;
2147 int hitsupercontentsmask;
2149 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2150 trace_t downtrace, trace;
2151 qboolean applygravity;
2153 // if frametime is 0 (due to client sending the same timestamp twice),
2155 if (sv.frametime <= 0)
2158 SV_CheckStuck (ent);
2160 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2162 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2164 SV_CheckVelocity(ent);
2166 // do a regular slide move unless it looks like you ran into a step
2167 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2169 VectorCopy (ent->fields.server->origin, start_origin);
2170 VectorCopy (ent->fields.server->velocity, start_velocity);
2172 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2174 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2177 // only try this if there was no floor in the way in the trace (no,
2178 // this check seems to be not REALLY necessary, because if clip & 1,
2179 // our trace will hit that thing too)
2180 VectorSet(upmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + 1);
2181 VectorSet(downmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
2182 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
2183 type = MOVE_MISSILE;
2184 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
2185 type = MOVE_NOMONSTERS; // only clip against bmodels
2188 trace = SV_TraceBox(upmove, ent->fields.server->mins, ent->fields.server->maxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2189 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2190 clip |= 1; // but we HAVE found a floor
2193 // if the move did not hit the ground at any point, we're not on ground
2195 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2197 SV_CheckVelocity(ent);
2199 SV_LinkEdict_TouchAreaGrid(ent);
2201 if(clip & 8) // teleport
2204 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2207 if (sv_nostep.integer)
2210 VectorCopy(ent->fields.server->origin, originalmove_origin);
2211 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2212 //originalmove_clip = clip;
2213 originalmove_flags = (int)ent->fields.server->flags;
2214 originalmove_groundentity = ent->fields.server->groundentity;
2216 // if move didn't block on a step, return
2219 // if move was not trying to move into the step, return
2220 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2223 if (ent->fields.server->movetype != MOVETYPE_FLY)
2225 // return if gibbed by a trigger
2226 if (ent->fields.server->movetype != MOVETYPE_WALK)
2229 // only step up while jumping if that is enabled
2230 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2231 if (!oldonground && ent->fields.server->waterlevel == 0)
2235 // try moving up and forward to go up a step
2236 // back to start pos
2237 VectorCopy (start_origin, ent->fields.server->origin);
2238 VectorCopy (start_velocity, ent->fields.server->velocity);
2241 VectorClear (upmove);
2242 upmove[2] = sv_stepheight.value;
2243 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2245 // we got teleported when upstepping... must abort the move
2250 ent->fields.server->velocity[2] = 0;
2251 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2252 ent->fields.server->velocity[2] += start_velocity[2];
2255 // we got teleported when upstepping... must abort the move
2256 // note that z velocity handling may not be what QC expects here, but we cannot help it
2260 SV_CheckVelocity(ent);
2262 SV_LinkEdict_TouchAreaGrid(ent);
2264 // check for stuckness, possibly due to the limited precision of floats
2265 // in the clipping hulls
2267 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2268 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2270 //Con_Printf("wall\n");
2271 // stepping up didn't make any progress, revert to original move
2272 VectorCopy(originalmove_origin, ent->fields.server->origin);
2273 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2274 //clip = originalmove_clip;
2275 ent->fields.server->flags = originalmove_flags;
2276 ent->fields.server->groundentity = originalmove_groundentity;
2277 // now try to unstick if needed
2278 //clip = SV_TryUnstick (ent, oldvel);
2282 //Con_Printf("step - ");
2284 // extra friction based on view angle
2285 if (clip & 2 && sv_wallfriction.integer)
2286 SV_WallFriction (ent, stepnormal);
2288 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2289 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))
2293 VectorClear (downmove);
2294 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2295 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2297 // we got teleported when downstepping... must abort the move
2301 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2303 // this has been disabled so that you can't jump when you are stepping
2304 // up while already jumping (also known as the Quake2 double jump bug)
2306 // LordHavoc: disabled this check so you can walk on monsters/players
2307 //if (ent->fields.server->solid == SOLID_BSP)
2309 //Con_Printf("onground\n");
2310 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2311 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2317 //Con_Printf("slope\n");
2318 // if the push down didn't end up on good ground, use the move without
2319 // the step up. This happens near wall / slope combinations, and can
2320 // cause the player to hop up higher on a slope too steep to climb
2321 VectorCopy(originalmove_origin, ent->fields.server->origin);
2322 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2323 //clip = originalmove_clip;
2324 ent->fields.server->flags = originalmove_flags;
2325 ent->fields.server->groundentity = originalmove_groundentity;
2328 SV_CheckVelocity(ent);
2330 SV_LinkEdict_TouchAreaGrid(ent);
2333 //============================================================================
2339 Entities that are "stuck" to another entity
2342 void SV_Physics_Follow (prvm_edict_t *ent)
2344 vec3_t vf, vr, vu, angles, v;
2348 if (!SV_RunThink (ent))
2351 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2352 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2353 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])
2355 // quick case for no rotation
2356 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2360 angles[0] = -ent->fields.server->punchangle[0];
2361 angles[1] = ent->fields.server->punchangle[1];
2362 angles[2] = ent->fields.server->punchangle[2];
2363 AngleVectors (angles, vf, vr, vu);
2364 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];
2365 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];
2366 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];
2367 angles[0] = -e->fields.server->angles[0];
2368 angles[1] = e->fields.server->angles[1];
2369 angles[2] = e->fields.server->angles[2];
2370 AngleVectors (angles, vf, vr, vu);
2371 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2372 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2373 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2375 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2377 //SV_LinkEdict_TouchAreaGrid(ent);
2381 ==============================================================================
2385 ==============================================================================
2390 SV_CheckWaterTransition
2394 void SV_CheckWaterTransition (prvm_edict_t *ent)
2397 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2398 if (!ent->fields.server->watertype)
2400 // just spawned here
2401 ent->fields.server->watertype = cont;
2402 ent->fields.server->waterlevel = 1;
2406 // DRESK - Support for Entity Contents Transition Event
2407 // NOTE: Call here BEFORE updating the watertype below,
2408 // and suppress watersplash sound if a valid function
2409 // call was made to allow for custom "splash" sounds.
2410 if( !SV_CheckContentsTransition(ent, cont) )
2411 { // Contents Transition Function Invalid; Potentially Play Water Sound
2412 // check if the entity crossed into or out of water
2413 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2414 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2417 if (cont <= CONTENTS_WATER)
2419 ent->fields.server->watertype = cont;
2420 ent->fields.server->waterlevel = 1;
2424 ent->fields.server->watertype = CONTENTS_EMPTY;
2425 ent->fields.server->waterlevel = 0;
2433 Toss, bounce, and fly movement. When onground, do nothing.
2436 void SV_Physics_Toss (prvm_edict_t *ent)
2442 prvm_edict_t *groundentity;
2444 // if onground, return without moving
2445 if ((int)ent->fields.server->flags & FL_ONGROUND)
2447 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2448 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2450 // don't stick to ground if onground and moving upward
2451 ent->fields.server->flags -= FL_ONGROUND;
2453 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2455 // we can trust FL_ONGROUND if groundentity is world because it never moves
2458 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2460 // if ent was supported by a brush model on previous frame,
2461 // and groundentity is now freed, set groundentity to 0 (world)
2462 // which leaves it suspended in the air
2463 ent->fields.server->groundentity = 0;
2464 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2467 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2469 // don't slide if still touching the groundentity
2473 ent->priv.server->suspendedinairflag = false;
2475 SV_CheckVelocity (ent);
2478 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2479 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2482 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2484 movetime = sv.frametime;
2485 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2488 VectorScale (ent->fields.server->velocity, movetime, move);
2489 if(!SV_PushEntity (&trace, ent, move, true, true))
2490 return; // teleported
2491 if (ent->priv.server->free)
2493 if (trace.bmodelstartsolid)
2495 // try to unstick the entity
2496 SV_UnstickEntity(ent);
2497 if(!SV_PushEntity (&trace, ent, move, false, true))
2498 return; // teleported
2499 if (ent->priv.server->free)
2502 if (trace.fraction == 1)
2504 movetime *= 1 - min(1, trace.fraction);
2505 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2508 float bouncefactor = 1.0f;
2509 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2510 if (val!=0 && val->_float)
2511 bouncefactor = val->_float;
2513 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2514 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2516 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2518 float d, ent_gravity;
2520 float bouncefactor = 0.5f;
2521 float bouncestop = 60.0f / 800.0f;
2523 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2524 if (val!=0 && val->_float)
2525 bouncefactor = val->_float;
2527 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2528 if (val!=0 && val->_float)
2529 bouncestop = val->_float;
2531 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2532 // LordHavoc: fixed grenades not bouncing when fired down a slope
2533 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2534 if (val!=0 && val->_float)
2535 ent_gravity = val->_float;
2538 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2540 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2541 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2543 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2544 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2545 VectorClear (ent->fields.server->velocity);
2546 VectorClear (ent->fields.server->avelocity);
2549 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2553 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2555 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2556 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2557 VectorClear (ent->fields.server->velocity);
2558 VectorClear (ent->fields.server->avelocity);
2561 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2566 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2567 if (trace.plane.normal[2] > 0.7)
2569 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2570 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2571 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2572 ent->priv.server->suspendedinairflag = true;
2573 VectorClear (ent->fields.server->velocity);
2574 VectorClear (ent->fields.server->avelocity);
2577 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2579 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2583 // check for in water
2584 SV_CheckWaterTransition (ent);
2588 ===============================================================================
2592 ===============================================================================
2599 Monsters freefall when they don't have a ground entity, otherwise
2600 all movement is done with discrete steps.
2602 This is also used for objects that have become still on the ground, but
2603 will fall if the floor is pulled out from under them.
2606 void SV_Physics_Step (prvm_edict_t *ent)
2608 int flags = (int)ent->fields.server->flags;
2611 // Backup Velocity in the event that movetypesteplandevent is called,
2612 // to provide a parameter with the entity's velocity at impact.
2613 prvm_eval_t *movetypesteplandevent;
2614 vec3_t backupVelocity;
2615 VectorCopy(ent->fields.server->velocity, backupVelocity);
2616 // don't fall at all if fly/swim
2617 if (!(flags & (FL_FLY | FL_SWIM)))
2619 if (flags & FL_ONGROUND)
2621 // freefall if onground and moving upward
2622 // freefall if not standing on a world surface (it may be a lift or trap door)
2623 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2625 ent->fields.server->flags -= FL_ONGROUND;
2626 SV_CheckVelocity(ent);
2627 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2629 SV_LinkEdict_TouchAreaGrid(ent);
2630 ent->priv.server->waterposition_forceupdate = true;
2635 // freefall if not onground
2636 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2638 SV_CheckVelocity(ent);
2639 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2641 SV_LinkEdict_TouchAreaGrid(ent);
2644 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2646 // DRESK - Check for Entity Land Event Function
2647 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2649 if(movetypesteplandevent->function)
2650 { // Valid Function; Execute
2651 // Prepare Parameters
2652 // Assign Velocity at Impact
2653 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2654 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2655 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2657 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2658 // Execute VM Function
2659 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2662 // Check for Engine Landing Sound
2663 if(sv_sound_land.string)
2664 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2666 ent->priv.server->waterposition_forceupdate = true;
2671 if (!SV_RunThink(ent))
2674 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2676 ent->priv.server->waterposition_forceupdate = false;
2677 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2678 SV_CheckWaterTransition(ent);
2682 //============================================================================
2684 static void SV_Physics_Entity (prvm_edict_t *ent)
2686 // don't run think/move on newly spawned projectiles as it messes up
2687 // movement interpolation and rocket trails, and is inconsistent with
2688 // respect to entities spawned in the same frame
2689 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2690 // but if it spawns a lower numbered ent, it doesn't - this never moves
2691 // ents in the first frame regardless)
2692 qboolean runmove = ent->priv.server->move;
2693 ent->priv.server->move = true;
2694 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2696 switch ((int) ent->fields.server->movetype)
2699 case MOVETYPE_FAKEPUSH:
2700 SV_Physics_Pusher (ent);
2703 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2704 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2707 case MOVETYPE_FOLLOW:
2708 SV_Physics_Follow (ent);
2710 case MOVETYPE_NOCLIP:
2711 if (SV_RunThink(ent))
2714 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2715 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2720 SV_Physics_Step (ent);
2723 if (SV_RunThink (ent))
2727 case MOVETYPE_BOUNCE:
2728 case MOVETYPE_BOUNCEMISSILE:
2729 case MOVETYPE_FLYMISSILE:
2732 if (SV_RunThink (ent))
2733 SV_Physics_Toss (ent);
2735 case MOVETYPE_PHYSICS:
2736 if (SV_RunThink(ent))
2739 SV_LinkEdict_TouchAreaGrid(ent);
2743 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2748 void SV_Physics_ClientMove(void)
2751 ent = host_client->edict;
2753 // call player physics, this needs the proper frametime
2754 prog->globals.server->frametime = sv.frametime;
2757 // call standard client pre-think, with frametime = 0
2758 prog->globals.server->time = sv.time;
2759 prog->globals.server->frametime = 0;
2760 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2761 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2762 prog->globals.server->frametime = sv.frametime;
2764 // make sure the velocity is sane (not a NaN)
2765 SV_CheckVelocity(ent);
2767 // perform MOVETYPE_WALK behavior
2770 // call standard player post-think, with frametime = 0
2771 prog->globals.server->time = sv.time;
2772 prog->globals.server->frametime = 0;
2773 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2774 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2775 prog->globals.server->frametime = sv.frametime;
2777 if(ent->fields.server->fixangle)
2779 // angle fixing was requested by physics code...
2780 // so store the current angles for later use
2781 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2782 host_client->fixangle_angles_set = TRUE;
2784 // and clear fixangle for the next frame
2785 ent->fields.server->fixangle = 0;
2789 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2791 // don't do physics on disconnected clients, FrikBot relies on this
2792 if (!host_client->spawned)
2795 // make sure the velocity is sane (not a NaN)
2796 SV_CheckVelocity(ent);
2798 // don't run physics here if running asynchronously
2799 if (host_client->clmovement_inputtimeout <= 0)
2802 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2805 // make sure the velocity is still sane (not a NaN)
2806 SV_CheckVelocity(ent);
2808 // call standard client pre-think
2809 prog->globals.server->time = sv.time;
2810 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2811 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2813 // make sure the velocity is still sane (not a NaN)
2814 SV_CheckVelocity(ent);
2817 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2819 // don't do physics on disconnected clients, FrikBot relies on this
2820 if (!host_client->spawned)
2823 // make sure the velocity is sane (not a NaN)
2824 SV_CheckVelocity(ent);
2826 // call standard player post-think
2827 prog->globals.server->time = sv.time;
2828 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2829 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2831 // make sure the velocity is still sane (not a NaN)
2832 SV_CheckVelocity(ent);
2834 if(ent->fields.server->fixangle)
2836 // angle fixing was requested by physics code...
2837 // so store the current angles for later use
2838 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2839 host_client->fixangle_angles_set = TRUE;
2841 // and clear fixangle for the next frame
2842 ent->fields.server->fixangle = 0;
2845 // decrement the countdown variable used to decide when to go back to
2846 // synchronous physics
2847 if (host_client->clmovement_inputtimeout > sv.frametime)
2848 host_client->clmovement_inputtimeout -= sv.frametime;
2850 host_client->clmovement_inputtimeout = 0;
2853 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2855 // don't do physics on disconnected clients, FrikBot relies on this
2856 if (!host_client->spawned)
2858 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2862 // make sure the velocity is sane (not a NaN)
2863 SV_CheckVelocity(ent);
2865 switch ((int) ent->fields.server->movetype)
2868 case MOVETYPE_FAKEPUSH:
2869 SV_Physics_Pusher (ent);
2872 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2873 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2876 case MOVETYPE_FOLLOW:
2877 SV_Physics_Follow (ent);
2879 case MOVETYPE_NOCLIP:
2882 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2883 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2886 SV_Physics_Step (ent);
2890 // don't run physics here if running asynchronously
2891 if (host_client->clmovement_inputtimeout <= 0)
2895 case MOVETYPE_BOUNCE:
2896 case MOVETYPE_BOUNCEMISSILE:
2897 case MOVETYPE_FLYMISSILE:
2900 SV_Physics_Toss (ent);
2906 case MOVETYPE_PHYSICS:
2910 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2914 SV_CheckVelocity (ent);
2917 SV_LinkEdict_TouchAreaGrid(ent);
2919 SV_CheckVelocity (ent);
2928 void SV_Physics (void)
2933 // let the progs know that a new frame has started
2934 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2935 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2936 prog->globals.server->time = sv.time;
2937 prog->globals.server->frametime = sv.frametime;
2938 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2940 // run physics engine
2941 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2944 // treat each object in turn
2947 // if force_retouch, relink all the entities
2948 if (prog->globals.server->force_retouch > 0)
2949 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2950 if (!ent->priv.server->free)
2951 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2953 if (sv_gameplayfix_consistentplayerprethink.integer)
2955 // run physics on the client entities in 3 stages
2956 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2957 if (!ent->priv.server->free)
2958 SV_Physics_ClientEntity_PreThink(ent);
2960 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2961 if (!ent->priv.server->free)
2962 SV_Physics_ClientEntity(ent);
2964 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2965 if (!ent->priv.server->free)
2966 SV_Physics_ClientEntity_PostThink(ent);
2970 // run physics on the client entities
2971 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2973 if (!ent->priv.server->free)
2975 SV_Physics_ClientEntity_PreThink(ent);
2976 SV_Physics_ClientEntity(ent);
2977 SV_Physics_ClientEntity_PostThink(ent);
2982 // run physics on all the non-client entities
2983 if (!sv_freezenonclients.integer)
2985 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2986 if (!ent->priv.server->free)
2987 SV_Physics_Entity(ent);
2988 // make a second pass to see if any ents spawned this frame and make
2989 // sure they run their move/think
2990 if (sv_gameplayfix_delayprojectiles.integer < 0)
2991 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2992 if (!ent->priv.server->move && !ent->priv.server->free)
2993 SV_Physics_Entity(ent);
2996 if (prog->globals.server->force_retouch > 0)
2997 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2999 // LordHavoc: endframe support
3000 if (prog->funcoffsets.EndFrame)
3002 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
3003 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
3004 prog->globals.server->time = sv.time;
3005 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
3008 // decrement prog->num_edicts if the highest number entities died
3009 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3011 if (!sv_freezenonclients.integer)
3012 sv.time += sv.frametime;