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(sv_gameplayfix_downtracesupportsongroundflag.integer)
2189 // only try this if there was no floor in the way in the trace (no,
2190 // this check seems to be not REALLY necessary, because if clip & 1,
2191 // 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 clip |= 1; // but we HAVE found a floor
2205 // if the move did not hit the ground at any point, we're not on ground
2207 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2209 SV_CheckVelocity(ent);
2211 SV_LinkEdict_TouchAreaGrid(ent);
2213 if(clip & 8) // teleport
2216 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2219 if (sv_nostep.integer)
2222 VectorCopy(ent->fields.server->origin, originalmove_origin);
2223 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2224 originalmove_clip = clip;
2225 originalmove_flags = (int)ent->fields.server->flags;
2226 originalmove_groundentity = ent->fields.server->groundentity;
2228 // if move didn't block on a step, return
2231 // if move was not trying to move into the step, return
2232 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2235 if (ent->fields.server->movetype != MOVETYPE_FLY)
2237 // return if gibbed by a trigger
2238 if (ent->fields.server->movetype != MOVETYPE_WALK)
2241 // only step up while jumping if that is enabled
2242 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2243 if (!oldonground && ent->fields.server->waterlevel == 0)
2247 // try moving up and forward to go up a step
2248 // back to start pos
2249 VectorCopy (start_origin, ent->fields.server->origin);
2250 VectorCopy (start_velocity, ent->fields.server->velocity);
2253 VectorClear (upmove);
2254 upmove[2] = sv_stepheight.value;
2255 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2257 // we got teleported when upstepping... must abort the move
2262 ent->fields.server->velocity[2] = 0;
2263 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2264 ent->fields.server->velocity[2] += start_velocity[2];
2267 // we got teleported when upstepping... must abort the move
2268 // note that z velocity handling may not be what QC expects here, but we cannot help it
2272 SV_CheckVelocity(ent);
2274 SV_LinkEdict_TouchAreaGrid(ent);
2276 // check for stuckness, possibly due to the limited precision of floats
2277 // in the clipping hulls
2279 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2280 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2282 //Con_Printf("wall\n");
2283 // stepping up didn't make any progress, revert to original move
2284 VectorCopy(originalmove_origin, ent->fields.server->origin);
2285 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2286 //clip = originalmove_clip;
2287 ent->fields.server->flags = originalmove_flags;
2288 ent->fields.server->groundentity = originalmove_groundentity;
2289 // now try to unstick if needed
2290 //clip = SV_TryUnstick (ent, oldvel);
2294 //Con_Printf("step - ");
2296 // extra friction based on view angle
2297 if (clip & 2 && sv_wallfriction.integer)
2298 SV_WallFriction (ent, stepnormal);
2300 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2301 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))
2305 VectorClear (downmove);
2306 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2307 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2309 // we got teleported when downstepping... must abort the move
2313 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2315 // this has been disabled so that you can't jump when you are stepping
2316 // up while already jumping (also known as the Quake2 double jump bug)
2318 // LordHavoc: disabled this check so you can walk on monsters/players
2319 //if (ent->fields.server->solid == SOLID_BSP)
2321 //Con_Printf("onground\n");
2322 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2323 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2329 //Con_Printf("slope\n");
2330 // if the push down didn't end up on good ground, use the move without
2331 // the step up. This happens near wall / slope combinations, and can
2332 // cause the player to hop up higher on a slope too steep to climb
2333 VectorCopy(originalmove_origin, ent->fields.server->origin);
2334 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2335 //clip = originalmove_clip;
2336 ent->fields.server->flags = originalmove_flags;
2337 ent->fields.server->groundentity = originalmove_groundentity;
2340 SV_CheckVelocity(ent);
2342 SV_LinkEdict_TouchAreaGrid(ent);
2345 //============================================================================
2351 Entities that are "stuck" to another entity
2354 void SV_Physics_Follow (prvm_edict_t *ent)
2356 vec3_t vf, vr, vu, angles, v;
2360 if (!SV_RunThink (ent))
2363 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2364 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2365 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])
2367 // quick case for no rotation
2368 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2372 angles[0] = -ent->fields.server->punchangle[0];
2373 angles[1] = ent->fields.server->punchangle[1];
2374 angles[2] = ent->fields.server->punchangle[2];
2375 AngleVectors (angles, vf, vr, vu);
2376 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];
2377 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];
2378 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];
2379 angles[0] = -e->fields.server->angles[0];
2380 angles[1] = e->fields.server->angles[1];
2381 angles[2] = e->fields.server->angles[2];
2382 AngleVectors (angles, vf, vr, vu);
2383 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2384 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2385 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2387 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2389 //SV_LinkEdict_TouchAreaGrid(ent);
2393 ==============================================================================
2397 ==============================================================================
2402 SV_CheckWaterTransition
2406 void SV_CheckWaterTransition (prvm_edict_t *ent)
2409 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2410 if (!ent->fields.server->watertype)
2412 // just spawned here
2413 ent->fields.server->watertype = cont;
2414 ent->fields.server->waterlevel = 1;
2418 // DRESK - Support for Entity Contents Transition Event
2419 // NOTE: Call here BEFORE updating the watertype below,
2420 // and suppress watersplash sound if a valid function
2421 // call was made to allow for custom "splash" sounds.
2422 if( !SV_CheckContentsTransition(ent, cont) )
2423 { // Contents Transition Function Invalid; Potentially Play Water Sound
2424 // check if the entity crossed into or out of water
2425 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2426 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2429 if (cont <= CONTENTS_WATER)
2431 ent->fields.server->watertype = cont;
2432 ent->fields.server->waterlevel = 1;
2436 ent->fields.server->watertype = CONTENTS_EMPTY;
2437 ent->fields.server->waterlevel = 0;
2445 Toss, bounce, and fly movement. When onground, do nothing.
2448 void SV_Physics_Toss (prvm_edict_t *ent)
2454 prvm_edict_t *groundentity;
2456 // if onground, return without moving
2457 if ((int)ent->fields.server->flags & FL_ONGROUND)
2459 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2460 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2462 // don't stick to ground if onground and moving upward
2463 ent->fields.server->flags -= FL_ONGROUND;
2465 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2467 // we can trust FL_ONGROUND if groundentity is world because it never moves
2470 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2472 // if ent was supported by a brush model on previous frame,
2473 // and groundentity is now freed, set groundentity to 0 (world)
2474 // which leaves it suspended in the air
2475 ent->fields.server->groundentity = 0;
2476 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2479 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2481 // don't slide if still touching the groundentity
2485 ent->priv.server->suspendedinairflag = false;
2487 SV_CheckVelocity (ent);
2490 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2491 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2494 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2496 movetime = sv.frametime;
2497 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2500 VectorScale (ent->fields.server->velocity, movetime, move);
2501 if(!SV_PushEntity (&trace, ent, move, true, true))
2502 return; // teleported
2503 if (ent->priv.server->free)
2505 if (trace.bmodelstartsolid)
2507 // try to unstick the entity
2508 SV_UnstickEntity(ent);
2509 if(!SV_PushEntity (&trace, ent, move, false, true))
2510 return; // teleported
2511 if (ent->priv.server->free)
2514 if (trace.fraction == 1)
2516 movetime *= 1 - min(1, trace.fraction);
2517 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2520 float bouncefactor = 1.0f;
2521 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2522 if (val!=0 && val->_float)
2523 bouncefactor = val->_float;
2525 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2526 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2528 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2530 float d, ent_gravity;
2532 float bouncefactor = 0.5f;
2533 float bouncestop = 60.0f / 800.0f;
2535 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2536 if (val!=0 && val->_float)
2537 bouncefactor = val->_float;
2539 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2540 if (val!=0 && val->_float)
2541 bouncestop = val->_float;
2543 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2544 // LordHavoc: fixed grenades not bouncing when fired down a slope
2545 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2546 if (val!=0 && val->_float)
2547 ent_gravity = val->_float;
2550 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2552 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2553 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2555 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2556 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2557 VectorClear (ent->fields.server->velocity);
2558 VectorClear (ent->fields.server->avelocity);
2561 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2565 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2567 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2568 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2569 VectorClear (ent->fields.server->velocity);
2570 VectorClear (ent->fields.server->avelocity);
2573 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2578 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2579 if (trace.plane.normal[2] > 0.7)
2581 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2582 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2583 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2584 ent->priv.server->suspendedinairflag = true;
2585 VectorClear (ent->fields.server->velocity);
2586 VectorClear (ent->fields.server->avelocity);
2589 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2591 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2595 // check for in water
2596 SV_CheckWaterTransition (ent);
2600 ===============================================================================
2604 ===============================================================================
2611 Monsters freefall when they don't have a ground entity, otherwise
2612 all movement is done with discrete steps.
2614 This is also used for objects that have become still on the ground, but
2615 will fall if the floor is pulled out from under them.
2618 void SV_Physics_Step (prvm_edict_t *ent)
2620 int flags = (int)ent->fields.server->flags;
2623 // Backup Velocity in the event that movetypesteplandevent is called,
2624 // to provide a parameter with the entity's velocity at impact.
2625 prvm_eval_t *movetypesteplandevent;
2626 vec3_t backupVelocity;
2627 VectorCopy(ent->fields.server->velocity, backupVelocity);
2628 // don't fall at all if fly/swim
2629 if (!(flags & (FL_FLY | FL_SWIM)))
2631 if (flags & FL_ONGROUND)
2633 // freefall if onground and moving upward
2634 // freefall if not standing on a world surface (it may be a lift or trap door)
2635 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2637 ent->fields.server->flags -= FL_ONGROUND;
2638 SV_CheckVelocity(ent);
2639 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2641 SV_LinkEdict_TouchAreaGrid(ent);
2642 ent->priv.server->waterposition_forceupdate = true;
2647 // freefall if not onground
2648 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2650 SV_CheckVelocity(ent);
2651 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2653 SV_LinkEdict_TouchAreaGrid(ent);
2656 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2658 // DRESK - Check for Entity Land Event Function
2659 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2661 if(movetypesteplandevent->function)
2662 { // Valid Function; Execute
2663 // Prepare Parameters
2664 // Assign Velocity at Impact
2665 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2666 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2667 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2669 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2670 // Execute VM Function
2671 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2674 // Check for Engine Landing Sound
2675 if(sv_sound_land.string)
2676 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2678 ent->priv.server->waterposition_forceupdate = true;
2683 if (!SV_RunThink(ent))
2686 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2688 ent->priv.server->waterposition_forceupdate = false;
2689 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2690 SV_CheckWaterTransition(ent);
2694 //============================================================================
2696 static void SV_Physics_Entity (prvm_edict_t *ent)
2698 // don't run think/move on newly spawned projectiles as it messes up
2699 // movement interpolation and rocket trails, and is inconsistent with
2700 // respect to entities spawned in the same frame
2701 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2702 // but if it spawns a lower numbered ent, it doesn't - this never moves
2703 // ents in the first frame regardless)
2704 qboolean runmove = ent->priv.server->move;
2705 ent->priv.server->move = true;
2706 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2708 switch ((int) ent->fields.server->movetype)
2711 case MOVETYPE_FAKEPUSH:
2712 SV_Physics_Pusher (ent);
2715 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2716 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2719 case MOVETYPE_FOLLOW:
2720 SV_Physics_Follow (ent);
2722 case MOVETYPE_NOCLIP:
2723 if (SV_RunThink(ent))
2726 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2727 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2732 SV_Physics_Step (ent);
2735 if (SV_RunThink (ent))
2739 case MOVETYPE_BOUNCE:
2740 case MOVETYPE_BOUNCEMISSILE:
2741 case MOVETYPE_FLYMISSILE:
2744 if (SV_RunThink (ent))
2745 SV_Physics_Toss (ent);
2747 case MOVETYPE_PHYSICS:
2748 if (SV_RunThink(ent))
2751 SV_LinkEdict_TouchAreaGrid(ent);
2755 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2760 void SV_Physics_ClientMove(void)
2763 ent = host_client->edict;
2765 // call player physics, this needs the proper frametime
2766 prog->globals.server->frametime = sv.frametime;
2769 // call standard client pre-think, with frametime = 0
2770 prog->globals.server->time = sv.time;
2771 prog->globals.server->frametime = 0;
2772 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2773 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2774 prog->globals.server->frametime = sv.frametime;
2776 // make sure the velocity is sane (not a NaN)
2777 SV_CheckVelocity(ent);
2779 // perform MOVETYPE_WALK behavior
2782 // call standard player post-think, with frametime = 0
2783 prog->globals.server->time = sv.time;
2784 prog->globals.server->frametime = 0;
2785 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2786 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2787 prog->globals.server->frametime = sv.frametime;
2789 if(ent->fields.server->fixangle)
2791 // angle fixing was requested by physics code...
2792 // so store the current angles for later use
2793 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2794 host_client->fixangle_angles_set = TRUE;
2796 // and clear fixangle for the next frame
2797 ent->fields.server->fixangle = 0;
2801 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2803 // don't do physics on disconnected clients, FrikBot relies on this
2804 if (!host_client->spawned)
2807 // make sure the velocity is sane (not a NaN)
2808 SV_CheckVelocity(ent);
2810 // don't run physics here if running asynchronously
2811 if (host_client->clmovement_inputtimeout <= 0)
2814 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2817 // make sure the velocity is still sane (not a NaN)
2818 SV_CheckVelocity(ent);
2820 // call standard client pre-think
2821 prog->globals.server->time = sv.time;
2822 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2823 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2825 // make sure the velocity is still sane (not a NaN)
2826 SV_CheckVelocity(ent);
2829 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2831 // don't do physics on disconnected clients, FrikBot relies on this
2832 if (!host_client->spawned)
2835 // make sure the velocity is sane (not a NaN)
2836 SV_CheckVelocity(ent);
2838 // call standard player post-think
2839 prog->globals.server->time = sv.time;
2840 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2841 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2843 // make sure the velocity is still sane (not a NaN)
2844 SV_CheckVelocity(ent);
2846 if(ent->fields.server->fixangle)
2848 // angle fixing was requested by physics code...
2849 // so store the current angles for later use
2850 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2851 host_client->fixangle_angles_set = TRUE;
2853 // and clear fixangle for the next frame
2854 ent->fields.server->fixangle = 0;
2857 // decrement the countdown variable used to decide when to go back to
2858 // synchronous physics
2859 if (host_client->clmovement_inputtimeout > sv.frametime)
2860 host_client->clmovement_inputtimeout -= sv.frametime;
2862 host_client->clmovement_inputtimeout = 0;
2865 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2867 // don't do physics on disconnected clients, FrikBot relies on this
2868 if (!host_client->spawned)
2870 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2874 // make sure the velocity is sane (not a NaN)
2875 SV_CheckVelocity(ent);
2877 switch ((int) ent->fields.server->movetype)
2880 case MOVETYPE_FAKEPUSH:
2881 SV_Physics_Pusher (ent);
2884 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2885 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2888 case MOVETYPE_FOLLOW:
2889 SV_Physics_Follow (ent);
2891 case MOVETYPE_NOCLIP:
2894 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2895 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2898 SV_Physics_Step (ent);
2902 // don't run physics here if running asynchronously
2903 if (host_client->clmovement_inputtimeout <= 0)
2907 case MOVETYPE_BOUNCE:
2908 case MOVETYPE_BOUNCEMISSILE:
2909 case MOVETYPE_FLYMISSILE:
2912 SV_Physics_Toss (ent);
2918 case MOVETYPE_PHYSICS:
2922 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2926 SV_CheckVelocity (ent);
2929 SV_LinkEdict_TouchAreaGrid(ent);
2931 SV_CheckVelocity (ent);
2940 void SV_Physics (void)
2945 // let the progs know that a new frame has started
2946 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2947 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2948 prog->globals.server->time = sv.time;
2949 prog->globals.server->frametime = sv.frametime;
2950 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2952 // run physics engine
2953 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2956 // treat each object in turn
2959 // if force_retouch, relink all the entities
2960 if (prog->globals.server->force_retouch > 0)
2961 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2962 if (!ent->priv.server->free)
2963 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2965 if (sv_gameplayfix_consistentplayerprethink.integer)
2967 // run physics on the client entities in 3 stages
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_PreThink(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(ent);
2976 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2977 if (!ent->priv.server->free)
2978 SV_Physics_ClientEntity_PostThink(ent);
2982 // run physics on the client entities
2983 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2985 if (!ent->priv.server->free)
2987 SV_Physics_ClientEntity_PreThink(ent);
2988 SV_Physics_ClientEntity(ent);
2989 SV_Physics_ClientEntity_PostThink(ent);
2994 // run physics on all the non-client entities
2995 if (!sv_freezenonclients.integer)
2997 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2998 if (!ent->priv.server->free)
2999 SV_Physics_Entity(ent);
3000 // make a second pass to see if any ents spawned this frame and make
3001 // sure they run their move/think
3002 if (sv_gameplayfix_delayprojectiles.integer < 0)
3003 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3004 if (!ent->priv.server->move && !ent->priv.server->free)
3005 SV_Physics_Entity(ent);
3008 if (prog->globals.server->force_retouch > 0)
3009 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
3011 // LordHavoc: endframe support
3012 if (prog->funcoffsets.EndFrame)
3014 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
3015 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
3016 prog->globals.server->time = sv.time;
3017 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
3020 // decrement prog->num_edicts if the highest number entities died
3021 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3023 if (!sv_freezenonclients.integer)
3024 sv.time += sv.frametime;