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.
23 #include "prvm_cmds.h"
28 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.
30 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
32 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
33 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
34 corpses are SOLID_NOT and MOVETYPE_TOSS
35 crates are SOLID_BBOX and MOVETYPE_TOSS
36 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
37 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
39 solid_edge items only clip against bsp models.
43 #define MOVE_EPSILON 0.01
45 void SV_Physics_Toss (prvm_edict_t *ent);
47 int SV_GetPitchSign(prvm_prog_t *prog, prvm_edict_t *ent)
51 (model = SV_GetModelFromEdict(ent))
53 model->type == mod_alias
56 (((unsigned char)PRVM_serveredictfloat(ent, pflags)) & PFLAGS_FULLDYNAMIC)
58 ((gamemode == GAME_TENEBRAE) && ((unsigned int)PRVM_serveredictfloat(ent, effects) & (16 | 32)))
66 ===============================================================================
70 ===============================================================================
73 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
75 prvm_prog_t *prog = SVVM_prog;
78 int dphitcontentsmask = (int)PRVM_serveredictfloat(passedict, dphitcontentsmask);
79 if (dphitcontentsmask)
80 return dphitcontentsmask;
81 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_SLIDEBOX)
83 if ((int)PRVM_serveredictfloat(passedict, flags) & FL_MONSTER)
84 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
86 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
88 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_CORPSE)
89 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
90 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_TRIGGER)
91 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
93 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
96 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
104 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
106 prvm_prog_t *prog = SVVM_prog;
107 int i, bodysupercontents;
110 prvm_edict_t *traceowner, *touch;
112 // bounding box of entire move area
113 vec3_t clipboxmins, clipboxmaxs;
114 // size when clipping against monsters
115 vec3_t clipmins2, clipmaxs2;
116 // start and end origin of move
120 // matrices to transform into/out of other entity's space
121 matrix4x4_t matrix, imatrix;
122 // model of other entity
124 // list of entities to test for collisions
126 static prvm_edict_t *touchedicts[MAX_EDICTS];
128 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
130 VectorCopy(start, clipstart);
131 VectorClear(clipmins2);
132 VectorClear(clipmaxs2);
133 #if COLLISIONPARANOID >= 3
134 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
138 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
139 cliptrace.bmodelstartsolid = cliptrace.startsolid;
140 if (cliptrace.startsolid || cliptrace.fraction < 1)
141 cliptrace.ent = prog->edicts;
142 if (type == MOVE_WORLDONLY)
145 if (type == MOVE_MISSILE)
147 // LordHavoc: modified this, was = -15, now -= 15
148 for (i = 0;i < 3;i++)
155 // create the bounding box of the entire move
156 for (i = 0;i < 3;i++)
158 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
159 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
162 // debug override to test against everything
163 if (sv_debugmove.integer)
165 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
166 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
169 // if the passedict is world, make it NULL (to avoid two checks each time)
170 if (passedict == prog->edicts)
172 // precalculate prog value for passedict for comparisons
173 passedictprog = PRVM_EDICT_TO_PROG(passedict);
174 // precalculate passedict's owner edict pointer for comparisons
175 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
178 // because this uses World_EntitiestoBox, we know all entity boxes overlap
179 // the clip region, so we can skip culling checks in the loop below
180 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
181 if (numtouchedicts > MAX_EDICTS)
183 // this never happens
184 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
185 numtouchedicts = MAX_EDICTS;
187 for (i = 0;i < numtouchedicts;i++)
189 touch = touchedicts[i];
191 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
193 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
198 // don't clip against self
199 if (passedict == touch)
201 // don't clip owned entities against owner
202 if (traceowner == touch)
204 // don't clip owner against owned entities
205 if (passedictprog == PRVM_serveredictedict(touch, owner))
207 // don't clip points against points (they can't collide)
208 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
212 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
214 // might interact, so do an exact clip
216 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
218 model = SV_GetModelFromEdict(touch);
219 pitchsign = SV_GetPitchSign(prog, touch);
222 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
224 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
225 Matrix4x4_Invert_Simple(&imatrix, &matrix);
226 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
227 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
228 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
229 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
230 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs), bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
232 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs), bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
234 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
246 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
247 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
249 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
252 prvm_prog_t *prog = SVVM_prog;
253 int i, bodysupercontents;
256 prvm_edict_t *traceowner, *touch;
258 // bounding box of entire move area
259 vec3_t clipboxmins, clipboxmaxs;
260 // size when clipping against monsters
261 vec3_t clipmins2, clipmaxs2;
262 // start and end origin of move
263 vec3_t clipstart, clipend;
266 // matrices to transform into/out of other entity's space
267 matrix4x4_t matrix, imatrix;
268 // model of other entity
270 // list of entities to test for collisions
272 static prvm_edict_t *touchedicts[MAX_EDICTS];
273 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
277 if (VectorCompare(start, pEnd))
278 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
280 if(collision_endposnudge.value > 0)
282 // TRICK: make the trace 1 qu longer!
283 VectorSubtract(pEnd, start, end);
284 len = VectorNormalizeLength(end);
285 VectorMA(pEnd, collision_endposnudge.value, end, end);
288 VectorCopy(pEnd, end);
290 if (VectorCompare(start, end))
291 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
294 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
296 VectorCopy(start, clipstart);
297 VectorCopy(end, clipend);
298 VectorClear(clipmins2);
299 VectorClear(clipmaxs2);
300 #if COLLISIONPARANOID >= 3
301 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
305 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, false);
306 cliptrace.bmodelstartsolid = cliptrace.startsolid;
307 if (cliptrace.startsolid || cliptrace.fraction < 1)
308 cliptrace.ent = prog->edicts;
309 if (type == MOVE_WORLDONLY)
312 if (type == MOVE_MISSILE)
314 // LordHavoc: modified this, was = -15, now -= 15
315 for (i = 0;i < 3;i++)
322 // create the bounding box of the entire move
323 for (i = 0;i < 3;i++)
325 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
326 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
329 // debug override to test against everything
330 if (sv_debugmove.integer)
332 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
333 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
336 // if the passedict is world, make it NULL (to avoid two checks each time)
337 if (passedict == prog->edicts)
339 // precalculate prog value for passedict for comparisons
340 passedictprog = PRVM_EDICT_TO_PROG(passedict);
341 // precalculate passedict's owner edict pointer for comparisons
342 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
345 // because this uses World_EntitiestoBox, we know all entity boxes overlap
346 // the clip region, so we can skip culling checks in the loop below
347 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
348 if (numtouchedicts > MAX_EDICTS)
350 // this never happens
351 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
352 numtouchedicts = MAX_EDICTS;
354 for (i = 0;i < numtouchedicts;i++)
356 touch = touchedicts[i];
358 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
360 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
365 // don't clip against self
366 if (passedict == touch)
368 // don't clip owned entities against owner
369 if (traceowner == touch)
371 // don't clip owner against owned entities
372 if (passedictprog == PRVM_serveredictedict(touch, owner))
374 // don't clip points against points (they can't collide)
375 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
379 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
381 // might interact, so do an exact clip
383 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
385 model = SV_GetModelFromEdict(touch);
386 pitchsign = SV_GetPitchSign(prog, touch);
389 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
391 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
392 Matrix4x4_Invert_Simple(&imatrix, &matrix);
393 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
394 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
395 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
396 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
397 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs), bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
399 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs), bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, false);
401 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
405 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
406 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
407 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
417 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
418 #if COLLISIONPARANOID >= 1
419 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
421 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)
424 #if COLLISIONPARANOID >= 1
425 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)
427 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)
431 prvm_prog_t *prog = SVVM_prog;
432 vec3_t hullmins, hullmaxs;
433 int i, bodysupercontents;
437 prvm_edict_t *traceowner, *touch;
439 // bounding box of entire move area
440 vec3_t clipboxmins, clipboxmaxs;
441 // size of the moving object
442 vec3_t clipmins, clipmaxs;
443 // size when clipping against monsters
444 vec3_t clipmins2, clipmaxs2;
445 // start and end origin of move
446 vec3_t clipstart, clipend;
449 // matrices to transform into/out of other entity's space
450 matrix4x4_t matrix, imatrix;
451 // model of other entity
453 // list of entities to test for collisions
455 static prvm_edict_t *touchedicts[MAX_EDICTS];
456 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
460 if (VectorCompare(mins, maxs))
462 vec3_t shiftstart, shiftend;
463 VectorAdd(start, mins, shiftstart);
464 VectorAdd(pEnd, mins, shiftend);
465 if (VectorCompare(start, pEnd))
466 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
468 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
469 VectorSubtract(trace.endpos, mins, trace.endpos);
473 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
475 // TRICK: make the trace 1 qu longer!
476 VectorSubtract(pEnd, start, end);
477 len = VectorNormalizeLength(end);
478 VectorMA(pEnd, collision_endposnudge.value, end, end);
481 VectorCopy(pEnd, end);
483 if (VectorCompare(mins, maxs))
485 vec3_t shiftstart, shiftend;
486 VectorAdd(start, mins, shiftstart);
487 VectorAdd(end, mins, shiftend);
488 if (VectorCompare(start, end))
489 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
491 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
492 VectorSubtract(trace.endpos, mins, trace.endpos);
497 VectorCopy(start, clipstart);
498 VectorCopy(end, clipend);
499 VectorCopy(mins, clipmins);
500 VectorCopy(maxs, clipmaxs);
501 VectorCopy(mins, clipmins2);
502 VectorCopy(maxs, clipmaxs2);
503 #if COLLISIONPARANOID >= 3
504 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
508 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
509 cliptrace.bmodelstartsolid = cliptrace.startsolid;
510 if (cliptrace.startsolid || cliptrace.fraction < 1)
511 cliptrace.ent = prog->edicts;
512 if (type == MOVE_WORLDONLY)
515 if (type == MOVE_MISSILE)
517 // LordHavoc: modified this, was = -15, now -= 15
518 for (i = 0;i < 3;i++)
525 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
526 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
527 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
530 VectorCopy(clipmins, hullmins);
531 VectorCopy(clipmaxs, hullmaxs);
534 // create the bounding box of the entire move
535 for (i = 0;i < 3;i++)
537 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
538 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
541 // debug override to test against everything
542 if (sv_debugmove.integer)
544 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
545 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
548 // if the passedict is world, make it NULL (to avoid two checks each time)
549 if (passedict == prog->edicts)
551 // precalculate prog value for passedict for comparisons
552 passedictprog = PRVM_EDICT_TO_PROG(passedict);
553 // figure out whether this is a point trace for comparisons
554 pointtrace = VectorCompare(clipmins, clipmaxs);
555 // precalculate passedict's owner edict pointer for comparisons
556 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
559 // because this uses World_EntitiestoBox, we know all entity boxes overlap
560 // the clip region, so we can skip culling checks in the loop below
561 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
562 if (numtouchedicts > MAX_EDICTS)
564 // this never happens
565 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
566 numtouchedicts = MAX_EDICTS;
568 for (i = 0;i < numtouchedicts;i++)
570 touch = touchedicts[i];
572 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
574 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
579 // don't clip against self
580 if (passedict == touch)
582 // don't clip owned entities against owner
583 if (traceowner == touch)
585 // don't clip owner against owned entities
586 if (passedictprog == PRVM_serveredictedict(touch, owner))
588 // don't clip points against points (they can't collide)
589 if (pointtrace && VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
593 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
595 // might interact, so do an exact clip
597 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
599 model = SV_GetModelFromEdict(touch);
600 pitchsign = SV_GetPitchSign(prog, touch);
603 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
605 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
606 Matrix4x4_Invert_Simple(&imatrix, &matrix);
607 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
608 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
609 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
610 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
611 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs), bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
613 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs), bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
615 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
619 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
620 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
621 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
626 #if COLLISIONPARANOID >= 1
627 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)
632 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
635 VectorCopy(trace.endpos, temp);
636 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
637 #if COLLISIONPARANOID < 3
638 if (trace.startsolid || endstuck)
640 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, PRVM_serveredictvector(passedict, origin)[0], PRVM_serveredictvector(passedict, origin)[1], PRVM_serveredictvector(passedict, origin)[2], end[0] - PRVM_serveredictvector(passedict, origin)[0], end[1] - PRVM_serveredictvector(passedict, origin)[1], end[2] - PRVM_serveredictvector(passedict, origin)[2], trace.fraction, trace.endpos[0] - PRVM_serveredictvector(passedict, origin)[0], trace.endpos[1] - PRVM_serveredictvector(passedict, origin)[1], trace.endpos[2] - PRVM_serveredictvector(passedict, origin)[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
646 int SV_PointSuperContents(const vec3_t point)
648 prvm_prog_t *prog = SVVM_prog;
649 int supercontents = 0;
653 // matrices to transform into/out of other entity's space
654 matrix4x4_t matrix, imatrix;
655 // model of other entity
658 // list of entities to test for collisions
660 static prvm_edict_t *touchedicts[MAX_EDICTS];
662 // get world supercontents at this point
663 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
664 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
666 // if sv_gameplayfix_swiminbmodels is off we're done
667 if (!sv_gameplayfix_swiminbmodels.integer)
668 return supercontents;
670 // get list of entities at this point
671 numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
672 if (numtouchedicts > MAX_EDICTS)
674 // this never happens
675 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
676 numtouchedicts = MAX_EDICTS;
678 for (i = 0;i < numtouchedicts;i++)
680 touch = touchedicts[i];
682 // we only care about SOLID_BSP for pointcontents
683 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
686 // might interact, so do an exact clip
687 model = SV_GetModelFromEdict(touch);
688 if (!model || !model->PointSuperContents)
690 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
691 Matrix4x4_Invert_Simple(&imatrix, &matrix);
692 Matrix4x4_Transform(&imatrix, point, transformed);
693 frame = (int)PRVM_serveredictfloat(touch, frame);
694 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
697 return supercontents;
701 ===============================================================================
703 Linking entities into the world culling system
705 ===============================================================================
708 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
710 prvm_prog_t *prog = SVVM_prog;
711 vec3_t paddedmins, paddedmaxs;
712 if (maxedicts < 1 || resultedicts == NULL)
714 VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
715 VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
716 if (sv_areadebug.integer)
718 int numresultedicts = 0;
721 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
723 ed = PRVM_EDICT_NUM(edictindex);
724 if (!ed->priv.required->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
726 resultedicts[numresultedicts++] = ed;
727 if (numresultedicts == maxedicts)
731 return numresultedicts;
734 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
737 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
739 prvm_prog_t *prog = SVVM_prog;
740 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
741 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
742 PRVM_serverglobalfloat(time) = sv.time;
743 PRVM_serverglobalfloat(trace_allsolid) = false;
744 PRVM_serverglobalfloat(trace_startsolid) = false;
745 PRVM_serverglobalfloat(trace_fraction) = 1;
746 PRVM_serverglobalfloat(trace_inwater) = false;
747 PRVM_serverglobalfloat(trace_inopen) = true;
748 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
749 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
750 PRVM_serverglobalfloat(trace_plane_dist) = 0;
751 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
752 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
753 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
754 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
755 PRVM_serverglobalstring(trace_dphittexturename) = 0;
756 prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
759 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
761 prvm_prog_t *prog = SVVM_prog;
762 int i, numtouchedicts, old_self, old_other;
764 static prvm_edict_t *touchedicts[MAX_EDICTS];
766 if (ent == prog->edicts)
767 return; // don't add the world
769 if (ent->priv.server->free)
772 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
775 // build a list of edicts to touch, because the link loop can be corrupted
776 // by IncreaseEdicts called during touch functions
777 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
778 if (numtouchedicts > MAX_EDICTS)
780 // this never happens
781 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
782 numtouchedicts = MAX_EDICTS;
785 old_self = PRVM_serverglobaledict(self);
786 old_other = PRVM_serverglobaledict(other);
787 for (i = 0;i < numtouchedicts;i++)
789 touch = touchedicts[i];
790 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
792 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
795 PRVM_serverglobaledict(self) = old_self;
796 PRVM_serverglobaledict(other) = old_other;
799 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
803 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
805 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
806 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
807 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
808 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];
809 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];
810 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
811 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];
812 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];
813 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
814 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];
815 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];
816 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
817 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];
818 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];
819 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
820 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];
821 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];
822 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
823 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];
824 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];
825 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
826 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];
827 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];
836 void SV_LinkEdict (prvm_edict_t *ent)
838 prvm_prog_t *prog = SVVM_prog;
843 if (ent == prog->edicts)
844 return; // don't add the world
846 if (ent->priv.server->free)
849 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
850 if (modelindex < 0 || modelindex >= MAX_MODELS)
852 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
855 model = SV_GetModelByIndex(modelindex);
857 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
858 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
859 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
863 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
865 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
866 // TODO special handling for spheres?
867 RotateBBox(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), PRVM_serveredictvector(ent, angles), mins, maxs);
868 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
869 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
871 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
875 if (!model->TraceBox)
876 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
878 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
880 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
881 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
883 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
885 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
886 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
890 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
891 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
896 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
897 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
898 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
903 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
904 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
908 // to make items easier to pick up and allow them to be grabbed off
909 // of shelves, the abs sizes are expanded
911 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
922 // because movement is clipped an epsilon away from an actual edge,
923 // we must fully check even when bounding boxes don't quite touch
932 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
933 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
935 World_LinkEdict(&sv.world, ent, mins, maxs);
939 ===============================================================================
943 ===============================================================================
948 SV_TestEntityPosition
950 returns true if the entity is in solid currently
953 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
955 prvm_prog_t *prog = SVVM_prog;
959 contents = SV_GenericHitSuperContentsMask(ent);
960 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
961 trace = SV_TraceBox(org, PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), PRVM_serveredictvector(ent, origin), ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, contents);
962 if (trace.startsupercontents & contents)
966 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
968 // q1bsp/hlbsp use hulls and if the entity does not exactly match
969 // a hull size it is incorrectly tested, so this code tries to
970 // 'fix' it slightly...
971 // FIXME: this breaks entities larger than the hull size
974 VectorAdd(org, PRVM_serveredictvector(ent, mins), m1);
975 VectorAdd(org, PRVM_serveredictvector(ent, maxs), m2);
976 VectorSubtract(m2, m1, s);
977 #define EPSILON (1.0f / 32.0f)
978 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
979 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
980 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
981 for (i = 0;i < 8;i++)
983 v[0] = (i & 1) ? m2[0] : m1[0];
984 v[1] = (i & 2) ? m2[1] : m1[1];
985 v[2] = (i & 4) ? m2[2] : m1[2];
986 if (SV_PointSuperContents(v) & contents)
991 // if the trace found a better position for the entity, move it there
992 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
995 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
996 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
998 // verify if the endpos is REALLY outside solid
999 VectorCopy(trace.endpos, org);
1000 trace = SV_TraceBox(org, PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), org, MOVE_NOMONSTERS, ent, contents);
1001 if(trace.startsolid)
1002 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
1004 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1010 // DRESK - Support for Entity Contents Transition Event
1013 SV_CheckContentsTransition
1015 returns true if entity had a valid contentstransition function call
1018 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
1020 prvm_prog_t *prog = SVVM_prog;
1021 int bValidFunctionCall;
1023 // Default Valid Function Call to False
1024 bValidFunctionCall = false;
1026 if(PRVM_serveredictfloat(ent, watertype) != nContents)
1027 { // Changed Contents
1028 // Acquire Contents Transition Function from QC
1029 if(PRVM_serveredictfunction(ent, contentstransition))
1030 { // Valid Function; Execute
1031 // Assign Valid Function
1032 bValidFunctionCall = true;
1033 // Prepare Parameters (Original Contents, New Contents)
1034 // Original Contents
1035 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
1037 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1039 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1041 PRVM_serverglobalfloat(time) = sv.time;
1042 // Execute VM Function
1043 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1047 // Return if Function Call was Valid
1048 return bValidFunctionCall;
1057 void SV_CheckVelocity (prvm_edict_t *ent)
1059 prvm_prog_t *prog = SVVM_prog;
1066 for (i=0 ; i<3 ; i++)
1068 if (IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1070 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1071 PRVM_serveredictvector(ent, velocity)[i] = 0;
1073 if (IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1075 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1076 PRVM_serveredictvector(ent, origin)[i] = 0;
1080 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1081 // player_run/player_stand1 does not horribly malfunction if the
1082 // velocity becomes a denormalized float
1083 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0001)
1084 VectorClear(PRVM_serveredictvector(ent, velocity));
1086 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1087 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1088 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1090 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1091 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1092 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1093 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1101 Runs thinking code if time. There is some play in the exact time the think
1102 function will be called, because it is called before any movement is done
1103 in a frame. Not used for pushmove objects, because they must be exact.
1104 Returns false if the entity removed itself.
1107 static qboolean SV_RunThink (prvm_edict_t *ent)
1109 prvm_prog_t *prog = SVVM_prog;
1112 // don't let things stay in the past.
1113 // it is possible to start that way by a trigger with a local time.
1114 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1117 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1119 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1120 PRVM_serveredictfloat(ent, nextthink) = 0;
1121 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1122 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1123 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1124 // mods often set nextthink to time to cause a think every frame,
1125 // we don't want to loop in that case, so exit if the new nextthink is
1126 // <= the time the qc was told, also exit if it is past the end of the
1128 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1131 return !ent->priv.server->free;
1138 Two entities have touched, so run their touch functions
1141 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1143 prvm_prog_t *prog = SVVM_prog;
1144 int restorevm_tempstringsbuf_cursize;
1145 int old_self, old_other;
1146 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1148 old_self = PRVM_serverglobaledict(self);
1149 old_other = PRVM_serverglobaledict(other);
1150 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1152 VM_SetTraceGlobals(prog, trace);
1154 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1156 PRVM_serverglobalfloat(time) = sv.time;
1157 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1158 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1159 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1162 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1164 PRVM_serverglobalfloat(time) = sv.time;
1165 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1166 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1167 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1168 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1169 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1170 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1171 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1172 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1173 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1174 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1175 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1178 PRVM_serverglobaledict(self) = old_self;
1179 PRVM_serverglobaledict(other) = old_other;
1180 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1188 Slide off of the impacting object
1189 returns the blocked flags (1 = floor, 2 = step / wall)
1192 #define STOP_EPSILON 0.1
1193 static void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1198 backoff = -DotProduct (in, normal) * overbounce;
1199 VectorMA(in, backoff, normal, out);
1201 for (i = 0;i < 3;i++)
1202 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1211 The basic solid body movement clip that slides along multiple planes
1212 Returns the clipflags if the velocity was modified (hit something solid)
1216 8 = teleported by touch method
1217 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1220 static float SV_Gravity (prvm_edict_t *ent);
1221 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1222 #define MAX_CLIP_PLANES 5
1223 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1225 prvm_prog_t *prog = SVVM_prog;
1226 int blocked, bumpcount;
1227 int i, j, numplanes;
1228 float d, time_left, gravity;
1229 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1240 gravity = SV_Gravity(ent);
1242 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1244 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1245 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1247 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1252 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1253 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1256 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1258 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1261 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1262 if(!SV_PushEntity(&trace, ent, push, false, false))
1264 // we got teleported by a touch function
1265 // let's abort the move
1270 if (trace.fraction == 1)
1272 if (trace.plane.normal[2])
1274 if (trace.plane.normal[2] > 0.7)
1281 Con_Printf ("SV_FlyMove: !trace.ent");
1282 trace.ent = prog->edicts;
1285 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1286 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1289 else if (stepheight)
1291 // step - handle it immediately
1297 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1298 VectorSet(steppush, 0, 0, stepheight);
1299 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1300 if(!SV_PushEntity(&steptrace, ent, steppush, false, false))
1305 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1306 if(!SV_PushEntity(&steptrace2, ent, push, false, false))
1311 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1312 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1313 if(!SV_PushEntity(&steptrace3, ent, steppush, false, false))
1318 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1319 // accept the new position if it made some progress...
1320 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1322 //Con_Printf("accepted (delta %f %f %f)\n", PRVM_serveredictvector(ent, origin)[0] - org[0], PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
1324 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1325 time_left *= 1 - trace.fraction;
1331 //Con_Printf("REJECTED (delta %f %f %f)\n", PRVM_serveredictvector(ent, origin)[0] - org[0], PRVM_serveredictvector(ent, origin)[1] - org[1], PRVM_serveredictvector(ent, origin)[2] - org[2]);
1332 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1337 // step - return it to caller
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(PRVM_serveredictvector(ent, 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(PRVM_serveredictvector(ent, velocity));
1362 for (i = 0;i < numplanes;i++)
1363 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1367 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1372 VectorCopy(trace.plane.normal, planes[numplanes]);
1375 // modify original_velocity so it parallels all of the clip planes
1376 for (i = 0;i < numplanes;i++)
1378 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1379 for (j = 0;j < numplanes;j++)
1384 if (DotProduct(new_velocity, planes[j]) < 0)
1394 // go along this plane
1395 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1399 // go along the crease
1402 VectorClear(PRVM_serveredictvector(ent, velocity));
1406 CrossProduct(planes[0], planes[1], dir);
1407 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1408 VectorNormalize(dir);
1409 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1410 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1413 // if current velocity is against the original velocity,
1414 // stop dead to avoid tiny occilations in sloping corners
1415 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1417 VectorClear(PRVM_serveredictvector(ent, velocity));
1422 //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, PRVM_serveredictvector(ent, velocity)[0], PRVM_serveredictvector(ent, velocity)[1], PRVM_serveredictvector(ent, velocity)[2]);
1425 if ((blocked & 1) == 0 && bumpcount > 1)
1427 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1428 // flag ONGROUND if there's ground under it
1429 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask);
1433 // LordHavoc: this came from QW and allows you to get out of water more easily
1434 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1435 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1439 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1441 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1442 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1455 static float SV_Gravity (prvm_edict_t *ent)
1457 prvm_prog_t *prog = SVVM_prog;
1460 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1463 return ent_gravity * sv_gravity.value * sv.frametime;
1468 ===============================================================================
1472 ===============================================================================
1475 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1477 prvm_prog_t *prog = SVVM_prog;
1481 vec3_t stuckmins, stuckmaxs;
1482 vec3_t goodmins, goodmaxs;
1486 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1487 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1488 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1489 VectorCopy(pivot, goodmins);
1490 VectorCopy(pivot, goodmaxs);
1491 for (bump = 0;bump < 6;bump++)
1493 int coord = 2-(bump >> 1);
1494 //int coord = (bump >> 1);
1495 int dir = (bump & 1);
1498 for(subbump = 0; ; ++subbump)
1500 VectorCopy(stuckorigin, testorigin);
1504 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1509 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1512 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1513 if (stucktrace.bmodelstartsolid)
1515 // BAD BAD, can't fix that
1519 if (stucktrace.fraction >= 1)
1524 // BAD BAD, can't fix that
1528 // we hit something... let's move out of it
1529 VectorSubtract(stucktrace.endpos, testorigin, move);
1530 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1531 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1535 Con_Printf("subbump: %d\n", subbump);
1541 goodmaxs[coord] = stuckmaxs[coord];
1546 goodmins[coord] = stuckmins[coord];
1551 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1556 static qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1558 prvm_prog_t *prog = SVVM_prog;
1562 vec3_t stuckmins, stuckmaxs;
1564 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1565 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1566 separation = 0.0f; // when using hulls, it can not be enlarged
1567 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1568 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1569 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1570 stuckmins[0] -= separation;
1571 stuckmins[1] -= separation;
1572 stuckmins[2] -= separation;
1573 stuckmaxs[0] += separation;
1574 stuckmaxs[1] += separation;
1575 stuckmaxs[2] += separation;
1576 for (bump = 0;bump < 10;bump++)
1578 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1579 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1581 // found a good location, use it
1582 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1585 nudge = -stucktrace.startdepth;
1586 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1595 Does not change the entities velocity at all
1596 The trace struct is filled with the trace that has been done.
1597 Returns true if the push did not result in the entity being teleported by QC code.
1600 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1602 prvm_prog_t *prog = SVVM_prog;
1610 solid = (int)PRVM_serveredictfloat(ent, solid);
1611 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1612 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1613 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1615 // move start position out of solids
1616 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1618 SV_NudgeOutOfSolid(ent);
1621 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1622 VectorAdd(start, push, end);
1624 if (movetype == MOVETYPE_FLYMISSILE)
1625 type = MOVE_MISSILE;
1626 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1627 type = MOVE_WORLDONLY;
1628 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1629 type = MOVE_NOMONSTERS; // only clip against bmodels
1633 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1634 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1637 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1639 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1644 if(!trace->startsolid)
1645 if(SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), PRVM_serveredictvector(ent, origin), type, ent, SV_GenericHitSuperContentsMask(ent)).startsolid)
1647 Con_Printf("something eeeeevil happened\n");
1652 SV_LinkEdict_TouchAreaGrid(ent);
1654 if((PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace->ent && (!((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) || PRVM_serveredictedict(ent, groundentity) != PRVM_EDICT_TO_PROG(trace->ent))))
1655 SV_Impact (ent, trace);
1657 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1659 ent->priv.required->mark = 0;
1662 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1664 ent->priv.required->mark = 0;
1669 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1681 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1683 prvm_prog_t *prog = SVVM_prog;
1685 int pusherowner, pusherprog;
1688 float savesolid, movetime2, pushltime;
1689 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1691 int numcheckentities;
1692 static prvm_edict_t *checkentities[MAX_EDICTS];
1693 dp_model_t *pushermodel;
1694 trace_t trace, trace2;
1695 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1696 static unsigned short moved_edicts[MAX_EDICTS];
1699 if (!PRVM_serveredictvector(pusher, velocity)[0] && !PRVM_serveredictvector(pusher, velocity)[1] && !PRVM_serveredictvector(pusher, velocity)[2] && !PRVM_serveredictvector(pusher, avelocity)[0] && !PRVM_serveredictvector(pusher, avelocity)[1] && !PRVM_serveredictvector(pusher, avelocity)[2])
1701 PRVM_serveredictfloat(pusher, ltime) += movetime;
1705 switch ((int) PRVM_serveredictfloat(pusher, solid))
1707 // LordHavoc: valid pusher types
1710 case SOLID_SLIDEBOX:
1711 case SOLID_CORPSE: // LordHavoc: this would be weird...
1713 // LordHavoc: no collisions
1716 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1717 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1718 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1719 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1720 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1721 PRVM_serveredictfloat(pusher, ltime) += movetime;
1722 SV_LinkEdict(pusher);
1725 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1728 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1729 if (index < 1 || index >= MAX_MODELS)
1731 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1734 pushermodel = SV_GetModelByIndex(index);
1735 pusherowner = PRVM_serveredictedict(pusher, owner);
1736 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1738 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1740 movetime2 = movetime;
1741 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1742 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1743 if (moveangle[0] || moveangle[2])
1745 for (i = 0;i < 3;i++)
1749 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1750 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1754 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1755 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1759 else if (moveangle[1])
1761 for (i = 0;i < 3;i++)
1765 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1766 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1770 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1771 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1777 for (i = 0;i < 3;i++)
1781 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1782 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1786 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1787 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1792 VectorNegate (moveangle, a);
1793 AngleVectorsFLU (a, forward, left, up);
1795 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1796 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1797 pushltime = PRVM_serveredictfloat(pusher, ltime);
1799 // move the pusher to its final position
1801 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1802 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1803 PRVM_serveredictfloat(pusher, ltime) += movetime;
1804 SV_LinkEdict(pusher);
1806 pushermodel = SV_GetModelFromEdict(pusher);
1807 Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, PRVM_serveredictvector(pusher, origin)[0], PRVM_serveredictvector(pusher, origin)[1], PRVM_serveredictvector(pusher, origin)[2], PRVM_serveredictvector(pusher, angles)[0], PRVM_serveredictvector(pusher, angles)[1], PRVM_serveredictvector(pusher, angles)[2], 1);
1808 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1810 savesolid = PRVM_serveredictfloat(pusher, solid);
1812 // see if any solid entities are inside the final position
1815 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1816 numcheckentities = 0;
1817 else // MOVETYPE_PUSH
1818 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1819 for (e = 0;e < numcheckentities;e++)
1821 prvm_edict_t *check = checkentities[e];
1822 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1827 case MOVETYPE_FOLLOW:
1828 case MOVETYPE_NOCLIP:
1829 case MOVETYPE_FLY_WORLDONLY:
1835 if (PRVM_serveredictedict(check, owner) == pusherprog)
1838 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1841 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1843 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1844 check->priv.server->waterposition_forceupdate = true;
1846 checkcontents = SV_GenericHitSuperContentsMask(check);
1848 // if the entity is standing on the pusher, it will definitely be moved
1849 // if the entity is not standing on the pusher, but is in the pusher's
1850 // final position, move it
1851 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1853 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, PRVM_serveredictvector(pusher, mins), PRVM_serveredictvector(pusher, maxs), SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), checkcontents);
1854 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1855 if (!trace.startsolid)
1857 //Con_Printf("- not in solid\n");
1862 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1863 //VectorClear(pivot);
1868 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1869 VectorAdd (org, pivot, org);
1870 org2[0] = DotProduct (org, forward);
1871 org2[1] = DotProduct (org, left);
1872 org2[2] = DotProduct (org, up);
1873 VectorSubtract (org2, org, move);
1874 VectorAdd (move, move1, move);
1877 VectorCopy (move1, move);
1879 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1881 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1882 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1883 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1885 // physics objects need better collisions than this code can do
1886 if (movetype == MOVETYPE_PHYSICS)
1888 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1889 SV_LinkEdict(check);
1890 SV_LinkEdict_TouchAreaGrid(check);
1894 // try moving the contacted entity
1895 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1896 if(!SV_PushEntity (&trace, check, move, true, true))
1898 // entity "check" got teleported
1899 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1900 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1901 continue; // pushed enough
1903 // FIXME: turn players specially
1904 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1905 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1906 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1908 // this trace.fraction < 1 check causes items to fall off of pushers
1909 // if they pass under or through a wall
1910 // the groundentity check causes items to fall off of ledges
1911 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1912 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1914 // if it is still inside the pusher, block
1915 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, PRVM_serveredictvector(pusher, mins), PRVM_serveredictvector(pusher, maxs), SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), checkcontents);
1916 if (trace.startsolid)
1919 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1921 // hack to invoke all necessary movement triggers
1923 if(!SV_PushEntity(&trace2, check, move2, true, true))
1925 // entity "check" got teleported
1932 // still inside pusher, so it's really blocked
1935 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1937 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1940 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1941 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1945 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1946 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1947 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1948 SV_LinkEdict(pusher);
1950 // move back any entities we already moved
1951 for (i = 0;i < num_moved;i++)
1953 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1954 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1955 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1959 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1960 if (PRVM_serveredictfunction(pusher, blocked))
1962 PRVM_serverglobalfloat(time) = sv.time;
1963 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1964 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1965 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1970 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1971 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1972 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1981 static void SV_Physics_Pusher (prvm_edict_t *ent)
1983 prvm_prog_t *prog = SVVM_prog;
1984 float thinktime, oldltime, movetime;
1986 oldltime = PRVM_serveredictfloat(ent, ltime);
1988 thinktime = PRVM_serveredictfloat(ent, nextthink);
1989 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1991 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1996 movetime = sv.frametime;
1999 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
2000 SV_PushMove (ent, movetime);
2002 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
2004 PRVM_serveredictfloat(ent, nextthink) = 0;
2005 PRVM_serverglobalfloat(time) = sv.time;
2006 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2007 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
2008 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2014 ===============================================================================
2018 ===============================================================================
2021 static float unstickoffsets[] =
2023 // poutting -/+z changes first as they are least weird
2038 typedef enum unstickresult_e
2046 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2048 prvm_prog_t *prog = SVVM_prog;
2051 // if not stuck in a bmodel, just return
2052 if (!SV_TestEntityPosition(ent, vec3_origin))
2053 return UNSTICK_GOOD;
2055 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2057 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2059 VectorCopy(unstickoffsets + i, offset);
2061 //SV_LinkEdict_TouchAreaGrid(ent);
2062 return UNSTICK_UNSTUCK;
2066 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2067 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2069 for(i = 2; i <= maxunstick; ++i)
2071 VectorClear(offset);
2073 if (!SV_TestEntityPosition(ent, offset))
2076 //SV_LinkEdict_TouchAreaGrid(ent);
2077 return UNSTICK_UNSTUCK;
2080 if (!SV_TestEntityPosition(ent, offset))
2083 //SV_LinkEdict_TouchAreaGrid(ent);
2084 return UNSTICK_UNSTUCK;
2088 return UNSTICK_STUCK;
2091 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2093 prvm_prog_t *prog = SVVM_prog;
2095 switch(SV_UnstickEntityReturnOffset(ent, offset))
2099 case UNSTICK_UNSTUCK:
2100 Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2103 if (developer_extra.integer)
2104 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2107 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2116 This is a big hack to try and fix the rare case of getting stuck in the world
2120 static void SV_CheckStuck (prvm_edict_t *ent)
2122 prvm_prog_t *prog = SVVM_prog;
2125 switch(SV_UnstickEntityReturnOffset(ent, offset))
2128 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2130 case UNSTICK_UNSTUCK:
2131 Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2134 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2135 if (!SV_TestEntityPosition(ent, offset))
2137 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2139 //SV_LinkEdict_TouchAreaGrid(ent);
2142 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2145 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2155 static qboolean SV_CheckWater (prvm_edict_t *ent)
2157 prvm_prog_t *prog = SVVM_prog;
2159 int nNativeContents;
2162 point[0] = PRVM_serveredictvector(ent, origin)[0];
2163 point[1] = PRVM_serveredictvector(ent, origin)[1];
2164 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2166 // DRESK - Support for Entity Contents Transition Event
2167 // NOTE: Some logic needed to be slightly re-ordered
2168 // to not affect performance and allow for the feature.
2170 // Acquire Super Contents Prior to Resets
2171 cont = SV_PointSuperContents(point);
2172 // Acquire Native Contents Here
2173 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2175 // DRESK - Support for Entity Contents Transition Event
2176 if(PRVM_serveredictfloat(ent, watertype))
2177 // Entity did NOT Spawn; Check
2178 SV_CheckContentsTransition(ent, nNativeContents);
2181 PRVM_serveredictfloat(ent, waterlevel) = 0;
2182 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2183 cont = SV_PointSuperContents(point);
2184 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2186 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2187 PRVM_serveredictfloat(ent, waterlevel) = 1;
2188 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2189 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2191 PRVM_serveredictfloat(ent, waterlevel) = 2;
2192 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2193 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2194 PRVM_serveredictfloat(ent, waterlevel) = 3;
2198 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2207 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2209 prvm_prog_t *prog = SVVM_prog;
2211 vec3_t forward, into, side;
2213 AngleVectors (PRVM_serveredictvector(ent, v_angle), forward, NULL, NULL);
2214 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2216 // cut the tangential velocity
2217 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2218 VectorScale (stepnormal, i, into);
2219 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2220 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2221 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2227 =====================
2230 Player has come to a dead stop, possibly due to the problem with limited
2231 float precision at some angle joins in the BSP hull.
2233 Try fixing by pushing one pixel in each direction.
2235 This is a hack, but in the interest of good gameplay...
2236 ======================
2238 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2243 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2246 for (i=0 ; i<8 ; i++)
2248 // try pushing a little in an axial direction
2251 case 0: dir[0] = 2; dir[1] = 0; break;
2252 case 1: dir[0] = 0; dir[1] = 2; break;
2253 case 2: dir[0] = -2; dir[1] = 0; break;
2254 case 3: dir[0] = 0; dir[1] = -2; break;
2255 case 4: dir[0] = 2; dir[1] = 2; break;
2256 case 5: dir[0] = -2; dir[1] = 2; break;
2257 case 6: dir[0] = 2; dir[1] = -2; break;
2258 case 7: dir[0] = -2; dir[1] = -2; break;
2261 SV_PushEntity (&trace, ent, dir, false, true);
2263 // retry the original move
2264 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2265 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2266 PRVM_serveredictvector(ent, velocity)[2] = 0;
2267 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2269 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2270 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2272 Con_DPrint("TryUnstick - success.\n");
2276 // go back to the original pos and try again
2277 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2281 VectorClear (PRVM_serveredictvector(ent, velocity));
2282 Con_DPrint("TryUnstick - failure.\n");
2288 =====================
2291 Only used by players
2292 ======================
2294 static void SV_WalkMove (prvm_edict_t *ent)
2296 prvm_prog_t *prog = SVVM_prog;
2299 //int originalmove_clip;
2300 int originalmove_flags;
2301 int originalmove_groundentity;
2302 int hitsupercontentsmask;
2304 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2305 trace_t downtrace, trace;
2306 qboolean applygravity;
2308 // if frametime is 0 (due to client sending the same timestamp twice),
2310 if (sv.frametime <= 0)
2313 if (sv_gameplayfix_unstickplayers.integer)
2314 SV_CheckStuck (ent);
2316 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2318 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2320 SV_CheckVelocity(ent);
2322 // do a regular slide move unless it looks like you ran into a step
2323 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2325 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2326 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2328 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2330 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2333 // only try this if there was no floor in the way in the trace (no,
2334 // this check seems to be not REALLY necessary, because if clip & 1,
2335 // our trace will hit that thing too)
2336 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2337 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2338 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2339 type = MOVE_MISSILE;
2340 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2341 type = MOVE_WORLDONLY;
2342 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2343 type = MOVE_NOMONSTERS; // only clip against bmodels
2346 trace = SV_TraceBox(upmove, PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2347 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2348 clip |= 1; // but we HAVE found a floor
2351 // if the move did not hit the ground at any point, we're not on ground
2353 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2355 SV_CheckVelocity(ent);
2357 SV_LinkEdict_TouchAreaGrid(ent);
2359 if(clip & 8) // teleport
2362 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2365 if (sv_nostep.integer)
2368 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2369 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2370 //originalmove_clip = clip;
2371 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2372 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2374 // if move didn't block on a step, return
2377 // if move was not trying to move into the step, return
2378 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2381 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2383 // return if gibbed by a trigger
2384 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2387 // only step up while jumping if that is enabled
2388 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2389 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2393 // try moving up and forward to go up a step
2394 // back to start pos
2395 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2396 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2399 VectorClear (upmove);
2400 upmove[2] = sv_stepheight.value;
2401 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2403 // we got teleported when upstepping... must abort the move
2408 PRVM_serveredictvector(ent, velocity)[2] = 0;
2409 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2410 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2413 // we got teleported when upstepping... must abort the move
2414 // note that z velocity handling may not be what QC expects here, but we cannot help it
2418 SV_CheckVelocity(ent);
2420 SV_LinkEdict_TouchAreaGrid(ent);
2422 // check for stuckness, possibly due to the limited precision of floats
2423 // in the clipping hulls
2425 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2426 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2428 //Con_Printf("wall\n");
2429 // stepping up didn't make any progress, revert to original move
2430 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2431 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2432 //clip = originalmove_clip;
2433 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2434 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2435 // now try to unstick if needed
2436 //clip = SV_TryUnstick (ent, oldvel);
2440 //Con_Printf("step - ");
2442 // extra friction based on view angle
2443 if (clip & 2 && sv_wallfriction.integer)
2444 SV_WallFriction (ent, stepnormal);
2446 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2447 else if (!sv_gameplayfix_stepdown.integer || PRVM_serveredictfloat(ent, waterlevel) >= 3 || start_velocity[2] >= (1.0 / 32.0) || !oldonground || ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
2451 VectorClear (downmove);
2452 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2453 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2455 // we got teleported when downstepping... must abort the move
2459 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2461 // this has been disabled so that you can't jump when you are stepping
2462 // up while already jumping (also known as the Quake2 double jump bug)
2464 // LordHavoc: disabled this check so you can walk on monsters/players
2465 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2467 //Con_Printf("onground\n");
2468 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2469 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2475 //Con_Printf("slope\n");
2476 // if the push down didn't end up on good ground, use the move without
2477 // the step up. This happens near wall / slope combinations, and can
2478 // cause the player to hop up higher on a slope too steep to climb
2479 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2480 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2481 //clip = originalmove_clip;
2482 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2483 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2486 SV_CheckVelocity(ent);
2488 SV_LinkEdict_TouchAreaGrid(ent);
2491 //============================================================================
2497 Entities that are "stuck" to another entity
2500 static void SV_Physics_Follow (prvm_edict_t *ent)
2502 prvm_prog_t *prog = SVVM_prog;
2503 vec3_t vf, vr, vu, angles, v;
2507 if (!SV_RunThink (ent))
2510 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2511 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2512 if (PRVM_serveredictvector(e, angles)[0] == PRVM_serveredictvector(ent, punchangle)[0] && PRVM_serveredictvector(e, angles)[1] == PRVM_serveredictvector(ent, punchangle)[1] && PRVM_serveredictvector(e, angles)[2] == PRVM_serveredictvector(ent, punchangle)[2])
2514 // quick case for no rotation
2515 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2519 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2520 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2521 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2522 AngleVectors (angles, vf, vr, vu);
2523 v[0] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[0] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[0] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[0];
2524 v[1] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[1] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[1] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[1];
2525 v[2] = PRVM_serveredictvector(ent, view_ofs)[0] * vf[2] + PRVM_serveredictvector(ent, view_ofs)[1] * vr[2] + PRVM_serveredictvector(ent, view_ofs)[2] * vu[2];
2526 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2527 angles[1] = PRVM_serveredictvector(e, angles)[1];
2528 angles[2] = PRVM_serveredictvector(e, angles)[2];
2529 AngleVectors (angles, vf, vr, vu);
2530 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2531 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2532 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2534 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2536 //SV_LinkEdict_TouchAreaGrid(ent);
2540 ==============================================================================
2544 ==============================================================================
2549 SV_CheckWaterTransition
2553 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2555 prvm_prog_t *prog = SVVM_prog;
2556 // LordHavoc: bugfixes in this function are keyed to the sv_gameplayfix_bugfixedcheckwatertransition cvar - if this cvar is 0 then all the original bugs should be reenabled for compatibility
2558 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(PRVM_serveredictvector(ent, origin)));
2559 if (!PRVM_serveredictfloat(ent, watertype))
2561 // just spawned here
2562 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2564 PRVM_serveredictfloat(ent, watertype) = cont;
2565 PRVM_serveredictfloat(ent, waterlevel) = 1;
2569 // DRESK - Support for Entity Contents Transition Event
2570 // NOTE: Call here BEFORE updating the watertype below,
2571 // and suppress watersplash sound if a valid function
2572 // call was made to allow for custom "splash" sounds.
2573 else if( !SV_CheckContentsTransition(ent, cont) )
2574 { // Contents Transition Function Invalid; Potentially Play Water Sound
2575 // check if the entity crossed into or out of water
2576 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2577 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2580 if (cont <= CONTENTS_WATER)
2582 PRVM_serveredictfloat(ent, watertype) = cont;
2583 PRVM_serveredictfloat(ent, waterlevel) = 1;
2587 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2588 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2596 Toss, bounce, and fly movement. When onground, do nothing.
2600 void SV_Physics_Toss (prvm_edict_t *ent)
2602 prvm_prog_t *prog = SVVM_prog;
2607 prvm_edict_t *groundentity;
2608 float d, ent_gravity;
2612 // if onground, return without moving
2613 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2615 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2616 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2618 // don't stick to ground if onground and moving upward
2619 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2621 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2623 // we can trust FL_ONGROUND if groundentity is world because it never moves
2626 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2628 // if ent was supported by a brush model on previous frame,
2629 // and groundentity is now freed, set groundentity to 0 (world)
2630 // which leaves it suspended in the air
2631 PRVM_serveredictedict(ent, groundentity) = 0;
2632 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2635 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2637 // don't slide if still touching the groundentity
2641 ent->priv.server->suspendedinairflag = false;
2643 SV_CheckVelocity (ent);
2646 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2647 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2650 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2652 movetime = sv.frametime;
2653 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2656 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2657 if(!SV_PushEntity(&trace, ent, move, true, true))
2658 return; // teleported
2659 if (ent->priv.server->free)
2661 if (trace.bmodelstartsolid)
2663 // try to unstick the entity
2664 if (sv_gameplayfix_unstickentities.integer)
2665 SV_UnstickEntity(ent);
2666 if(!SV_PushEntity(&trace, ent, move, false, true))
2667 return; // teleported
2668 if (ent->priv.server->free)
2671 if (trace.fraction == 1)
2673 movetime *= 1 - min(1, trace.fraction);
2674 switch((int)PRVM_serveredictfloat(ent, movetype))
2676 case MOVETYPE_BOUNCEMISSILE:
2677 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2679 bouncefactor = 1.0f;
2681 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2682 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2683 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2686 case MOVETYPE_BOUNCE:
2687 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2689 bouncefactor = 0.5f;
2691 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2693 bouncestop = 60.0f / 800.0f;
2695 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2696 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2699 // LordHavoc: fixed grenades not bouncing when fired down a slope
2700 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2701 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2703 d = PRVM_serveredictvector(ent, velocity)[2];
2704 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2706 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2707 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2708 VectorClear(PRVM_serveredictvector(ent, velocity));
2709 VectorClear(PRVM_serveredictvector(ent, avelocity));
2714 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2715 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2720 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2721 if (trace.plane.normal[2] > 0.7)
2723 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2724 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2725 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2726 ent->priv.server->suspendedinairflag = true;
2727 VectorClear (PRVM_serveredictvector(ent, velocity));
2728 VectorClear (PRVM_serveredictvector(ent, avelocity));
2731 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2737 // check for in water
2738 SV_CheckWaterTransition (ent);
2742 ===============================================================================
2746 ===============================================================================
2753 Monsters freefall when they don't have a ground entity, otherwise
2754 all movement is done with discrete steps.
2756 This is also used for objects that have become still on the ground, but
2757 will fall if the floor is pulled out from under them.
2760 static void SV_Physics_Step (prvm_edict_t *ent)
2762 prvm_prog_t *prog = SVVM_prog;
2763 int flags = (int)PRVM_serveredictfloat(ent, flags);
2766 // Backup Velocity in the event that movetypesteplandevent is called,
2767 // to provide a parameter with the entity's velocity at impact.
2768 vec3_t backupVelocity;
2769 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2770 // don't fall at all if fly/swim
2771 if (!(flags & (FL_FLY | FL_SWIM)))
2773 if (flags & FL_ONGROUND)
2775 // freefall if onground and moving upward
2776 // freefall if not standing on a world surface (it may be a lift or trap door)
2777 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2779 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2780 SV_CheckVelocity(ent);
2781 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2783 SV_LinkEdict_TouchAreaGrid(ent);
2784 ent->priv.server->waterposition_forceupdate = true;
2789 // freefall if not onground
2790 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2792 SV_CheckVelocity(ent);
2793 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2795 SV_LinkEdict_TouchAreaGrid(ent);
2798 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2800 // DRESK - Check for Entity Land Event Function
2801 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2802 { // Valid Function; Execute
2803 // Prepare Parameters
2804 // Assign Velocity at Impact
2805 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2806 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2807 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2809 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2811 PRVM_serverglobalfloat(time) = sv.time;
2812 // Execute VM Function
2813 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2816 // Check for Engine Landing Sound
2817 if(sv_sound_land.string)
2818 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2820 ent->priv.server->waterposition_forceupdate = true;
2825 if (!SV_RunThink(ent))
2828 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2830 ent->priv.server->waterposition_forceupdate = false;
2831 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2832 SV_CheckWaterTransition(ent);
2836 //============================================================================
2838 static void SV_Physics_Entity (prvm_edict_t *ent)
2840 prvm_prog_t *prog = SVVM_prog;
2841 // don't run think/move on newly spawned projectiles as it messes up
2842 // movement interpolation and rocket trails, and is inconsistent with
2843 // respect to entities spawned in the same frame
2844 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2845 // but if it spawns a lower numbered ent, it doesn't - this never moves
2846 // ents in the first frame regardless)
2847 qboolean runmove = ent->priv.server->move;
2848 ent->priv.server->move = true;
2849 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2851 switch ((int) PRVM_serveredictfloat(ent, movetype))
2854 case MOVETYPE_FAKEPUSH:
2855 SV_Physics_Pusher (ent);
2858 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2859 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2862 case MOVETYPE_FOLLOW:
2863 SV_Physics_Follow (ent);
2865 case MOVETYPE_NOCLIP:
2866 if (SV_RunThink(ent))
2869 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2870 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2875 SV_Physics_Step (ent);
2878 if (SV_RunThink (ent))
2882 case MOVETYPE_BOUNCE:
2883 case MOVETYPE_BOUNCEMISSILE:
2884 case MOVETYPE_FLYMISSILE:
2886 case MOVETYPE_FLY_WORLDONLY:
2888 if (SV_RunThink (ent))
2889 SV_Physics_Toss (ent);
2891 case MOVETYPE_PHYSICS:
2892 if (SV_RunThink(ent))
2895 SV_LinkEdict_TouchAreaGrid(ent);
2899 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2904 void SV_Physics_ClientMove(void)
2906 prvm_prog_t *prog = SVVM_prog;
2908 ent = host_client->edict;
2910 // call player physics, this needs the proper frametime
2911 PRVM_serverglobalfloat(frametime) = sv.frametime;
2914 // call standard client pre-think, with frametime = 0
2915 PRVM_serverglobalfloat(time) = sv.time;
2916 PRVM_serverglobalfloat(frametime) = 0;
2917 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2918 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2919 PRVM_serverglobalfloat(frametime) = sv.frametime;
2921 // make sure the velocity is sane (not a NaN)
2922 SV_CheckVelocity(ent);
2924 // perform MOVETYPE_WALK behavior
2927 // call standard player post-think, with frametime = 0
2928 PRVM_serverglobalfloat(time) = sv.time;
2929 PRVM_serverglobalfloat(frametime) = 0;
2930 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2931 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2932 PRVM_serverglobalfloat(frametime) = sv.frametime;
2934 if(PRVM_serveredictfloat(ent, fixangle))
2936 // angle fixing was requested by physics code...
2937 // so store the current angles for later use
2938 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2939 host_client->fixangle_angles_set = TRUE;
2941 // and clear fixangle for the next frame
2942 PRVM_serveredictfloat(ent, fixangle) = 0;
2946 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2948 prvm_prog_t *prog = SVVM_prog;
2949 // don't do physics on disconnected clients, FrikBot relies on this
2950 if (!host_client->spawned)
2953 // make sure the velocity is sane (not a NaN)
2954 SV_CheckVelocity(ent);
2956 // don't run physics here if running asynchronously
2957 if (host_client->clmovement_inputtimeout <= 0)
2960 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2963 // make sure the velocity is still sane (not a NaN)
2964 SV_CheckVelocity(ent);
2966 // call standard client pre-think
2967 PRVM_serverglobalfloat(time) = sv.time;
2968 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2969 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2971 // make sure the velocity is still sane (not a NaN)
2972 SV_CheckVelocity(ent);
2975 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2977 prvm_prog_t *prog = SVVM_prog;
2978 // don't do physics on disconnected clients, FrikBot relies on this
2979 if (!host_client->spawned)
2982 // make sure the velocity is sane (not a NaN)
2983 SV_CheckVelocity(ent);
2985 // call standard player post-think
2986 PRVM_serverglobalfloat(time) = sv.time;
2987 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2988 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2990 // make sure the velocity is still sane (not a NaN)
2991 SV_CheckVelocity(ent);
2993 if(PRVM_serveredictfloat(ent, fixangle))
2995 // angle fixing was requested by physics code...
2996 // so store the current angles for later use
2997 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2998 host_client->fixangle_angles_set = TRUE;
3000 // and clear fixangle for the next frame
3001 PRVM_serveredictfloat(ent, fixangle) = 0;
3004 // decrement the countdown variable used to decide when to go back to
3005 // synchronous physics
3006 if (host_client->clmovement_inputtimeout > sv.frametime)
3007 host_client->clmovement_inputtimeout -= sv.frametime;
3009 host_client->clmovement_inputtimeout = 0;
3012 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3014 prvm_prog_t *prog = SVVM_prog;
3015 // don't do physics on disconnected clients, FrikBot relies on this
3016 if (!host_client->spawned)
3018 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3022 // make sure the velocity is sane (not a NaN)
3023 SV_CheckVelocity(ent);
3025 switch ((int) PRVM_serveredictfloat(ent, movetype))
3028 case MOVETYPE_FAKEPUSH:
3029 SV_Physics_Pusher (ent);
3032 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3033 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3036 case MOVETYPE_FOLLOW:
3037 SV_Physics_Follow (ent);
3039 case MOVETYPE_NOCLIP:
3042 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3043 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3046 SV_Physics_Step (ent);
3050 // don't run physics here if running asynchronously
3051 if (host_client->clmovement_inputtimeout <= 0)
3055 case MOVETYPE_BOUNCE:
3056 case MOVETYPE_BOUNCEMISSILE:
3057 case MOVETYPE_FLYMISSILE:
3060 SV_Physics_Toss (ent);
3063 case MOVETYPE_FLY_WORLDONLY:
3067 case MOVETYPE_PHYSICS:
3071 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3075 SV_CheckVelocity (ent);
3078 SV_LinkEdict_TouchAreaGrid(ent);
3080 SV_CheckVelocity (ent);
3089 void SV_Physics (void)
3091 prvm_prog_t *prog = SVVM_prog;
3095 // let the progs know that a new frame has started
3096 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3097 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3098 PRVM_serverglobalfloat(time) = sv.time;
3099 PRVM_serverglobalfloat(frametime) = sv.frametime;
3100 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3102 // run physics engine
3103 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3106 // treat each object in turn
3109 // if force_retouch, relink all the entities
3110 if (PRVM_serverglobalfloat(force_retouch) > 0)
3111 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3112 if (!ent->priv.server->free)
3113 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3115 if (sv_gameplayfix_consistentplayerprethink.integer)
3117 // run physics on the client entities in 3 stages
3118 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3119 if (!ent->priv.server->free)
3120 SV_Physics_ClientEntity_PreThink(ent);
3122 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3123 if (!ent->priv.server->free)
3124 SV_Physics_ClientEntity(ent);
3126 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3127 if (!ent->priv.server->free)
3128 SV_Physics_ClientEntity_PostThink(ent);
3132 // run physics on the client entities
3133 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3135 if (!ent->priv.server->free)
3137 SV_Physics_ClientEntity_PreThink(ent);
3138 SV_Physics_ClientEntity(ent);
3139 SV_Physics_ClientEntity_PostThink(ent);
3144 // run physics on all the non-client entities
3145 if (!sv_freezenonclients.integer)
3147 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3148 if (!ent->priv.server->free)
3149 SV_Physics_Entity(ent);
3150 // make a second pass to see if any ents spawned this frame and make
3151 // sure they run their move/think
3152 if (sv_gameplayfix_delayprojectiles.integer < 0)
3153 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3154 if (!ent->priv.server->move && !ent->priv.server->free)
3155 SV_Physics_Entity(ent);
3158 if (PRVM_serverglobalfloat(force_retouch) > 0)
3159 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3161 // LordHavoc: endframe support
3162 if (PRVM_serverfunction(EndFrame))
3164 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3165 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3166 PRVM_serverglobalfloat(time) = sv.time;
3167 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3170 // decrement prog->num_edicts if the highest number entities died
3171 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3173 if (!sv_freezenonclients.integer)
3174 sv.time += sv.frametime;