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 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 prvm_edict_t *touchedicts[MAX_EDICTS];
270 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
274 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
276 // TRICK: make the trace 1 qu longer!
277 VectorSubtract(pEnd, start, end);
278 len = VectorNormalizeLength(end);
279 VectorMA(pEnd, collision_endposnudge.value, end, end);
282 VectorCopy(pEnd, end);
285 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
287 if (VectorCompare(start, end))
288 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
290 VectorCopy(start, clipstart);
291 VectorCopy(end, clipend);
292 VectorClear(clipmins2);
293 VectorClear(clipmaxs2);
294 #if COLLISIONPARANOID >= 3
295 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
299 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask);
300 cliptrace.bmodelstartsolid = cliptrace.startsolid;
301 if (cliptrace.startsolid || cliptrace.fraction < 1)
302 cliptrace.ent = prog->edicts;
303 if (type == MOVE_WORLDONLY)
306 if (type == MOVE_MISSILE)
308 // LordHavoc: modified this, was = -15, now -= 15
309 for (i = 0;i < 3;i++)
316 // create the bounding box of the entire move
317 for (i = 0;i < 3;i++)
319 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
320 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
323 // debug override to test against everything
324 if (sv_debugmove.integer)
326 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
327 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
330 // if the passedict is world, make it NULL (to avoid two checks each time)
331 if (passedict == prog->edicts)
333 // precalculate prog value for passedict for comparisons
334 passedictprog = PRVM_EDICT_TO_PROG(passedict);
335 // precalculate passedict's owner edict pointer for comparisons
336 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
339 // because this uses World_EntitiestoBox, we know all entity boxes overlap
340 // the clip region, so we can skip culling checks in the loop below
341 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
342 if (numtouchedicts > MAX_EDICTS)
344 // this never happens
345 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
346 numtouchedicts = MAX_EDICTS;
348 for (i = 0;i < numtouchedicts;i++)
350 touch = touchedicts[i];
352 if (touch->fields.server->solid < SOLID_BBOX)
354 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
359 // don't clip against self
360 if (passedict == touch)
362 // don't clip owned entities against owner
363 if (traceowner == touch)
365 // don't clip owner against owned entities
366 if (passedictprog == touch->fields.server->owner)
368 // don't clip points against points (they can't collide)
369 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
373 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
375 // might interact, so do an exact clip
377 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
379 model = SV_GetModelFromEdict(touch);
380 pitchsign = SV_GetPitchSign(touch);
383 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);
385 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
386 Matrix4x4_Invert_Simple(&imatrix, &matrix);
387 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
388 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
389 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
390 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
391 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);
393 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);
395 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
399 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
400 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
401 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
411 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
412 #if COLLISIONPARANOID >= 1
413 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)
415 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 #if COLLISIONPARANOID >= 1
419 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)
421 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)
425 vec3_t hullmins, hullmaxs;
426 int i, bodysupercontents;
430 prvm_edict_t *traceowner, *touch;
432 // bounding box of entire move area
433 vec3_t clipboxmins, clipboxmaxs;
434 // size of the moving object
435 vec3_t clipmins, clipmaxs;
436 // size when clipping against monsters
437 vec3_t clipmins2, clipmaxs2;
438 // start and end origin of move
439 vec3_t clipstart, clipend;
442 // matrices to transform into/out of other entity's space
443 matrix4x4_t matrix, imatrix;
444 // model of other entity
446 // list of entities to test for collisions
448 prvm_edict_t *touchedicts[MAX_EDICTS];
449 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
453 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
455 // TRICK: make the trace 1 qu longer!
456 VectorSubtract(pEnd, start, end);
457 len = VectorNormalizeLength(end);
458 VectorMA(pEnd, collision_endposnudge.value, end, end);
461 VectorCopy(pEnd, end);
464 if (VectorCompare(mins, maxs))
466 vec3_t shiftstart, shiftend;
467 VectorAdd(start, mins, shiftstart);
468 VectorAdd(end, mins, shiftend);
469 if (VectorCompare(start, end))
470 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
472 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
473 VectorSubtract(trace.endpos, mins, trace.endpos);
477 VectorCopy(start, clipstart);
478 VectorCopy(end, clipend);
479 VectorCopy(mins, clipmins);
480 VectorCopy(maxs, clipmaxs);
481 VectorCopy(mins, clipmins2);
482 VectorCopy(maxs, clipmaxs2);
483 #if COLLISIONPARANOID >= 3
484 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
488 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
489 cliptrace.bmodelstartsolid = cliptrace.startsolid;
490 if (cliptrace.startsolid || cliptrace.fraction < 1)
491 cliptrace.ent = prog->edicts;
492 if (type == MOVE_WORLDONLY)
495 if (type == MOVE_MISSILE)
497 // LordHavoc: modified this, was = -15, now -= 15
498 for (i = 0;i < 3;i++)
505 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
506 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
507 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
510 VectorCopy(clipmins, hullmins);
511 VectorCopy(clipmaxs, hullmaxs);
514 // create the bounding box of the entire move
515 for (i = 0;i < 3;i++)
517 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
518 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
521 // debug override to test against everything
522 if (sv_debugmove.integer)
524 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
525 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
528 // if the passedict is world, make it NULL (to avoid two checks each time)
529 if (passedict == prog->edicts)
531 // precalculate prog value for passedict for comparisons
532 passedictprog = PRVM_EDICT_TO_PROG(passedict);
533 // figure out whether this is a point trace for comparisons
534 pointtrace = VectorCompare(clipmins, clipmaxs);
535 // precalculate passedict's owner edict pointer for comparisons
536 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
539 // because this uses World_EntitiestoBox, we know all entity boxes overlap
540 // the clip region, so we can skip culling checks in the loop below
541 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
542 if (numtouchedicts > MAX_EDICTS)
544 // this never happens
545 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
546 numtouchedicts = MAX_EDICTS;
548 for (i = 0;i < numtouchedicts;i++)
550 touch = touchedicts[i];
552 if (touch->fields.server->solid < SOLID_BBOX)
554 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
559 // don't clip against self
560 if (passedict == touch)
562 // don't clip owned entities against owner
563 if (traceowner == touch)
565 // don't clip owner against owned entities
566 if (passedictprog == touch->fields.server->owner)
568 // don't clip points against points (they can't collide)
569 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
573 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
575 // might interact, so do an exact clip
577 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
579 model = SV_GetModelFromEdict(touch);
580 pitchsign = SV_GetPitchSign(touch);
583 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);
585 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
586 Matrix4x4_Invert_Simple(&imatrix, &matrix);
587 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
588 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
589 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
590 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
591 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);
593 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);
595 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
599 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
600 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
601 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
606 #if COLLISIONPARANOID >= 1
607 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)
612 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
615 VectorCopy(trace.endpos, temp);
616 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
617 #if COLLISIONPARANOID < 3
618 if (trace.startsolid || endstuck)
620 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" : "");
626 int SV_PointSuperContents(const vec3_t point)
628 int supercontents = 0;
632 // matrices to transform into/out of other entity's space
633 matrix4x4_t matrix, imatrix;
634 // model of other entity
637 // list of entities to test for collisions
639 prvm_edict_t *touchedicts[MAX_EDICTS];
641 // get world supercontents at this point
642 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
643 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
645 // if sv_gameplayfix_swiminbmodels is off we're done
646 if (!sv_gameplayfix_swiminbmodels.integer)
647 return supercontents;
649 // get list of entities at this point
650 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
651 if (numtouchedicts > MAX_EDICTS)
653 // this never happens
654 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
655 numtouchedicts = MAX_EDICTS;
657 for (i = 0;i < numtouchedicts;i++)
659 touch = touchedicts[i];
661 // we only care about SOLID_BSP for pointcontents
662 if (touch->fields.server->solid != SOLID_BSP)
665 // might interact, so do an exact clip
666 model = SV_GetModelFromEdict(touch);
667 if (!model || !model->PointSuperContents)
669 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);
670 Matrix4x4_Invert_Simple(&imatrix, &matrix);
671 Matrix4x4_Transform(&imatrix, point, transformed);
672 frame = (int)touch->fields.server->frame;
673 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
676 return supercontents;
680 ===============================================================================
682 Linking entities into the world culling system
684 ===============================================================================
687 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
690 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
691 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
692 prog->globals.server->time = sv.time;
693 prog->globals.server->trace_allsolid = false;
694 prog->globals.server->trace_startsolid = false;
695 prog->globals.server->trace_fraction = 1;
696 prog->globals.server->trace_inwater = false;
697 prog->globals.server->trace_inopen = true;
698 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
699 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
700 prog->globals.server->trace_plane_dist = 0;
701 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
702 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
704 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
706 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
708 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
710 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
713 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
715 int i, numtouchedicts, old_self, old_other;
716 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
718 if (ent == prog->edicts)
719 return; // don't add the world
721 if (ent->priv.server->free)
724 if (ent->fields.server->solid == SOLID_NOT)
727 // build a list of edicts to touch, because the link loop can be corrupted
728 // by IncreaseEdicts called during touch functions
729 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
730 if (numtouchedicts > MAX_EDICTS)
732 // this never happens
733 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
734 numtouchedicts = MAX_EDICTS;
737 old_self = prog->globals.server->self;
738 old_other = prog->globals.server->other;
739 for (i = 0;i < numtouchedicts;i++)
741 touch = touchedicts[i];
742 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
744 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
747 prog->globals.server->self = old_self;
748 prog->globals.server->other = old_other;
751 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
755 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
757 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
758 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
759 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
760 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];
761 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];
762 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
763 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];
764 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];
765 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
766 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];
767 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];
768 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
769 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];
770 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];
771 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
772 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];
773 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];
774 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
775 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];
776 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];
777 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
778 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];
779 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 void SV_LinkEdict (prvm_edict_t *ent)
794 if (ent == prog->edicts)
795 return; // don't add the world
797 if (ent->priv.server->free)
800 modelindex = (int)ent->fields.server->modelindex;
801 if (modelindex < 0 || modelindex >= MAX_MODELS)
803 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
806 model = SV_GetModelByIndex(modelindex);
808 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
809 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
810 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
814 if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
816 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
817 // TODO special handling for spheres?
818 RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs);
819 VectorAdd(ent->fields.server->origin, mins, mins);
820 VectorAdd(ent->fields.server->origin, maxs, maxs);
822 else if (ent->fields.server->solid == SOLID_BSP)
826 if (!model->TraceBox && developer.integer >= 1)
827 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
829 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
831 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
832 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
834 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
836 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
837 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
841 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
842 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
847 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
848 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
849 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
854 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
855 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
859 // to make items easier to pick up and allow them to be grabbed off
860 // of shelves, the abs sizes are expanded
862 if ((int)ent->fields.server->flags & FL_ITEM)
873 // because movement is clipped an epsilon away from an actual edge,
874 // we must fully check even when bounding boxes don't quite touch
883 VectorCopy(mins, ent->fields.server->absmin);
884 VectorCopy(maxs, ent->fields.server->absmax);
886 World_LinkEdict(&sv.world, ent, mins, maxs);
890 ===============================================================================
894 ===============================================================================
899 SV_TestEntityPosition
901 returns true if the entity is in solid currently
904 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
909 contents = SV_GenericHitSuperContentsMask(ent);
910 VectorAdd(ent->fields.server->origin, offset, org);
911 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
912 if (trace.startsupercontents & contents)
916 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
918 // q1bsp/hlbsp use hulls and if the entity does not exactly match
919 // a hull size it is incorrectly tested, so this code tries to
920 // 'fix' it slightly...
921 // FIXME: this breaks entities larger than the hull size
924 VectorAdd(org, ent->fields.server->mins, m1);
925 VectorAdd(org, ent->fields.server->maxs, m2);
926 VectorSubtract(m2, m1, s);
927 #define EPSILON (1.0f / 32.0f)
928 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
929 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
930 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
931 for (i = 0;i < 8;i++)
933 v[0] = (i & 1) ? m2[0] : m1[0];
934 v[1] = (i & 2) ? m2[1] : m1[1];
935 v[2] = (i & 4) ? m2[2] : m1[2];
936 if (SV_PointSuperContents(v) & contents)
941 // if the trace found a better position for the entity, move it there
942 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
945 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
946 VectorCopy(trace.endpos, ent->fields.server->origin);
948 // verify if the endpos is REALLY outside solid
949 VectorCopy(trace.endpos, org);
950 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
952 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
954 VectorCopy(org, ent->fields.server->origin);
965 void SV_CheckAllEnts (void)
970 // see if any solid entities are inside the final position
971 check = PRVM_NEXT_EDICT(prog->edicts);
972 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
974 if (check->priv.server->free)
976 if (check->fields.server->movetype == MOVETYPE_PUSH
977 || check->fields.server->movetype == MOVETYPE_NONE
978 || check->fields.server->movetype == MOVETYPE_FOLLOW
979 || check->fields.server->movetype == MOVETYPE_NOCLIP)
982 if (SV_TestEntityPosition (check, vec3_origin))
983 Con_Print("entity in invalid position\n");
987 // DRESK - Support for Entity Contents Transition Event
990 SV_CheckContentsTransition
992 returns true if entity had a valid contentstransition function call
995 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
997 int bValidFunctionCall;
998 prvm_eval_t *contentstransition;
1000 // Default Valid Function Call to False
1001 bValidFunctionCall = false;
1003 if(ent->fields.server->watertype != nContents)
1004 { // Changed Contents
1005 // Acquire Contents Transition Function from QC
1006 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
1008 if(contentstransition->function)
1009 { // Valid Function; Execute
1010 // Assign Valid Function
1011 bValidFunctionCall = true;
1012 // Prepare Parameters (Original Contents, New Contents)
1013 // Original Contents
1014 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
1016 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1018 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1019 // Execute VM Function
1020 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
1024 // Return if Function Call was Valid
1025 return bValidFunctionCall;
1034 void SV_CheckVelocity (prvm_edict_t *ent)
1042 for (i=0 ; i<3 ; i++)
1044 if (IS_NAN(ent->fields.server->velocity[i]))
1046 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1047 ent->fields.server->velocity[i] = 0;
1049 if (IS_NAN(ent->fields.server->origin[i]))
1051 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1052 ent->fields.server->origin[i] = 0;
1056 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1057 // player_run/player_stand1 does not horribly malfunction if the
1058 // velocity becomes a denormalized float
1059 if (VectorLength2(ent->fields.server->velocity) < 0.0001)
1060 VectorClear(ent->fields.server->velocity);
1062 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1063 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1064 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1066 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1067 ent->fields.server->velocity[0] *= wishspeed;
1068 ent->fields.server->velocity[1] *= wishspeed;
1069 ent->fields.server->velocity[2] *= wishspeed;
1077 Runs thinking code if time. There is some play in the exact time the think
1078 function will be called, because it is called before any movement is done
1079 in a frame. Not used for pushmove objects, because they must be exact.
1080 Returns false if the entity removed itself.
1083 qboolean SV_RunThink (prvm_edict_t *ent)
1087 // don't let things stay in the past.
1088 // it is possible to start that way by a trigger with a local time.
1089 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1092 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1094 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1095 ent->fields.server->nextthink = 0;
1096 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1097 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1098 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1099 // mods often set nextthink to time to cause a think every frame,
1100 // we don't want to loop in that case, so exit if the new nextthink is
1101 // <= the time the qc was told, also exit if it is past the end of the
1103 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1106 return !ent->priv.server->free;
1113 Two entities have touched, so run their touch functions
1114 returns true if the impact kept the origin of the touching entity intact
1117 extern void VM_SetTraceGlobals(const trace_t *trace);
1118 extern sizebuf_t vm_tempstringsbuf;
1119 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1121 int restorevm_tempstringsbuf_cursize;
1122 int old_self, old_other;
1124 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1127 old_self = prog->globals.server->self;
1128 old_other = prog->globals.server->other;
1129 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1131 VectorCopy(e1->fields.server->origin, org);
1133 VM_SetTraceGlobals(trace);
1135 prog->globals.server->time = sv.time;
1136 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1138 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1139 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1140 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1143 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1145 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1146 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1147 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1148 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1149 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1150 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1151 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1153 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1155 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1157 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1159 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1162 prog->globals.server->self = old_self;
1163 prog->globals.server->other = old_other;
1164 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1166 return VectorCompare(e1->fields.server->origin, org);
1174 Slide off of the impacting object
1175 returns the blocked flags (1 = floor, 2 = step / wall)
1178 #define STOP_EPSILON 0.1
1179 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1184 backoff = -DotProduct (in, normal) * overbounce;
1185 VectorMA(in, backoff, normal, out);
1187 for (i = 0;i < 3;i++)
1188 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1197 The basic solid body movement clip that slides along multiple planes
1198 Returns the clipflags if the velocity was modified (hit something solid)
1202 8 = teleported by touch method
1203 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1206 static float SV_Gravity (prvm_edict_t *ent);
1207 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1208 #define MAX_CLIP_PLANES 5
1209 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
1211 int blocked, bumpcount;
1212 int i, j, numplanes;
1213 float d, time_left, gravity;
1214 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1224 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1226 gravity = SV_Gravity(ent) * 0.5f;
1227 ent->fields.server->velocity[2] -= gravity;
1231 applygravity = false;
1232 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1236 VectorCopy(ent->fields.server->velocity, original_velocity);
1237 VectorCopy(ent->fields.server->velocity, primal_velocity);
1240 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1242 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1245 VectorScale(ent->fields.server->velocity, time_left, push);
1247 VectorAdd(ent->fields.server->origin, push, end);
1249 if(!SV_PushEntity(&trace, ent, push, false, false))
1251 // we got teleported by a touch function
1252 // let's abort the move
1258 //if (trace.fraction < 0.002)
1263 VectorCopy(ent->fields.server->origin, start);
1264 start[2] += 3;//0.03125;
1265 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1266 end[2] += 3;//0.03125;
1267 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1268 if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
1270 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
1276 for (i = 0;i < numplanes;i++)
1278 VectorCopy(ent->fields.server->origin, start);
1279 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1280 VectorMA(start, 3, planes[i], start);
1281 VectorMA(end, 3, planes[i], end);
1282 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1283 if (trace.fraction < testtrace.fraction)
1286 VectorCopy(start, ent->fields.server->origin);
1291 // VectorAdd(ent->fields.server->origin, planes[j], start);
1297 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
1298 if (trace.fraction < 1)
1299 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
1304 if (trace.bmodelstartsolid)
1306 // LordHavoc: note: this code is what makes entities stick in place
1307 // if embedded in world only (you can walk through other objects if
1309 // entity is trapped in another solid
1310 VectorClear(ent->fields.server->velocity);
1315 if (trace.fraction == 1)
1317 if (trace.plane.normal[2])
1319 if (trace.plane.normal[2] > 0.7)
1326 Con_Printf ("SV_FlyMove: !trace.ent");
1327 trace.ent = prog->edicts;
1330 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1331 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1338 // save the trace for player extrafriction
1340 VectorCopy(trace.plane.normal, stepnormal);
1342 if (trace.fraction >= 0.001)
1344 // actually covered some distance
1345 VectorCopy(ent->fields.server->velocity, original_velocity);
1349 time_left *= 1 - trace.fraction;
1351 // clipped to another plane
1352 if (numplanes >= MAX_CLIP_PLANES)
1354 // this shouldn't really happen
1355 VectorClear(ent->fields.server->velocity);
1361 for (i = 0;i < numplanes;i++)
1362 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1366 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1371 VectorCopy(trace.plane.normal, planes[numplanes]);
1374 if (sv_newflymove.integer)
1375 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1378 // modify original_velocity so it parallels all of the clip planes
1379 for (i = 0;i < numplanes;i++)
1381 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1382 for (j = 0;j < numplanes;j++)
1387 if (DotProduct(new_velocity, planes[j]) < 0)
1397 // go along this plane
1398 VectorCopy(new_velocity, ent->fields.server->velocity);
1402 // go along the crease
1405 VectorClear(ent->fields.server->velocity);
1409 CrossProduct(planes[0], planes[1], dir);
1410 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1411 VectorNormalize(dir);
1412 d = DotProduct(dir, ent->fields.server->velocity);
1413 VectorScale(dir, d, ent->fields.server->velocity);
1417 // if current velocity is against the original velocity,
1418 // stop dead to avoid tiny occilations in sloping corners
1419 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1421 VectorClear(ent->fields.server->velocity);
1426 //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]);
1429 if ((blocked & 1) == 0 && bumpcount > 1)
1431 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1432 // flag ONGROUND if there's ground under it
1433 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1437 // LordHavoc: this came from QW and allows you to get out of water more easily
1438 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1439 VectorCopy(primal_velocity, ent->fields.server->velocity);
1440 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1441 ent->fields.server->velocity[2] -= gravity;
1451 static float SV_Gravity (prvm_edict_t *ent)
1456 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1457 if (val!=0 && val->_float)
1458 ent_gravity = val->_float;
1461 return ent_gravity * sv_gravity.value * sv.frametime;
1466 ===============================================================================
1470 ===============================================================================
1477 Does not change the entities velocity at all
1478 The trace struct is filled with the trace that has been done.
1479 Returns true if the push did not result in the entity being teleported by QC code.
1482 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1489 VectorCopy(ent->fields.server->origin, original);
1490 VectorAdd (ent->fields.server->origin, push, end);
1492 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1493 type = MOVE_MISSILE;
1494 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1495 type = MOVE_NOMONSTERS; // only clip against bmodels
1499 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1501 while (trace->startsolid && sv_gameplayfix_nudgeoutofsolid.integer)
1503 vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
1504 VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
1505 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1509 VectorCopy(original, ent->fields.server->origin);
1513 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1517 VectorCopy (trace->endpos, ent->fields.server->origin);
1521 if(!trace->startsolid)
1522 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)
1524 Con_Printf("something eeeeevil happened\n");
1529 SV_LinkEdict_TouchAreaGrid(ent);
1531 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))))
1532 return SV_Impact (ent, trace);
1544 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1547 int pusherowner, pusherprog;
1550 float savesolid, movetime2, pushltime;
1551 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1553 int numcheckentities;
1554 static prvm_edict_t *checkentities[MAX_EDICTS];
1555 dp_model_t *pushermodel;
1556 trace_t trace, trace2;
1557 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1558 unsigned short moved_edicts[MAX_EDICTS];
1560 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])
1562 pusher->fields.server->ltime += movetime;
1566 switch ((int) pusher->fields.server->solid)
1568 // LordHavoc: valid pusher types
1571 case SOLID_SLIDEBOX:
1572 case SOLID_CORPSE: // LordHavoc: this would be weird...
1574 // LordHavoc: no collisions
1577 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1578 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1579 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1580 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1581 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1582 pusher->fields.server->ltime += movetime;
1583 SV_LinkEdict(pusher);
1586 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1589 index = (int) pusher->fields.server->modelindex;
1590 if (index < 1 || index >= MAX_MODELS)
1592 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1595 pushermodel = SV_GetModelByIndex(index);
1596 pusherowner = pusher->fields.server->owner;
1597 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1599 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1601 movetime2 = movetime;
1602 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1603 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1604 if (moveangle[0] || moveangle[2])
1606 for (i = 0;i < 3;i++)
1610 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1611 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1615 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1616 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1620 else if (moveangle[1])
1622 for (i = 0;i < 3;i++)
1626 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1627 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1631 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1632 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1638 for (i = 0;i < 3;i++)
1642 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1643 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1647 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1648 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1653 VectorNegate (moveangle, a);
1654 AngleVectorsFLU (a, forward, left, up);
1656 VectorCopy (pusher->fields.server->origin, pushorig);
1657 VectorCopy (pusher->fields.server->angles, pushang);
1658 pushltime = pusher->fields.server->ltime;
1660 // move the pusher to its final position
1662 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1663 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1664 pusher->fields.server->ltime += movetime;
1665 SV_LinkEdict(pusher);
1667 pushermodel = SV_GetModelFromEdict(pusher);
1668 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);
1669 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1671 savesolid = pusher->fields.server->solid;
1673 // see if any solid entities are inside the final position
1676 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1677 for (e = 0;e < numcheckentities;e++)
1679 prvm_edict_t *check = checkentities[e];
1680 int movetype = (int)check->fields.server->movetype;
1685 case MOVETYPE_FOLLOW:
1686 case MOVETYPE_NOCLIP:
1687 case MOVETYPE_FAKEPUSH:
1693 if (check->fields.server->owner == pusherprog)
1696 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1699 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1701 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1702 check->priv.server->waterposition_forceupdate = true;
1704 checkcontents = SV_GenericHitSuperContentsMask(check);
1706 // if the entity is standing on the pusher, it will definitely be moved
1707 // if the entity is not standing on the pusher, but is in the pusher's
1708 // final position, move it
1709 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1711 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);
1712 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1713 if (!trace.startsolid)
1715 //Con_Printf("- not in solid\n");
1723 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1724 org2[0] = DotProduct (org, forward);
1725 org2[1] = DotProduct (org, left);
1726 org2[2] = DotProduct (org, up);
1727 VectorSubtract (org2, org, move);
1728 VectorAdd (move, move1, move);
1731 VectorCopy (move1, move);
1733 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1735 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1736 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1737 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1739 // physics objects need better collisions than this code can do
1740 if (movetype == MOVETYPE_PHYSICS)
1742 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1743 SV_LinkEdict(check);
1744 SV_LinkEdict_TouchAreaGrid(check);
1748 // try moving the contacted entity
1749 pusher->fields.server->solid = SOLID_NOT;
1750 if(!SV_PushEntity (&trace, check, move, true, true))
1752 // entity "check" got teleported
1753 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1754 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1755 continue; // pushed enough
1757 // FIXME: turn players specially
1758 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1759 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1760 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1762 // this trace.fraction < 1 check causes items to fall off of pushers
1763 // if they pass under or through a wall
1764 // the groundentity check causes items to fall off of ledges
1765 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1766 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1768 // if it is still inside the pusher, block
1769 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);
1770 if (trace.startsolid)
1772 // try moving the contacted entity a tiny bit further to account for precision errors
1774 pusher->fields.server->solid = SOLID_NOT;
1775 VectorScale(move, 1.1, move2);
1776 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1777 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1778 if(!SV_PushEntity (&trace2, check, move2, true, true))
1780 // entity "check" got teleported
1783 pusher->fields.server->solid = savesolid;
1784 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);
1785 if (trace.startsolid)
1787 // try moving the contacted entity a tiny bit less to account for precision errors
1788 pusher->fields.server->solid = SOLID_NOT;
1789 VectorScale(move, 0.9, move2);
1790 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1791 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1792 if(!SV_PushEntity (&trace2, check, move2, true, true))
1794 // entity "check" got teleported
1797 pusher->fields.server->solid = savesolid;
1798 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);
1799 if (trace.startsolid)
1801 // still inside pusher, so it's really blocked
1804 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1806 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1809 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1810 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1814 VectorCopy (pushorig, pusher->fields.server->origin);
1815 VectorCopy (pushang, pusher->fields.server->angles);
1816 pusher->fields.server->ltime = pushltime;
1817 SV_LinkEdict(pusher);
1819 // move back any entities we already moved
1820 for (i = 0;i < num_moved;i++)
1822 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1823 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1824 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1828 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1829 if (pusher->fields.server->blocked)
1831 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1832 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1833 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1840 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1841 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1842 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1851 void SV_Physics_Pusher (prvm_edict_t *ent)
1853 float thinktime, oldltime, movetime;
1855 oldltime = ent->fields.server->ltime;
1857 thinktime = ent->fields.server->nextthink;
1858 if (thinktime < ent->fields.server->ltime + sv.frametime)
1860 movetime = thinktime - ent->fields.server->ltime;
1865 movetime = sv.frametime;
1868 // advances ent->fields.server->ltime if not blocked
1869 SV_PushMove (ent, movetime);
1871 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1873 ent->fields.server->nextthink = 0;
1874 prog->globals.server->time = sv.time;
1875 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1876 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1877 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1883 ===============================================================================
1887 ===============================================================================
1890 static float unstickoffsets[] =
1892 // poutting -/+z changes first as they are least weird
1907 typedef enum unstickresult_e
1915 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1919 // if not stuck in a bmodel, just return
1920 if (!SV_TestEntityPosition(ent, vec3_origin))
1921 return UNSTICK_GOOD;
1923 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1925 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1927 VectorCopy(unstickoffsets + i, offset);
1929 //SV_LinkEdict_TouchAreaGrid(ent);
1930 return UNSTICK_UNSTUCK;
1934 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1935 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1937 for(i = 2; i <= maxunstick; ++i)
1939 VectorClear(offset);
1941 if (!SV_TestEntityPosition(ent, offset))
1944 //SV_LinkEdict_TouchAreaGrid(ent);
1945 return UNSTICK_UNSTUCK;
1948 if (!SV_TestEntityPosition(ent, offset))
1951 //SV_LinkEdict_TouchAreaGrid(ent);
1952 return UNSTICK_UNSTUCK;
1956 return UNSTICK_STUCK;
1959 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1962 switch(SV_UnstickEntityReturnOffset(ent, offset))
1966 case UNSTICK_UNSTUCK:
1967 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]);
1970 if (developer.integer >= 100)
1971 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1974 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1983 This is a big hack to try and fix the rare case of getting stuck in the world
1987 void SV_CheckStuck (prvm_edict_t *ent)
1991 switch(SV_UnstickEntityReturnOffset(ent, offset))
1994 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1996 case UNSTICK_UNSTUCK:
1997 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]);
2000 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
2001 if (!SV_TestEntityPosition(ent, offset))
2003 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2005 //SV_LinkEdict_TouchAreaGrid(ent);
2008 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2011 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2021 qboolean SV_CheckWater (prvm_edict_t *ent)
2024 int nNativeContents;
2027 point[0] = ent->fields.server->origin[0];
2028 point[1] = ent->fields.server->origin[1];
2029 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2031 // DRESK - Support for Entity Contents Transition Event
2032 // NOTE: Some logic needed to be slightly re-ordered
2033 // to not affect performance and allow for the feature.
2035 // Acquire Super Contents Prior to Resets
2036 cont = SV_PointSuperContents(point);
2037 // Acquire Native Contents Here
2038 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2040 // DRESK - Support for Entity Contents Transition Event
2041 if(ent->fields.server->watertype)
2042 // Entity did NOT Spawn; Check
2043 SV_CheckContentsTransition(ent, nNativeContents);
2046 ent->fields.server->waterlevel = 0;
2047 ent->fields.server->watertype = CONTENTS_EMPTY;
2048 cont = SV_PointSuperContents(point);
2049 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2051 ent->fields.server->watertype = nNativeContents;
2052 ent->fields.server->waterlevel = 1;
2053 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2054 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2056 ent->fields.server->waterlevel = 2;
2057 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2058 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2059 ent->fields.server->waterlevel = 3;
2063 return ent->fields.server->waterlevel > 1;
2072 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2075 vec3_t forward, into, side;
2077 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2078 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2080 // cut the tangential velocity
2081 i = DotProduct (stepnormal, ent->fields.server->velocity);
2082 VectorScale (stepnormal, i, into);
2083 VectorSubtract (ent->fields.server->velocity, into, side);
2084 ent->fields.server->velocity[0] = side[0] * (1 + d);
2085 ent->fields.server->velocity[1] = side[1] * (1 + d);
2091 =====================
2094 Player has come to a dead stop, possibly due to the problem with limited
2095 float precision at some angle joins in the BSP hull.
2097 Try fixing by pushing one pixel in each direction.
2099 This is a hack, but in the interest of good gameplay...
2100 ======================
2102 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2107 VectorCopy (ent->fields.server->origin, oldorg);
2110 for (i=0 ; i<8 ; i++)
2112 // try pushing a little in an axial direction
2115 case 0: dir[0] = 2; dir[1] = 0; break;
2116 case 1: dir[0] = 0; dir[1] = 2; break;
2117 case 2: dir[0] = -2; dir[1] = 0; break;
2118 case 3: dir[0] = 0; dir[1] = -2; break;
2119 case 4: dir[0] = 2; dir[1] = 2; break;
2120 case 5: dir[0] = -2; dir[1] = 2; break;
2121 case 6: dir[0] = 2; dir[1] = -2; break;
2122 case 7: dir[0] = -2; dir[1] = -2; break;
2125 SV_PushEntity (&trace, ent, dir, false, true);
2127 // retry the original move
2128 ent->fields.server->velocity[0] = oldvel[0];
2129 ent->fields.server->velocity[1] = oldvel[1];
2130 ent->fields.server->velocity[2] = 0;
2131 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2133 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2134 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2136 Con_DPrint("TryUnstick - success.\n");
2140 // go back to the original pos and try again
2141 VectorCopy (oldorg, ent->fields.server->origin);
2145 VectorClear (ent->fields.server->velocity);
2146 Con_DPrint("TryUnstick - failure.\n");
2152 =====================
2155 Only used by players
2156 ======================
2158 void SV_WalkMove (prvm_edict_t *ent)
2160 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask, type;
2161 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2162 trace_t downtrace, trace;
2163 qboolean applygravity;
2165 // if frametime is 0 (due to client sending the same timestamp twice),
2167 if (sv.frametime <= 0)
2170 SV_CheckStuck (ent);
2172 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2174 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2176 SV_CheckVelocity(ent);
2178 // do a regular slide move unless it looks like you ran into a step
2179 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2181 VectorCopy (ent->fields.server->origin, start_origin);
2182 VectorCopy (ent->fields.server->velocity, start_velocity);
2184 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2186 // if the move did not hit the ground at any point, we're not on ground
2188 // only try this if there was no floor in the way in the trace (no,
2189 // this check seems to be not REALLY necessary, because if clip & 1,
2190 // our trace will hit that thing too)
2192 VectorSet(upmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + 1);
2193 VectorSet(downmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
2194 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
2195 type = MOVE_MISSILE;
2196 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
2197 type = MOVE_NOMONSTERS; // only clip against bmodels
2200 trace = SV_TraceBox(upmove, ent->fields.server->mins, ent->fields.server->maxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2201 if(trace.fraction >= 1 || trace.plane.normal[2] <= 0.7)
2202 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2205 SV_CheckVelocity(ent);
2207 SV_LinkEdict_TouchAreaGrid(ent);
2209 if(clip & 8) // teleport
2212 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2215 if (sv_nostep.integer)
2218 VectorCopy(ent->fields.server->origin, originalmove_origin);
2219 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2220 originalmove_clip = clip;
2221 originalmove_flags = (int)ent->fields.server->flags;
2222 originalmove_groundentity = ent->fields.server->groundentity;
2224 // if move didn't block on a step, return
2227 // if move was not trying to move into the step, return
2228 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2231 if (ent->fields.server->movetype != MOVETYPE_FLY)
2233 // return if gibbed by a trigger
2234 if (ent->fields.server->movetype != MOVETYPE_WALK)
2237 // only step up while jumping if that is enabled
2238 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2239 if (!oldonground && ent->fields.server->waterlevel == 0)
2243 // try moving up and forward to go up a step
2244 // back to start pos
2245 VectorCopy (start_origin, ent->fields.server->origin);
2246 VectorCopy (start_velocity, ent->fields.server->velocity);
2249 VectorClear (upmove);
2250 upmove[2] = sv_stepheight.value;
2251 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2253 // we got teleported when upstepping... must abort the move
2258 ent->fields.server->velocity[2] = 0;
2259 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2260 ent->fields.server->velocity[2] += start_velocity[2];
2263 // we got teleported when upstepping... must abort the move
2264 // note that z velocity handling may not be what QC expects here, but we cannot help it
2268 SV_CheckVelocity(ent);
2270 SV_LinkEdict_TouchAreaGrid(ent);
2272 // check for stuckness, possibly due to the limited precision of floats
2273 // in the clipping hulls
2275 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2276 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2278 //Con_Printf("wall\n");
2279 // stepping up didn't make any progress, revert to original move
2280 VectorCopy(originalmove_origin, ent->fields.server->origin);
2281 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2282 //clip = originalmove_clip;
2283 ent->fields.server->flags = originalmove_flags;
2284 ent->fields.server->groundentity = originalmove_groundentity;
2285 // now try to unstick if needed
2286 //clip = SV_TryUnstick (ent, oldvel);
2290 //Con_Printf("step - ");
2292 // extra friction based on view angle
2293 if (clip & 2 && sv_wallfriction.integer)
2294 SV_WallFriction (ent, stepnormal);
2296 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2297 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))
2301 VectorClear (downmove);
2302 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2303 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2305 // we got teleported when downstepping... must abort the move
2309 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2311 // this has been disabled so that you can't jump when you are stepping
2312 // up while already jumping (also known as the Quake2 double jump bug)
2314 // LordHavoc: disabled this check so you can walk on monsters/players
2315 //if (ent->fields.server->solid == SOLID_BSP)
2317 //Con_Printf("onground\n");
2318 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2319 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2325 //Con_Printf("slope\n");
2326 // if the push down didn't end up on good ground, use the move without
2327 // the step up. This happens near wall / slope combinations, and can
2328 // cause the player to hop up higher on a slope too steep to climb
2329 VectorCopy(originalmove_origin, ent->fields.server->origin);
2330 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2331 //clip = originalmove_clip;
2332 ent->fields.server->flags = originalmove_flags;
2333 ent->fields.server->groundentity = originalmove_groundentity;
2336 SV_CheckVelocity(ent);
2338 SV_LinkEdict_TouchAreaGrid(ent);
2341 //============================================================================
2347 Entities that are "stuck" to another entity
2350 void SV_Physics_Follow (prvm_edict_t *ent)
2352 vec3_t vf, vr, vu, angles, v;
2356 if (!SV_RunThink (ent))
2359 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2360 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2361 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])
2363 // quick case for no rotation
2364 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2368 angles[0] = -ent->fields.server->punchangle[0];
2369 angles[1] = ent->fields.server->punchangle[1];
2370 angles[2] = ent->fields.server->punchangle[2];
2371 AngleVectors (angles, vf, vr, vu);
2372 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];
2373 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];
2374 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];
2375 angles[0] = -e->fields.server->angles[0];
2376 angles[1] = e->fields.server->angles[1];
2377 angles[2] = e->fields.server->angles[2];
2378 AngleVectors (angles, vf, vr, vu);
2379 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2380 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2381 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2383 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2385 //SV_LinkEdict_TouchAreaGrid(ent);
2389 ==============================================================================
2393 ==============================================================================
2398 SV_CheckWaterTransition
2402 void SV_CheckWaterTransition (prvm_edict_t *ent)
2405 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2406 if (!ent->fields.server->watertype)
2408 // just spawned here
2409 ent->fields.server->watertype = cont;
2410 ent->fields.server->waterlevel = 1;
2414 // DRESK - Support for Entity Contents Transition Event
2415 // NOTE: Call here BEFORE updating the watertype below,
2416 // and suppress watersplash sound if a valid function
2417 // call was made to allow for custom "splash" sounds.
2418 if( !SV_CheckContentsTransition(ent, cont) )
2419 { // Contents Transition Function Invalid; Potentially Play Water Sound
2420 // check if the entity crossed into or out of water
2421 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2422 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2425 if (cont <= CONTENTS_WATER)
2427 ent->fields.server->watertype = cont;
2428 ent->fields.server->waterlevel = 1;
2432 ent->fields.server->watertype = CONTENTS_EMPTY;
2433 ent->fields.server->waterlevel = 0;
2441 Toss, bounce, and fly movement. When onground, do nothing.
2444 void SV_Physics_Toss (prvm_edict_t *ent)
2450 prvm_edict_t *groundentity;
2452 // if onground, return without moving
2453 if ((int)ent->fields.server->flags & FL_ONGROUND)
2455 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2456 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2458 // don't stick to ground if onground and moving upward
2459 ent->fields.server->flags -= FL_ONGROUND;
2461 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2463 // we can trust FL_ONGROUND if groundentity is world because it never moves
2466 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2468 // if ent was supported by a brush model on previous frame,
2469 // and groundentity is now freed, set groundentity to 0 (world)
2470 // which leaves it suspended in the air
2471 ent->fields.server->groundentity = 0;
2472 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2475 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2477 // don't slide if still touching the groundentity
2481 ent->priv.server->suspendedinairflag = false;
2483 SV_CheckVelocity (ent);
2486 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2487 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2490 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2492 movetime = sv.frametime;
2493 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2496 VectorScale (ent->fields.server->velocity, movetime, move);
2497 if(!SV_PushEntity (&trace, ent, move, true, true))
2498 return; // teleported
2499 if (ent->priv.server->free)
2501 if (trace.bmodelstartsolid)
2503 // try to unstick the entity
2504 SV_UnstickEntity(ent);
2505 if(!SV_PushEntity (&trace, ent, move, false, true))
2506 return; // teleported
2507 if (ent->priv.server->free)
2510 if (trace.fraction == 1)
2512 movetime *= 1 - min(1, trace.fraction);
2513 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2516 float bouncefactor = 1.0f;
2517 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2518 if (val!=0 && val->_float)
2519 bouncefactor = val->_float;
2521 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2522 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2524 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2526 float d, ent_gravity;
2528 float bouncefactor = 0.5f;
2529 float bouncestop = 60.0f / 800.0f;
2531 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2532 if (val!=0 && val->_float)
2533 bouncefactor = val->_float;
2535 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2536 if (val!=0 && val->_float)
2537 bouncestop = val->_float;
2539 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2540 // LordHavoc: fixed grenades not bouncing when fired down a slope
2541 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2542 if (val!=0 && val->_float)
2543 ent_gravity = val->_float;
2546 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2548 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2549 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2551 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2552 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2553 VectorClear (ent->fields.server->velocity);
2554 VectorClear (ent->fields.server->avelocity);
2557 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2561 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2563 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2564 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2565 VectorClear (ent->fields.server->velocity);
2566 VectorClear (ent->fields.server->avelocity);
2569 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2574 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2575 if (trace.plane.normal[2] > 0.7)
2577 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2578 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2579 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2580 ent->priv.server->suspendedinairflag = true;
2581 VectorClear (ent->fields.server->velocity);
2582 VectorClear (ent->fields.server->avelocity);
2585 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2587 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2591 // check for in water
2592 SV_CheckWaterTransition (ent);
2596 ===============================================================================
2600 ===============================================================================
2607 Monsters freefall when they don't have a ground entity, otherwise
2608 all movement is done with discrete steps.
2610 This is also used for objects that have become still on the ground, but
2611 will fall if the floor is pulled out from under them.
2614 void SV_Physics_Step (prvm_edict_t *ent)
2616 int flags = (int)ent->fields.server->flags;
2619 // Backup Velocity in the event that movetypesteplandevent is called,
2620 // to provide a parameter with the entity's velocity at impact.
2621 prvm_eval_t *movetypesteplandevent;
2622 vec3_t backupVelocity;
2623 VectorCopy(ent->fields.server->velocity, backupVelocity);
2624 // don't fall at all if fly/swim
2625 if (!(flags & (FL_FLY | FL_SWIM)))
2627 if (flags & FL_ONGROUND)
2629 // freefall if onground and moving upward
2630 // freefall if not standing on a world surface (it may be a lift or trap door)
2631 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2633 ent->fields.server->flags -= FL_ONGROUND;
2634 SV_CheckVelocity(ent);
2635 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2637 SV_LinkEdict_TouchAreaGrid(ent);
2638 ent->priv.server->waterposition_forceupdate = true;
2643 // freefall if not onground
2644 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2646 SV_CheckVelocity(ent);
2647 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2649 SV_LinkEdict_TouchAreaGrid(ent);
2652 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2654 // DRESK - Check for Entity Land Event Function
2655 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2657 if(movetypesteplandevent->function)
2658 { // Valid Function; Execute
2659 // Prepare Parameters
2660 // Assign Velocity at Impact
2661 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2662 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2663 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2665 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2666 // Execute VM Function
2667 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2670 // Check for Engine Landing Sound
2671 if(sv_sound_land.string)
2672 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2674 ent->priv.server->waterposition_forceupdate = true;
2679 if (!SV_RunThink(ent))
2682 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2684 ent->priv.server->waterposition_forceupdate = false;
2685 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2686 SV_CheckWaterTransition(ent);
2690 //============================================================================
2692 static void SV_Physics_Entity (prvm_edict_t *ent)
2694 // don't run think/move on newly spawned projectiles as it messes up
2695 // movement interpolation and rocket trails, and is inconsistent with
2696 // respect to entities spawned in the same frame
2697 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2698 // but if it spawns a lower numbered ent, it doesn't - this never moves
2699 // ents in the first frame regardless)
2700 qboolean runmove = ent->priv.server->move;
2701 ent->priv.server->move = true;
2702 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2704 switch ((int) ent->fields.server->movetype)
2707 case MOVETYPE_FAKEPUSH:
2708 SV_Physics_Pusher (ent);
2711 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2712 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2715 case MOVETYPE_FOLLOW:
2716 SV_Physics_Follow (ent);
2718 case MOVETYPE_NOCLIP:
2719 if (SV_RunThink(ent))
2722 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2723 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2728 SV_Physics_Step (ent);
2731 if (SV_RunThink (ent))
2735 case MOVETYPE_BOUNCE:
2736 case MOVETYPE_BOUNCEMISSILE:
2737 case MOVETYPE_FLYMISSILE:
2740 if (SV_RunThink (ent))
2741 SV_Physics_Toss (ent);
2743 case MOVETYPE_PHYSICS:
2744 if (SV_RunThink(ent))
2747 SV_LinkEdict_TouchAreaGrid(ent);
2751 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2756 void SV_Physics_ClientMove(void)
2759 ent = host_client->edict;
2761 // call player physics, this needs the proper frametime
2762 prog->globals.server->frametime = sv.frametime;
2765 // call standard client pre-think, with frametime = 0
2766 prog->globals.server->time = sv.time;
2767 prog->globals.server->frametime = 0;
2768 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2769 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2770 prog->globals.server->frametime = sv.frametime;
2772 // make sure the velocity is sane (not a NaN)
2773 SV_CheckVelocity(ent);
2775 // perform MOVETYPE_WALK behavior
2778 // call standard player post-think, with frametime = 0
2779 prog->globals.server->time = sv.time;
2780 prog->globals.server->frametime = 0;
2781 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2782 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2783 prog->globals.server->frametime = sv.frametime;
2785 if(ent->fields.server->fixangle)
2787 // angle fixing was requested by physics code...
2788 // so store the current angles for later use
2789 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2790 host_client->fixangle_angles_set = TRUE;
2792 // and clear fixangle for the next frame
2793 ent->fields.server->fixangle = 0;
2797 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2799 // don't do physics on disconnected clients, FrikBot relies on this
2800 if (!host_client->spawned)
2803 // make sure the velocity is sane (not a NaN)
2804 SV_CheckVelocity(ent);
2806 // don't run physics here if running asynchronously
2807 if (host_client->clmovement_inputtimeout <= 0)
2810 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2813 // make sure the velocity is still sane (not a NaN)
2814 SV_CheckVelocity(ent);
2816 // call standard client pre-think
2817 prog->globals.server->time = sv.time;
2818 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2819 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2821 // make sure the velocity is still sane (not a NaN)
2822 SV_CheckVelocity(ent);
2825 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2827 // don't do physics on disconnected clients, FrikBot relies on this
2828 if (!host_client->spawned)
2831 // make sure the velocity is sane (not a NaN)
2832 SV_CheckVelocity(ent);
2834 // call standard player post-think
2835 prog->globals.server->time = sv.time;
2836 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2837 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2839 // make sure the velocity is still sane (not a NaN)
2840 SV_CheckVelocity(ent);
2842 if(ent->fields.server->fixangle)
2844 // angle fixing was requested by physics code...
2845 // so store the current angles for later use
2846 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2847 host_client->fixangle_angles_set = TRUE;
2849 // and clear fixangle for the next frame
2850 ent->fields.server->fixangle = 0;
2853 // decrement the countdown variable used to decide when to go back to
2854 // synchronous physics
2855 if (host_client->clmovement_inputtimeout > sv.frametime)
2856 host_client->clmovement_inputtimeout -= sv.frametime;
2858 host_client->clmovement_inputtimeout = 0;
2861 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2863 // don't do physics on disconnected clients, FrikBot relies on this
2864 if (!host_client->spawned)
2866 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2870 // make sure the velocity is sane (not a NaN)
2871 SV_CheckVelocity(ent);
2873 switch ((int) ent->fields.server->movetype)
2876 case MOVETYPE_FAKEPUSH:
2877 SV_Physics_Pusher (ent);
2880 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2881 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2884 case MOVETYPE_FOLLOW:
2885 SV_Physics_Follow (ent);
2887 case MOVETYPE_NOCLIP:
2890 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2891 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2894 SV_Physics_Step (ent);
2898 // don't run physics here if running asynchronously
2899 if (host_client->clmovement_inputtimeout <= 0)
2903 case MOVETYPE_BOUNCE:
2904 case MOVETYPE_BOUNCEMISSILE:
2905 case MOVETYPE_FLYMISSILE:
2908 SV_Physics_Toss (ent);
2914 case MOVETYPE_PHYSICS:
2918 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2922 SV_CheckVelocity (ent);
2925 SV_LinkEdict_TouchAreaGrid(ent);
2927 SV_CheckVelocity (ent);
2936 void SV_Physics (void)
2941 // let the progs know that a new frame has started
2942 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2943 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2944 prog->globals.server->time = sv.time;
2945 prog->globals.server->frametime = sv.frametime;
2946 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2948 // run physics engine
2949 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2952 // treat each object in turn
2955 // if force_retouch, relink all the entities
2956 if (prog->globals.server->force_retouch > 0)
2957 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2958 if (!ent->priv.server->free)
2959 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2961 if (sv_gameplayfix_consistentplayerprethink.integer)
2963 // run physics on the client entities in 3 stages
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_PreThink(ent);
2968 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2969 if (!ent->priv.server->free)
2970 SV_Physics_ClientEntity(ent);
2972 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)
2974 SV_Physics_ClientEntity_PostThink(ent);
2978 // run physics on the client entities
2979 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2981 if (!ent->priv.server->free)
2983 SV_Physics_ClientEntity_PreThink(ent);
2984 SV_Physics_ClientEntity(ent);
2985 SV_Physics_ClientEntity_PostThink(ent);
2990 // run physics on all the non-client entities
2991 if (!sv_freezenonclients.integer)
2993 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2994 if (!ent->priv.server->free)
2995 SV_Physics_Entity(ent);
2996 // make a second pass to see if any ents spawned this frame and make
2997 // sure they run their move/think
2998 if (sv_gameplayfix_delayprojectiles.integer < 0)
2999 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3000 if (!ent->priv.server->move && !ent->priv.server->free)
3001 SV_Physics_Entity(ent);
3004 if (prog->globals.server->force_retouch > 0)
3005 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
3007 // LordHavoc: endframe support
3008 if (prog->funcoffsets.EndFrame)
3010 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
3011 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
3012 prog->globals.server->time = sv.time;
3013 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
3016 // decrement prog->num_edicts if the highest number entities died
3017 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3019 if (!sv_freezenonclients.integer)
3020 sv.time += sv.frametime;