2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
29 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38 solid_edge items only clip against bsp models.
42 #define MOVE_EPSILON 0.01
44 void SV_Physics_Toss (prvm_edict_t *ent);
46 int SV_GetPitchSign(prvm_edict_t *ent)
50 (model = SV_GetModelFromEdict(ent))
52 model->type == mod_alias
55 (((unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
57 ((gamemode == GAME_TENEBRAE) && ((unsigned int)ent->fields.server->effects & (16 | 32)))
65 ===============================================================================
69 ===============================================================================
72 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
77 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
78 if (val && val->_float)
79 return (int)val->_float;
80 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
82 if ((int)passedict->fields.server->flags & FL_MONSTER)
83 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
85 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
87 else if (passedict->fields.server->solid == SOLID_CORPSE)
88 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
89 else if (passedict->fields.server->solid == SOLID_TRIGGER)
90 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
92 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
95 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
103 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
105 int i, bodysupercontents;
108 prvm_edict_t *traceowner, *touch;
110 // bounding box of entire move area
111 vec3_t clipboxmins, clipboxmaxs;
112 // size when clipping against monsters
113 vec3_t clipmins2, clipmaxs2;
114 // start and end origin of move
118 // matrices to transform into/out of other entity's space
119 matrix4x4_t matrix, imatrix;
120 // model of other entity
122 // list of entities to test for collisions
124 static prvm_edict_t *touchedicts[MAX_EDICTS];
126 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
128 VectorCopy(start, clipstart);
129 VectorClear(clipmins2);
130 VectorClear(clipmaxs2);
131 #if COLLISIONPARANOID >= 3
132 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
136 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
137 cliptrace.bmodelstartsolid = cliptrace.startsolid;
138 if (cliptrace.startsolid || cliptrace.fraction < 1)
139 cliptrace.ent = prog->edicts;
140 if (type == MOVE_WORLDONLY)
143 if (type == MOVE_MISSILE)
145 // LordHavoc: modified this, was = -15, now -= 15
146 for (i = 0;i < 3;i++)
153 // create the bounding box of the entire move
154 for (i = 0;i < 3;i++)
156 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
157 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
160 // debug override to test against everything
161 if (sv_debugmove.integer)
163 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
164 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
167 // if the passedict is world, make it NULL (to avoid two checks each time)
168 if (passedict == prog->edicts)
170 // precalculate prog value for passedict for comparisons
171 passedictprog = PRVM_EDICT_TO_PROG(passedict);
172 // precalculate passedict's owner edict pointer for comparisons
173 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
176 // because this uses World_EntitiestoBox, we know all entity boxes overlap
177 // the clip region, so we can skip culling checks in the loop below
178 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
179 if (numtouchedicts > MAX_EDICTS)
181 // this never happens
182 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
183 numtouchedicts = MAX_EDICTS;
185 for (i = 0;i < numtouchedicts;i++)
187 touch = touchedicts[i];
189 if (touch->fields.server->solid < SOLID_BBOX)
191 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
196 // don't clip against self
197 if (passedict == touch)
199 // don't clip owned entities against owner
200 if (traceowner == touch)
202 // don't clip owner against owned entities
203 if (passedictprog == touch->fields.server->owner)
205 // don't clip points against points (they can't collide)
206 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
210 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
212 // might interact, so do an exact clip
214 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
216 model = SV_GetModelFromEdict(touch);
217 pitchsign = SV_GetPitchSign(touch);
220 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
222 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
223 Matrix4x4_Invert_Simple(&imatrix, &matrix);
224 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
225 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
226 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
227 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
228 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
230 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
232 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
244 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
245 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
247 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
250 int i, bodysupercontents;
253 prvm_edict_t *traceowner, *touch;
255 // bounding box of entire move area
256 vec3_t clipboxmins, clipboxmaxs;
257 // size when clipping against monsters
258 vec3_t clipmins2, clipmaxs2;
259 // start and end origin of move
260 vec3_t clipstart, clipend;
263 // matrices to transform into/out of other entity's space
264 matrix4x4_t matrix, imatrix;
265 // model of other entity
267 // list of entities to test for collisions
269 static prvm_edict_t *touchedicts[MAX_EDICTS];
270 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
274 if(!VectorCompare(start, pEnd) && 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 static 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 static 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;
717 static prvm_edict_t *touchedicts[MAX_EDICTS];
719 if (ent == prog->edicts)
720 return; // don't add the world
722 if (ent->priv.server->free)
725 if (ent->fields.server->solid == SOLID_NOT)
728 // build a list of edicts to touch, because the link loop can be corrupted
729 // by IncreaseEdicts called during touch functions
730 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
731 if (numtouchedicts > MAX_EDICTS)
733 // this never happens
734 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
735 numtouchedicts = MAX_EDICTS;
738 old_self = prog->globals.server->self;
739 old_other = prog->globals.server->other;
740 for (i = 0;i < numtouchedicts;i++)
742 touch = touchedicts[i];
743 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
745 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
748 prog->globals.server->self = old_self;
749 prog->globals.server->other = old_other;
752 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
756 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
758 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
759 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
760 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
761 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];
762 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];
763 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
764 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];
765 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];
766 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
767 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];
768 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];
769 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
770 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];
771 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];
772 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
773 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];
774 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];
775 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
776 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];
777 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];
778 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
779 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];
780 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];
789 void SV_LinkEdict (prvm_edict_t *ent)
795 if (ent == prog->edicts)
796 return; // don't add the world
798 if (ent->priv.server->free)
801 modelindex = (int)ent->fields.server->modelindex;
802 if (modelindex < 0 || modelindex >= MAX_MODELS)
804 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
807 model = SV_GetModelByIndex(modelindex);
809 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
810 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
811 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
815 if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
817 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
818 // TODO special handling for spheres?
819 RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs);
820 VectorAdd(ent->fields.server->origin, mins, mins);
821 VectorAdd(ent->fields.server->origin, maxs, maxs);
823 else if (ent->fields.server->solid == SOLID_BSP)
827 if (!model->TraceBox && developer.integer >= 1)
828 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
830 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
832 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
833 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
835 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
837 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
838 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
842 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
843 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
848 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
849 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
850 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
855 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
856 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
860 // to make items easier to pick up and allow them to be grabbed off
861 // of shelves, the abs sizes are expanded
863 if ((int)ent->fields.server->flags & FL_ITEM)
874 // because movement is clipped an epsilon away from an actual edge,
875 // we must fully check even when bounding boxes don't quite touch
884 VectorCopy(mins, ent->fields.server->absmin);
885 VectorCopy(maxs, ent->fields.server->absmax);
887 World_LinkEdict(&sv.world, ent, mins, maxs);
891 ===============================================================================
895 ===============================================================================
900 SV_TestEntityPosition
902 returns true if the entity is in solid currently
905 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
910 contents = SV_GenericHitSuperContentsMask(ent);
911 VectorAdd(ent->fields.server->origin, offset, org);
912 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
913 if (trace.startsupercontents & contents)
917 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
919 // q1bsp/hlbsp use hulls and if the entity does not exactly match
920 // a hull size it is incorrectly tested, so this code tries to
921 // 'fix' it slightly...
922 // FIXME: this breaks entities larger than the hull size
925 VectorAdd(org, ent->fields.server->mins, m1);
926 VectorAdd(org, ent->fields.server->maxs, m2);
927 VectorSubtract(m2, m1, s);
928 #define EPSILON (1.0f / 32.0f)
929 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
930 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
931 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
932 for (i = 0;i < 8;i++)
934 v[0] = (i & 1) ? m2[0] : m1[0];
935 v[1] = (i & 2) ? m2[1] : m1[1];
936 v[2] = (i & 4) ? m2[2] : m1[2];
937 if (SV_PointSuperContents(v) & contents)
942 // if the trace found a better position for the entity, move it there
943 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
946 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
947 VectorCopy(trace.endpos, ent->fields.server->origin);
949 // verify if the endpos is REALLY outside solid
950 VectorCopy(trace.endpos, org);
951 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
953 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
955 VectorCopy(org, ent->fields.server->origin);
966 void SV_CheckAllEnts (void)
971 // see if any solid entities are inside the final position
972 check = PRVM_NEXT_EDICT(prog->edicts);
973 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
975 if (check->priv.server->free)
977 if (check->fields.server->movetype == MOVETYPE_PUSH
978 || check->fields.server->movetype == MOVETYPE_NONE
979 || check->fields.server->movetype == MOVETYPE_FOLLOW
980 || check->fields.server->movetype == MOVETYPE_NOCLIP)
983 if (SV_TestEntityPosition (check, vec3_origin))
984 Con_Print("entity in invalid position\n");
988 // DRESK - Support for Entity Contents Transition Event
991 SV_CheckContentsTransition
993 returns true if entity had a valid contentstransition function call
996 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
998 int bValidFunctionCall;
999 prvm_eval_t *contentstransition;
1001 // Default Valid Function Call to False
1002 bValidFunctionCall = false;
1004 if(ent->fields.server->watertype != nContents)
1005 { // Changed Contents
1006 // Acquire Contents Transition Function from QC
1007 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
1009 if(contentstransition->function)
1010 { // Valid Function; Execute
1011 // Assign Valid Function
1012 bValidFunctionCall = true;
1013 // Prepare Parameters (Original Contents, New Contents)
1014 // Original Contents
1015 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
1017 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1019 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1020 // Execute VM Function
1021 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
1025 // Return if Function Call was Valid
1026 return bValidFunctionCall;
1035 void SV_CheckVelocity (prvm_edict_t *ent)
1043 for (i=0 ; i<3 ; i++)
1045 if (IS_NAN(ent->fields.server->velocity[i]))
1047 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1048 ent->fields.server->velocity[i] = 0;
1050 if (IS_NAN(ent->fields.server->origin[i]))
1052 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1053 ent->fields.server->origin[i] = 0;
1057 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1058 // player_run/player_stand1 does not horribly malfunction if the
1059 // velocity becomes a denormalized float
1060 if (VectorLength2(ent->fields.server->velocity) < 0.0001)
1061 VectorClear(ent->fields.server->velocity);
1063 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1064 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1065 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1067 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1068 ent->fields.server->velocity[0] *= wishspeed;
1069 ent->fields.server->velocity[1] *= wishspeed;
1070 ent->fields.server->velocity[2] *= wishspeed;
1078 Runs thinking code if time. There is some play in the exact time the think
1079 function will be called, because it is called before any movement is done
1080 in a frame. Not used for pushmove objects, because they must be exact.
1081 Returns false if the entity removed itself.
1084 qboolean SV_RunThink (prvm_edict_t *ent)
1088 // don't let things stay in the past.
1089 // it is possible to start that way by a trigger with a local time.
1090 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1093 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1095 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1096 ent->fields.server->nextthink = 0;
1097 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1098 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1099 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1100 // mods often set nextthink to time to cause a think every frame,
1101 // we don't want to loop in that case, so exit if the new nextthink is
1102 // <= the time the qc was told, also exit if it is past the end of the
1104 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1107 return !ent->priv.server->free;
1114 Two entities have touched, so run their touch functions
1115 returns true if the impact kept the origin of the touching entity intact
1118 extern void VM_SetTraceGlobals(const trace_t *trace);
1119 extern sizebuf_t vm_tempstringsbuf;
1120 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1122 int restorevm_tempstringsbuf_cursize;
1123 int old_self, old_other;
1125 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1128 old_self = prog->globals.server->self;
1129 old_other = prog->globals.server->other;
1130 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1132 VectorCopy(e1->fields.server->origin, org);
1134 VM_SetTraceGlobals(trace);
1136 prog->globals.server->time = sv.time;
1137 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1139 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1140 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1141 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1144 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1146 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1147 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1148 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1149 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1150 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1151 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1152 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1154 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1156 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1158 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1160 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1163 prog->globals.server->self = old_self;
1164 prog->globals.server->other = old_other;
1165 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1167 return VectorCompare(e1->fields.server->origin, org);
1175 Slide off of the impacting object
1176 returns the blocked flags (1 = floor, 2 = step / wall)
1179 #define STOP_EPSILON 0.1
1180 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1185 backoff = -DotProduct (in, normal) * overbounce;
1186 VectorMA(in, backoff, normal, out);
1188 for (i = 0;i < 3;i++)
1189 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1198 The basic solid body movement clip that slides along multiple planes
1199 Returns the clipflags if the velocity was modified (hit something solid)
1203 8 = teleported by touch method
1204 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1207 static float SV_Gravity (prvm_edict_t *ent);
1208 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1209 #define MAX_CLIP_PLANES 5
1210 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
1212 int blocked, bumpcount;
1213 int i, j, numplanes;
1214 float d, time_left, gravity;
1215 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1225 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1227 gravity = SV_Gravity(ent) * 0.5f;
1228 ent->fields.server->velocity[2] -= gravity;
1232 applygravity = false;
1233 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1237 VectorCopy(ent->fields.server->velocity, original_velocity);
1238 VectorCopy(ent->fields.server->velocity, primal_velocity);
1241 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1243 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1246 VectorScale(ent->fields.server->velocity, time_left, push);
1248 VectorAdd(ent->fields.server->origin, push, end);
1250 if(!SV_PushEntity(&trace, ent, push, false, false))
1252 // we got teleported by a touch function
1253 // let's abort the move
1259 //if (trace.fraction < 0.002)
1264 VectorCopy(ent->fields.server->origin, start);
1265 start[2] += 3;//0.03125;
1266 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1267 end[2] += 3;//0.03125;
1268 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1269 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)))
1271 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
1277 for (i = 0;i < numplanes;i++)
1279 VectorCopy(ent->fields.server->origin, start);
1280 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1281 VectorMA(start, 3, planes[i], start);
1282 VectorMA(end, 3, planes[i], end);
1283 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1284 if (trace.fraction < testtrace.fraction)
1287 VectorCopy(start, ent->fields.server->origin);
1292 // VectorAdd(ent->fields.server->origin, planes[j], start);
1298 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);
1299 if (trace.fraction < 1)
1300 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
1305 if (trace.bmodelstartsolid)
1307 // LordHavoc: note: this code is what makes entities stick in place
1308 // if embedded in world only (you can walk through other objects if
1310 // entity is trapped in another solid
1311 VectorClear(ent->fields.server->velocity);
1316 if (trace.fraction == 1)
1318 if (trace.plane.normal[2])
1320 if (trace.plane.normal[2] > 0.7)
1327 Con_Printf ("SV_FlyMove: !trace.ent");
1328 trace.ent = prog->edicts;
1331 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1332 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1339 // save the trace for player extrafriction
1341 VectorCopy(trace.plane.normal, stepnormal);
1343 if (trace.fraction >= 0.001)
1345 // actually covered some distance
1346 VectorCopy(ent->fields.server->velocity, original_velocity);
1350 time_left *= 1 - trace.fraction;
1352 // clipped to another plane
1353 if (numplanes >= MAX_CLIP_PLANES)
1355 // this shouldn't really happen
1356 VectorClear(ent->fields.server->velocity);
1362 for (i = 0;i < numplanes;i++)
1363 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1367 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1372 VectorCopy(trace.plane.normal, planes[numplanes]);
1375 if (sv_newflymove.integer)
1376 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1379 // modify original_velocity so it parallels all of the clip planes
1380 for (i = 0;i < numplanes;i++)
1382 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1383 for (j = 0;j < numplanes;j++)
1388 if (DotProduct(new_velocity, planes[j]) < 0)
1398 // go along this plane
1399 VectorCopy(new_velocity, ent->fields.server->velocity);
1403 // go along the crease
1406 VectorClear(ent->fields.server->velocity);
1410 CrossProduct(planes[0], planes[1], dir);
1411 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1412 VectorNormalize(dir);
1413 d = DotProduct(dir, ent->fields.server->velocity);
1414 VectorScale(dir, d, ent->fields.server->velocity);
1418 // if current velocity is against the original velocity,
1419 // stop dead to avoid tiny occilations in sloping corners
1420 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1422 VectorClear(ent->fields.server->velocity);
1427 //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]);
1430 if ((blocked & 1) == 0 && bumpcount > 1)
1432 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1433 // flag ONGROUND if there's ground under it
1434 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1438 // LordHavoc: this came from QW and allows you to get out of water more easily
1439 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1440 VectorCopy(primal_velocity, ent->fields.server->velocity);
1441 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1442 ent->fields.server->velocity[2] -= gravity;
1452 static float SV_Gravity (prvm_edict_t *ent)
1457 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1458 if (val!=0 && val->_float)
1459 ent_gravity = val->_float;
1462 return ent_gravity * sv_gravity.value * sv.frametime;
1467 ===============================================================================
1471 ===============================================================================
1478 Does not change the entities velocity at all
1479 The trace struct is filled with the trace that has been done.
1480 Returns true if the push did not result in the entity being teleported by QC code.
1483 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1490 VectorCopy(ent->fields.server->origin, original);
1491 VectorAdd (ent->fields.server->origin, push, end);
1493 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1494 type = MOVE_MISSILE;
1495 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1496 type = MOVE_NOMONSTERS; // only clip against bmodels
1500 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1502 while (trace->startsolid && sv_gameplayfix_nudgeoutofsolid.integer)
1504 vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
1505 VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
1506 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1510 VectorCopy(original, ent->fields.server->origin);
1514 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1518 VectorCopy (trace->endpos, ent->fields.server->origin);
1522 if(!trace->startsolid)
1523 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)
1525 Con_Printf("something eeeeevil happened\n");
1530 SV_LinkEdict_TouchAreaGrid(ent);
1532 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))))
1533 return SV_Impact (ent, trace);
1545 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1548 int pusherowner, pusherprog;
1551 float savesolid, movetime2, pushltime;
1552 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1554 int numcheckentities;
1555 static prvm_edict_t *checkentities[MAX_EDICTS];
1556 dp_model_t *pushermodel;
1557 trace_t trace, trace2;
1558 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1559 static unsigned short moved_edicts[MAX_EDICTS];
1561 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])
1563 pusher->fields.server->ltime += movetime;
1567 switch ((int) pusher->fields.server->solid)
1569 // LordHavoc: valid pusher types
1572 case SOLID_SLIDEBOX:
1573 case SOLID_CORPSE: // LordHavoc: this would be weird...
1575 // LordHavoc: no collisions
1578 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1579 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1580 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1581 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1582 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1583 pusher->fields.server->ltime += movetime;
1584 SV_LinkEdict(pusher);
1587 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1590 index = (int) pusher->fields.server->modelindex;
1591 if (index < 1 || index >= MAX_MODELS)
1593 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1596 pushermodel = SV_GetModelByIndex(index);
1597 pusherowner = pusher->fields.server->owner;
1598 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1600 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1602 movetime2 = movetime;
1603 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1604 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1605 if (moveangle[0] || moveangle[2])
1607 for (i = 0;i < 3;i++)
1611 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1612 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1616 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1617 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1621 else if (moveangle[1])
1623 for (i = 0;i < 3;i++)
1627 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1628 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1632 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1633 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1639 for (i = 0;i < 3;i++)
1643 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1644 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1648 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1649 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1654 VectorNegate (moveangle, a);
1655 AngleVectorsFLU (a, forward, left, up);
1657 VectorCopy (pusher->fields.server->origin, pushorig);
1658 VectorCopy (pusher->fields.server->angles, pushang);
1659 pushltime = pusher->fields.server->ltime;
1661 // move the pusher to its final position
1663 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1664 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1665 pusher->fields.server->ltime += movetime;
1666 SV_LinkEdict(pusher);
1668 pushermodel = SV_GetModelFromEdict(pusher);
1669 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);
1670 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1672 savesolid = pusher->fields.server->solid;
1674 // see if any solid entities are inside the final position
1677 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1678 for (e = 0;e < numcheckentities;e++)
1680 prvm_edict_t *check = checkentities[e];
1681 int movetype = (int)check->fields.server->movetype;
1686 case MOVETYPE_FOLLOW:
1687 case MOVETYPE_NOCLIP:
1688 case MOVETYPE_FAKEPUSH:
1694 if (check->fields.server->owner == pusherprog)
1697 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1700 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1702 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1703 check->priv.server->waterposition_forceupdate = true;
1705 checkcontents = SV_GenericHitSuperContentsMask(check);
1707 // if the entity is standing on the pusher, it will definitely be moved
1708 // if the entity is not standing on the pusher, but is in the pusher's
1709 // final position, move it
1710 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1712 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);
1713 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1714 if (!trace.startsolid)
1716 //Con_Printf("- not in solid\n");
1724 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1725 org2[0] = DotProduct (org, forward);
1726 org2[1] = DotProduct (org, left);
1727 org2[2] = DotProduct (org, up);
1728 VectorSubtract (org2, org, move);
1729 VectorAdd (move, move1, move);
1732 VectorCopy (move1, move);
1734 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1736 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1737 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1738 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1740 // physics objects need better collisions than this code can do
1741 if (movetype == MOVETYPE_PHYSICS)
1743 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1744 SV_LinkEdict(check);
1745 SV_LinkEdict_TouchAreaGrid(check);
1749 // try moving the contacted entity
1750 pusher->fields.server->solid = SOLID_NOT;
1751 if(!SV_PushEntity (&trace, check, move, true, true))
1753 // entity "check" got teleported
1754 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1755 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1756 continue; // pushed enough
1758 // FIXME: turn players specially
1759 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1760 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1761 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1763 // this trace.fraction < 1 check causes items to fall off of pushers
1764 // if they pass under or through a wall
1765 // the groundentity check causes items to fall off of ledges
1766 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1767 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1769 // if it is still inside the pusher, block
1770 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);
1771 if (trace.startsolid)
1773 // try moving the contacted entity a tiny bit further to account for precision errors
1775 pusher->fields.server->solid = SOLID_NOT;
1776 VectorScale(move, 1.1, move2);
1777 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1778 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1779 if(!SV_PushEntity (&trace2, check, move2, true, true))
1781 // entity "check" got teleported
1784 pusher->fields.server->solid = savesolid;
1785 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);
1786 if (trace.startsolid)
1788 // try moving the contacted entity a tiny bit less to account for precision errors
1789 pusher->fields.server->solid = SOLID_NOT;
1790 VectorScale(move, 0.9, move2);
1791 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1792 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1793 if(!SV_PushEntity (&trace2, check, move2, true, true))
1795 // entity "check" got teleported
1798 pusher->fields.server->solid = savesolid;
1799 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);
1800 if (trace.startsolid)
1802 // still inside pusher, so it's really blocked
1805 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1807 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1810 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1811 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1815 VectorCopy (pushorig, pusher->fields.server->origin);
1816 VectorCopy (pushang, pusher->fields.server->angles);
1817 pusher->fields.server->ltime = pushltime;
1818 SV_LinkEdict(pusher);
1820 // move back any entities we already moved
1821 for (i = 0;i < num_moved;i++)
1823 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1824 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1825 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1829 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1830 if (pusher->fields.server->blocked)
1832 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1833 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1834 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1841 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1842 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1843 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1852 void SV_Physics_Pusher (prvm_edict_t *ent)
1854 float thinktime, oldltime, movetime;
1856 oldltime = ent->fields.server->ltime;
1858 thinktime = ent->fields.server->nextthink;
1859 if (thinktime < ent->fields.server->ltime + sv.frametime)
1861 movetime = thinktime - ent->fields.server->ltime;
1866 movetime = sv.frametime;
1869 // advances ent->fields.server->ltime if not blocked
1870 SV_PushMove (ent, movetime);
1872 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1874 ent->fields.server->nextthink = 0;
1875 prog->globals.server->time = sv.time;
1876 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1877 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1878 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1884 ===============================================================================
1888 ===============================================================================
1891 static float unstickoffsets[] =
1893 // poutting -/+z changes first as they are least weird
1908 typedef enum unstickresult_e
1916 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1920 // if not stuck in a bmodel, just return
1921 if (!SV_TestEntityPosition(ent, vec3_origin))
1922 return UNSTICK_GOOD;
1924 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1926 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1928 VectorCopy(unstickoffsets + i, offset);
1930 //SV_LinkEdict_TouchAreaGrid(ent);
1931 return UNSTICK_UNSTUCK;
1935 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1936 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1938 for(i = 2; i <= maxunstick; ++i)
1940 VectorClear(offset);
1942 if (!SV_TestEntityPosition(ent, offset))
1945 //SV_LinkEdict_TouchAreaGrid(ent);
1946 return UNSTICK_UNSTUCK;
1949 if (!SV_TestEntityPosition(ent, offset))
1952 //SV_LinkEdict_TouchAreaGrid(ent);
1953 return UNSTICK_UNSTUCK;
1957 return UNSTICK_STUCK;
1960 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1963 switch(SV_UnstickEntityReturnOffset(ent, offset))
1967 case UNSTICK_UNSTUCK:
1968 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]);
1971 if (developer.integer >= 100)
1972 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1975 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1984 This is a big hack to try and fix the rare case of getting stuck in the world
1988 void SV_CheckStuck (prvm_edict_t *ent)
1992 switch(SV_UnstickEntityReturnOffset(ent, offset))
1995 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1997 case UNSTICK_UNSTUCK:
1998 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]);
2001 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
2002 if (!SV_TestEntityPosition(ent, offset))
2004 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2006 //SV_LinkEdict_TouchAreaGrid(ent);
2009 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2012 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2022 qboolean SV_CheckWater (prvm_edict_t *ent)
2025 int nNativeContents;
2028 point[0] = ent->fields.server->origin[0];
2029 point[1] = ent->fields.server->origin[1];
2030 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2032 // DRESK - Support for Entity Contents Transition Event
2033 // NOTE: Some logic needed to be slightly re-ordered
2034 // to not affect performance and allow for the feature.
2036 // Acquire Super Contents Prior to Resets
2037 cont = SV_PointSuperContents(point);
2038 // Acquire Native Contents Here
2039 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2041 // DRESK - Support for Entity Contents Transition Event
2042 if(ent->fields.server->watertype)
2043 // Entity did NOT Spawn; Check
2044 SV_CheckContentsTransition(ent, nNativeContents);
2047 ent->fields.server->waterlevel = 0;
2048 ent->fields.server->watertype = CONTENTS_EMPTY;
2049 cont = SV_PointSuperContents(point);
2050 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2052 ent->fields.server->watertype = nNativeContents;
2053 ent->fields.server->waterlevel = 1;
2054 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2055 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2057 ent->fields.server->waterlevel = 2;
2058 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2059 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2060 ent->fields.server->waterlevel = 3;
2064 return ent->fields.server->waterlevel > 1;
2073 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2076 vec3_t forward, into, side;
2078 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2079 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2081 // cut the tangential velocity
2082 i = DotProduct (stepnormal, ent->fields.server->velocity);
2083 VectorScale (stepnormal, i, into);
2084 VectorSubtract (ent->fields.server->velocity, into, side);
2085 ent->fields.server->velocity[0] = side[0] * (1 + d);
2086 ent->fields.server->velocity[1] = side[1] * (1 + d);
2092 =====================
2095 Player has come to a dead stop, possibly due to the problem with limited
2096 float precision at some angle joins in the BSP hull.
2098 Try fixing by pushing one pixel in each direction.
2100 This is a hack, but in the interest of good gameplay...
2101 ======================
2103 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2108 VectorCopy (ent->fields.server->origin, oldorg);
2111 for (i=0 ; i<8 ; i++)
2113 // try pushing a little in an axial direction
2116 case 0: dir[0] = 2; dir[1] = 0; break;
2117 case 1: dir[0] = 0; dir[1] = 2; break;
2118 case 2: dir[0] = -2; dir[1] = 0; break;
2119 case 3: dir[0] = 0; dir[1] = -2; break;
2120 case 4: dir[0] = 2; dir[1] = 2; break;
2121 case 5: dir[0] = -2; dir[1] = 2; break;
2122 case 6: dir[0] = 2; dir[1] = -2; break;
2123 case 7: dir[0] = -2; dir[1] = -2; break;
2126 SV_PushEntity (&trace, ent, dir, false, true);
2128 // retry the original move
2129 ent->fields.server->velocity[0] = oldvel[0];
2130 ent->fields.server->velocity[1] = oldvel[1];
2131 ent->fields.server->velocity[2] = 0;
2132 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2134 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2135 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2137 Con_DPrint("TryUnstick - success.\n");
2141 // go back to the original pos and try again
2142 VectorCopy (oldorg, ent->fields.server->origin);
2146 VectorClear (ent->fields.server->velocity);
2147 Con_DPrint("TryUnstick - failure.\n");
2153 =====================
2156 Only used by players
2157 ======================
2159 void SV_WalkMove (prvm_edict_t *ent)
2161 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask, type;
2162 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2163 trace_t downtrace, trace;
2164 qboolean applygravity;
2166 // if frametime is 0 (due to client sending the same timestamp twice),
2168 if (sv.frametime <= 0)
2171 SV_CheckStuck (ent);
2173 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2175 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2177 SV_CheckVelocity(ent);
2179 // do a regular slide move unless it looks like you ran into a step
2180 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2182 VectorCopy (ent->fields.server->origin, start_origin);
2183 VectorCopy (ent->fields.server->velocity, start_velocity);
2185 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2187 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2190 // only try this if there was no floor in the way in the trace (no,
2191 // this check seems to be not REALLY necessary, because if clip & 1,
2192 // our trace will hit that thing too)
2193 VectorSet(upmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + 1);
2194 VectorSet(downmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
2195 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
2196 type = MOVE_MISSILE;
2197 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
2198 type = MOVE_NOMONSTERS; // only clip against bmodels
2201 trace = SV_TraceBox(upmove, ent->fields.server->mins, ent->fields.server->maxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2202 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2203 clip |= 1; // but we HAVE found a floor
2206 // if the move did not hit the ground at any point, we're not on ground
2208 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2210 SV_CheckVelocity(ent);
2212 SV_LinkEdict_TouchAreaGrid(ent);
2214 if(clip & 8) // teleport
2217 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2220 if (sv_nostep.integer)
2223 VectorCopy(ent->fields.server->origin, originalmove_origin);
2224 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2225 originalmove_clip = clip;
2226 originalmove_flags = (int)ent->fields.server->flags;
2227 originalmove_groundentity = ent->fields.server->groundentity;
2229 // if move didn't block on a step, return
2232 // if move was not trying to move into the step, return
2233 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2236 if (ent->fields.server->movetype != MOVETYPE_FLY)
2238 // return if gibbed by a trigger
2239 if (ent->fields.server->movetype != MOVETYPE_WALK)
2242 // only step up while jumping if that is enabled
2243 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2244 if (!oldonground && ent->fields.server->waterlevel == 0)
2248 // try moving up and forward to go up a step
2249 // back to start pos
2250 VectorCopy (start_origin, ent->fields.server->origin);
2251 VectorCopy (start_velocity, ent->fields.server->velocity);
2254 VectorClear (upmove);
2255 upmove[2] = sv_stepheight.value;
2256 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2258 // we got teleported when upstepping... must abort the move
2263 ent->fields.server->velocity[2] = 0;
2264 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2265 ent->fields.server->velocity[2] += start_velocity[2];
2268 // we got teleported when upstepping... must abort the move
2269 // note that z velocity handling may not be what QC expects here, but we cannot help it
2273 SV_CheckVelocity(ent);
2275 SV_LinkEdict_TouchAreaGrid(ent);
2277 // check for stuckness, possibly due to the limited precision of floats
2278 // in the clipping hulls
2280 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2281 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2283 //Con_Printf("wall\n");
2284 // stepping up didn't make any progress, revert to original move
2285 VectorCopy(originalmove_origin, ent->fields.server->origin);
2286 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2287 //clip = originalmove_clip;
2288 ent->fields.server->flags = originalmove_flags;
2289 ent->fields.server->groundentity = originalmove_groundentity;
2290 // now try to unstick if needed
2291 //clip = SV_TryUnstick (ent, oldvel);
2295 //Con_Printf("step - ");
2297 // extra friction based on view angle
2298 if (clip & 2 && sv_wallfriction.integer)
2299 SV_WallFriction (ent, stepnormal);
2301 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2302 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))
2306 VectorClear (downmove);
2307 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2308 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2310 // we got teleported when downstepping... must abort the move
2314 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2316 // this has been disabled so that you can't jump when you are stepping
2317 // up while already jumping (also known as the Quake2 double jump bug)
2319 // LordHavoc: disabled this check so you can walk on monsters/players
2320 //if (ent->fields.server->solid == SOLID_BSP)
2322 //Con_Printf("onground\n");
2323 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2324 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2330 //Con_Printf("slope\n");
2331 // if the push down didn't end up on good ground, use the move without
2332 // the step up. This happens near wall / slope combinations, and can
2333 // cause the player to hop up higher on a slope too steep to climb
2334 VectorCopy(originalmove_origin, ent->fields.server->origin);
2335 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2336 //clip = originalmove_clip;
2337 ent->fields.server->flags = originalmove_flags;
2338 ent->fields.server->groundentity = originalmove_groundentity;
2341 SV_CheckVelocity(ent);
2343 SV_LinkEdict_TouchAreaGrid(ent);
2346 //============================================================================
2352 Entities that are "stuck" to another entity
2355 void SV_Physics_Follow (prvm_edict_t *ent)
2357 vec3_t vf, vr, vu, angles, v;
2361 if (!SV_RunThink (ent))
2364 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2365 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2366 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])
2368 // quick case for no rotation
2369 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2373 angles[0] = -ent->fields.server->punchangle[0];
2374 angles[1] = ent->fields.server->punchangle[1];
2375 angles[2] = ent->fields.server->punchangle[2];
2376 AngleVectors (angles, vf, vr, vu);
2377 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];
2378 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];
2379 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];
2380 angles[0] = -e->fields.server->angles[0];
2381 angles[1] = e->fields.server->angles[1];
2382 angles[2] = e->fields.server->angles[2];
2383 AngleVectors (angles, vf, vr, vu);
2384 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2385 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2386 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2388 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2390 //SV_LinkEdict_TouchAreaGrid(ent);
2394 ==============================================================================
2398 ==============================================================================
2403 SV_CheckWaterTransition
2407 void SV_CheckWaterTransition (prvm_edict_t *ent)
2410 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2411 if (!ent->fields.server->watertype)
2413 // just spawned here
2414 ent->fields.server->watertype = cont;
2415 ent->fields.server->waterlevel = 1;
2419 // DRESK - Support for Entity Contents Transition Event
2420 // NOTE: Call here BEFORE updating the watertype below,
2421 // and suppress watersplash sound if a valid function
2422 // call was made to allow for custom "splash" sounds.
2423 if( !SV_CheckContentsTransition(ent, cont) )
2424 { // Contents Transition Function Invalid; Potentially Play Water Sound
2425 // check if the entity crossed into or out of water
2426 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2427 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2430 if (cont <= CONTENTS_WATER)
2432 ent->fields.server->watertype = cont;
2433 ent->fields.server->waterlevel = 1;
2437 ent->fields.server->watertype = CONTENTS_EMPTY;
2438 ent->fields.server->waterlevel = 0;
2446 Toss, bounce, and fly movement. When onground, do nothing.
2449 void SV_Physics_Toss (prvm_edict_t *ent)
2455 prvm_edict_t *groundentity;
2457 // if onground, return without moving
2458 if ((int)ent->fields.server->flags & FL_ONGROUND)
2460 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2461 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2463 // don't stick to ground if onground and moving upward
2464 ent->fields.server->flags -= FL_ONGROUND;
2466 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2468 // we can trust FL_ONGROUND if groundentity is world because it never moves
2471 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2473 // if ent was supported by a brush model on previous frame,
2474 // and groundentity is now freed, set groundentity to 0 (world)
2475 // which leaves it suspended in the air
2476 ent->fields.server->groundentity = 0;
2477 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2480 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2482 // don't slide if still touching the groundentity
2486 ent->priv.server->suspendedinairflag = false;
2488 SV_CheckVelocity (ent);
2491 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2492 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2495 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2497 movetime = sv.frametime;
2498 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2501 VectorScale (ent->fields.server->velocity, movetime, move);
2502 if(!SV_PushEntity (&trace, ent, move, true, true))
2503 return; // teleported
2504 if (ent->priv.server->free)
2506 if (trace.bmodelstartsolid)
2508 // try to unstick the entity
2509 SV_UnstickEntity(ent);
2510 if(!SV_PushEntity (&trace, ent, move, false, true))
2511 return; // teleported
2512 if (ent->priv.server->free)
2515 if (trace.fraction == 1)
2517 movetime *= 1 - min(1, trace.fraction);
2518 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2521 float bouncefactor = 1.0f;
2522 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2523 if (val!=0 && val->_float)
2524 bouncefactor = val->_float;
2526 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2527 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2529 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2531 float d, ent_gravity;
2533 float bouncefactor = 0.5f;
2534 float bouncestop = 60.0f / 800.0f;
2536 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2537 if (val!=0 && val->_float)
2538 bouncefactor = val->_float;
2540 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2541 if (val!=0 && val->_float)
2542 bouncestop = val->_float;
2544 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2545 // LordHavoc: fixed grenades not bouncing when fired down a slope
2546 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2547 if (val!=0 && val->_float)
2548 ent_gravity = val->_float;
2551 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2553 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2554 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2556 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2557 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2558 VectorClear (ent->fields.server->velocity);
2559 VectorClear (ent->fields.server->avelocity);
2562 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2566 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2568 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2569 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2570 VectorClear (ent->fields.server->velocity);
2571 VectorClear (ent->fields.server->avelocity);
2574 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2579 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2580 if (trace.plane.normal[2] > 0.7)
2582 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2583 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2584 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2585 ent->priv.server->suspendedinairflag = true;
2586 VectorClear (ent->fields.server->velocity);
2587 VectorClear (ent->fields.server->avelocity);
2590 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2592 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2596 // check for in water
2597 SV_CheckWaterTransition (ent);
2601 ===============================================================================
2605 ===============================================================================
2612 Monsters freefall when they don't have a ground entity, otherwise
2613 all movement is done with discrete steps.
2615 This is also used for objects that have become still on the ground, but
2616 will fall if the floor is pulled out from under them.
2619 void SV_Physics_Step (prvm_edict_t *ent)
2621 int flags = (int)ent->fields.server->flags;
2624 // Backup Velocity in the event that movetypesteplandevent is called,
2625 // to provide a parameter with the entity's velocity at impact.
2626 prvm_eval_t *movetypesteplandevent;
2627 vec3_t backupVelocity;
2628 VectorCopy(ent->fields.server->velocity, backupVelocity);
2629 // don't fall at all if fly/swim
2630 if (!(flags & (FL_FLY | FL_SWIM)))
2632 if (flags & FL_ONGROUND)
2634 // freefall if onground and moving upward
2635 // freefall if not standing on a world surface (it may be a lift or trap door)
2636 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2638 ent->fields.server->flags -= FL_ONGROUND;
2639 SV_CheckVelocity(ent);
2640 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2642 SV_LinkEdict_TouchAreaGrid(ent);
2643 ent->priv.server->waterposition_forceupdate = true;
2648 // freefall if not onground
2649 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2651 SV_CheckVelocity(ent);
2652 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2654 SV_LinkEdict_TouchAreaGrid(ent);
2657 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2659 // DRESK - Check for Entity Land Event Function
2660 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2662 if(movetypesteplandevent->function)
2663 { // Valid Function; Execute
2664 // Prepare Parameters
2665 // Assign Velocity at Impact
2666 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2667 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2668 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2670 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2671 // Execute VM Function
2672 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2675 // Check for Engine Landing Sound
2676 if(sv_sound_land.string)
2677 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2679 ent->priv.server->waterposition_forceupdate = true;
2684 if (!SV_RunThink(ent))
2687 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2689 ent->priv.server->waterposition_forceupdate = false;
2690 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2691 SV_CheckWaterTransition(ent);
2695 //============================================================================
2697 static void SV_Physics_Entity (prvm_edict_t *ent)
2699 // don't run think/move on newly spawned projectiles as it messes up
2700 // movement interpolation and rocket trails, and is inconsistent with
2701 // respect to entities spawned in the same frame
2702 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2703 // but if it spawns a lower numbered ent, it doesn't - this never moves
2704 // ents in the first frame regardless)
2705 qboolean runmove = ent->priv.server->move;
2706 ent->priv.server->move = true;
2707 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2709 switch ((int) ent->fields.server->movetype)
2712 case MOVETYPE_FAKEPUSH:
2713 SV_Physics_Pusher (ent);
2716 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2717 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2720 case MOVETYPE_FOLLOW:
2721 SV_Physics_Follow (ent);
2723 case MOVETYPE_NOCLIP:
2724 if (SV_RunThink(ent))
2727 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2728 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2733 SV_Physics_Step (ent);
2736 if (SV_RunThink (ent))
2740 case MOVETYPE_BOUNCE:
2741 case MOVETYPE_BOUNCEMISSILE:
2742 case MOVETYPE_FLYMISSILE:
2745 if (SV_RunThink (ent))
2746 SV_Physics_Toss (ent);
2748 case MOVETYPE_PHYSICS:
2749 if (SV_RunThink(ent))
2752 SV_LinkEdict_TouchAreaGrid(ent);
2756 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2761 void SV_Physics_ClientMove(void)
2764 ent = host_client->edict;
2766 // call player physics, this needs the proper frametime
2767 prog->globals.server->frametime = sv.frametime;
2770 // call standard client pre-think, with frametime = 0
2771 prog->globals.server->time = sv.time;
2772 prog->globals.server->frametime = 0;
2773 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2774 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2775 prog->globals.server->frametime = sv.frametime;
2777 // make sure the velocity is sane (not a NaN)
2778 SV_CheckVelocity(ent);
2780 // perform MOVETYPE_WALK behavior
2783 // call standard player post-think, with frametime = 0
2784 prog->globals.server->time = sv.time;
2785 prog->globals.server->frametime = 0;
2786 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2787 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2788 prog->globals.server->frametime = sv.frametime;
2790 if(ent->fields.server->fixangle)
2792 // angle fixing was requested by physics code...
2793 // so store the current angles for later use
2794 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2795 host_client->fixangle_angles_set = TRUE;
2797 // and clear fixangle for the next frame
2798 ent->fields.server->fixangle = 0;
2802 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2804 // don't do physics on disconnected clients, FrikBot relies on this
2805 if (!host_client->spawned)
2808 // make sure the velocity is sane (not a NaN)
2809 SV_CheckVelocity(ent);
2811 // don't run physics here if running asynchronously
2812 if (host_client->clmovement_inputtimeout <= 0)
2815 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2818 // make sure the velocity is still sane (not a NaN)
2819 SV_CheckVelocity(ent);
2821 // call standard client pre-think
2822 prog->globals.server->time = sv.time;
2823 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2824 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2826 // make sure the velocity is still sane (not a NaN)
2827 SV_CheckVelocity(ent);
2830 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2832 // don't do physics on disconnected clients, FrikBot relies on this
2833 if (!host_client->spawned)
2836 // make sure the velocity is sane (not a NaN)
2837 SV_CheckVelocity(ent);
2839 // call standard player post-think
2840 prog->globals.server->time = sv.time;
2841 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2842 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2844 // make sure the velocity is still sane (not a NaN)
2845 SV_CheckVelocity(ent);
2847 if(ent->fields.server->fixangle)
2849 // angle fixing was requested by physics code...
2850 // so store the current angles for later use
2851 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2852 host_client->fixangle_angles_set = TRUE;
2854 // and clear fixangle for the next frame
2855 ent->fields.server->fixangle = 0;
2858 // decrement the countdown variable used to decide when to go back to
2859 // synchronous physics
2860 if (host_client->clmovement_inputtimeout > sv.frametime)
2861 host_client->clmovement_inputtimeout -= sv.frametime;
2863 host_client->clmovement_inputtimeout = 0;
2866 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2868 // don't do physics on disconnected clients, FrikBot relies on this
2869 if (!host_client->spawned)
2871 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2875 // make sure the velocity is sane (not a NaN)
2876 SV_CheckVelocity(ent);
2878 switch ((int) ent->fields.server->movetype)
2881 case MOVETYPE_FAKEPUSH:
2882 SV_Physics_Pusher (ent);
2885 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2886 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2889 case MOVETYPE_FOLLOW:
2890 SV_Physics_Follow (ent);
2892 case MOVETYPE_NOCLIP:
2895 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2896 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2899 SV_Physics_Step (ent);
2903 // don't run physics here if running asynchronously
2904 if (host_client->clmovement_inputtimeout <= 0)
2908 case MOVETYPE_BOUNCE:
2909 case MOVETYPE_BOUNCEMISSILE:
2910 case MOVETYPE_FLYMISSILE:
2913 SV_Physics_Toss (ent);
2919 case MOVETYPE_PHYSICS:
2923 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2927 SV_CheckVelocity (ent);
2930 SV_LinkEdict_TouchAreaGrid(ent);
2932 SV_CheckVelocity (ent);
2941 void SV_Physics (void)
2946 // let the progs know that a new frame has started
2947 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2948 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2949 prog->globals.server->time = sv.time;
2950 prog->globals.server->frametime = sv.frametime;
2951 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2953 // run physics engine
2954 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2957 // treat each object in turn
2960 // if force_retouch, relink all the entities
2961 if (prog->globals.server->force_retouch > 0)
2962 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2963 if (!ent->priv.server->free)
2964 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2966 if (sv_gameplayfix_consistentplayerprethink.integer)
2968 // run physics on the client entities in 3 stages
2969 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2970 if (!ent->priv.server->free)
2971 SV_Physics_ClientEntity_PreThink(ent);
2973 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2974 if (!ent->priv.server->free)
2975 SV_Physics_ClientEntity(ent);
2977 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2978 if (!ent->priv.server->free)
2979 SV_Physics_ClientEntity_PostThink(ent);
2983 // run physics on the client entities
2984 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2986 if (!ent->priv.server->free)
2988 SV_Physics_ClientEntity_PreThink(ent);
2989 SV_Physics_ClientEntity(ent);
2990 SV_Physics_ClientEntity_PostThink(ent);
2995 // run physics on all the non-client entities
2996 if (!sv_freezenonclients.integer)
2998 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2999 if (!ent->priv.server->free)
3000 SV_Physics_Entity(ent);
3001 // make a second pass to see if any ents spawned this frame and make
3002 // sure they run their move/think
3003 if (sv_gameplayfix_delayprojectiles.integer < 0)
3004 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3005 if (!ent->priv.server->move && !ent->priv.server->free)
3006 SV_Physics_Entity(ent);
3009 if (prog->globals.server->force_retouch > 0)
3010 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
3012 // LordHavoc: endframe support
3013 if (prog->funcoffsets.EndFrame)
3015 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
3016 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
3017 prog->globals.server->time = sv.time;
3018 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
3021 // decrement prog->num_edicts if the highest number entities died
3022 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3024 if (!sv_freezenonclients.integer)
3025 sv.time += sv.frametime;