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);
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);
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);
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);
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;
1238 if(sv_gameplayfix_nogravityonground.integer)
1239 if((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
1240 applygravity = false;
1244 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1246 gravity = SV_Gravity(ent) * 0.5f;
1247 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1251 applygravity = false;
1252 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
1256 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1257 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1260 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1262 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1265 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1266 if(!SV_PushEntity(&trace, ent, push, false, false))
1268 // we got teleported by a touch function
1269 // let's abort the move
1274 if (trace.fraction == 1)
1276 if (trace.plane.normal[2])
1278 if (trace.plane.normal[2] > 0.7)
1285 Con_Printf ("SV_FlyMove: !trace.ent");
1286 trace.ent = prog->edicts;
1289 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1290 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1293 else if (stepheight)
1295 // step - handle it immediately
1301 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1302 VectorSet(steppush, 0, 0, stepheight);
1303 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1304 if(!SV_PushEntity(&steptrace, ent, steppush, false, false))
1309 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1310 if(!SV_PushEntity(&steptrace2, ent, push, false, false))
1315 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1316 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1317 if(!SV_PushEntity(&steptrace3, ent, steppush, false, false))
1322 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1323 // accept the new position if it made some progress...
1324 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1326 //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]);
1328 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1329 time_left *= 1 - trace.fraction;
1335 //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]);
1336 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1341 // step - return it to caller
1343 // save the trace for player extrafriction
1345 VectorCopy(trace.plane.normal, stepnormal);
1347 if (trace.fraction >= 0.001)
1349 // actually covered some distance
1350 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1354 time_left *= 1 - trace.fraction;
1356 // clipped to another plane
1357 if (numplanes >= MAX_CLIP_PLANES)
1359 // this shouldn't really happen
1360 VectorClear(PRVM_serveredictvector(ent, velocity));
1366 for (i = 0;i < numplanes;i++)
1367 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1371 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1376 VectorCopy(trace.plane.normal, planes[numplanes]);
1379 // modify original_velocity so it parallels all of the clip planes
1380 for (i = 0;i < numplanes;i++)
1382 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1383 for (j = 0;j < numplanes;j++)
1388 if (DotProduct(new_velocity, planes[j]) < 0)
1398 // go along this plane
1399 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1403 // go along the crease
1406 VectorClear(PRVM_serveredictvector(ent, velocity));
1410 CrossProduct(planes[0], planes[1], dir);
1411 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1412 VectorNormalize(dir);
1413 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1414 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1417 // if current velocity is against the original velocity,
1418 // stop dead to avoid tiny occilations in sloping corners
1419 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1421 VectorClear(PRVM_serveredictvector(ent, velocity));
1426 //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]);
1429 if ((blocked & 1) == 0 && bumpcount > 1)
1431 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1432 // flag ONGROUND if there's ground under it
1433 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask);
1437 // LordHavoc: this came from QW and allows you to get out of water more easily
1438 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1439 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1440 if (applygravity && !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1441 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1451 static float SV_Gravity (prvm_edict_t *ent)
1453 prvm_prog_t *prog = SVVM_prog;
1456 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1459 return ent_gravity * sv_gravity.value * sv.frametime;
1464 ===============================================================================
1468 ===============================================================================
1471 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1473 prvm_prog_t *prog = SVVM_prog;
1477 vec3_t stuckmins, stuckmaxs;
1478 vec3_t goodmins, goodmaxs;
1482 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1483 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1484 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1485 VectorCopy(pivot, goodmins);
1486 VectorCopy(pivot, goodmaxs);
1487 for (bump = 0;bump < 6;bump++)
1489 int coord = 2-(bump >> 1);
1490 //int coord = (bump >> 1);
1491 int dir = (bump & 1);
1494 for(subbump = 0; ; ++subbump)
1496 VectorCopy(stuckorigin, testorigin);
1500 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1505 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1508 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1509 if (stucktrace.bmodelstartsolid)
1511 // BAD BAD, can't fix that
1515 if (stucktrace.fraction >= 1)
1520 // BAD BAD, can't fix that
1524 // we hit something... let's move out of it
1525 VectorSubtract(stucktrace.endpos, testorigin, move);
1526 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1527 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1531 Con_Printf("subbump: %d\n", subbump);
1537 goodmaxs[coord] = stuckmaxs[coord];
1542 goodmins[coord] = stuckmins[coord];
1547 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1552 static qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1554 prvm_prog_t *prog = SVVM_prog;
1558 vec3_t stuckmins, stuckmaxs;
1560 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1561 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1562 separation = 0.0f; // when using hulls, it can not be enlarged
1563 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1564 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1565 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1566 stuckmins[0] -= separation;
1567 stuckmins[1] -= separation;
1568 stuckmins[2] -= separation;
1569 stuckmaxs[0] += separation;
1570 stuckmaxs[1] += separation;
1571 stuckmaxs[2] += separation;
1572 for (bump = 0;bump < 10;bump++)
1574 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1575 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1577 // found a good location, use it
1578 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1581 nudge = -stucktrace.startdepth;
1582 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1591 Does not change the entities velocity at all
1592 The trace struct is filled with the trace that has been done.
1593 Returns true if the push did not result in the entity being teleported by QC code.
1596 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1598 prvm_prog_t *prog = SVVM_prog;
1606 solid = (int)PRVM_serveredictfloat(ent, solid);
1607 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1608 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1609 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1611 // move start position out of solids
1612 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1614 SV_NudgeOutOfSolid(ent);
1617 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1618 VectorAdd(start, push, end);
1620 if (movetype == MOVETYPE_FLYMISSILE)
1621 type = MOVE_MISSILE;
1622 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1623 type = MOVE_WORLDONLY;
1624 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1625 type = MOVE_NOMONSTERS; // only clip against bmodels
1629 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1630 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1633 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1635 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1640 if(!trace->startsolid)
1641 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)
1643 Con_Printf("something eeeeevil happened\n");
1648 SV_LinkEdict_TouchAreaGrid(ent);
1650 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))))
1651 SV_Impact (ent, trace);
1653 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1655 ent->priv.required->mark = 0;
1658 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1660 ent->priv.required->mark = 0;
1665 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1677 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1679 prvm_prog_t *prog = SVVM_prog;
1681 int pusherowner, pusherprog;
1684 float savesolid, movetime2, pushltime;
1685 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1687 int numcheckentities;
1688 static prvm_edict_t *checkentities[MAX_EDICTS];
1689 dp_model_t *pushermodel;
1690 trace_t trace, trace2;
1691 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1692 static unsigned short moved_edicts[MAX_EDICTS];
1695 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])
1697 PRVM_serveredictfloat(pusher, ltime) += movetime;
1701 switch ((int) PRVM_serveredictfloat(pusher, solid))
1703 // LordHavoc: valid pusher types
1706 case SOLID_SLIDEBOX:
1707 case SOLID_CORPSE: // LordHavoc: this would be weird...
1709 // LordHavoc: no collisions
1712 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1713 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1714 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1715 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1716 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1717 PRVM_serveredictfloat(pusher, ltime) += movetime;
1718 SV_LinkEdict(pusher);
1721 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1724 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1725 if (index < 1 || index >= MAX_MODELS)
1727 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1730 pushermodel = SV_GetModelByIndex(index);
1731 pusherowner = PRVM_serveredictedict(pusher, owner);
1732 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1734 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1736 movetime2 = movetime;
1737 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1738 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1739 if (moveangle[0] || moveangle[2])
1741 for (i = 0;i < 3;i++)
1745 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1746 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1750 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1751 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1755 else if (moveangle[1])
1757 for (i = 0;i < 3;i++)
1761 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1762 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1766 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1767 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1773 for (i = 0;i < 3;i++)
1777 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1778 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1782 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1783 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1788 VectorNegate (moveangle, a);
1789 AngleVectorsFLU (a, forward, left, up);
1791 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1792 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1793 pushltime = PRVM_serveredictfloat(pusher, ltime);
1795 // move the pusher to its final position
1797 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1798 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1799 PRVM_serveredictfloat(pusher, ltime) += movetime;
1800 SV_LinkEdict(pusher);
1802 pushermodel = SV_GetModelFromEdict(pusher);
1803 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);
1804 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1806 savesolid = PRVM_serveredictfloat(pusher, solid);
1808 // see if any solid entities are inside the final position
1811 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1812 numcheckentities = 0;
1813 else // MOVETYPE_PUSH
1814 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1815 for (e = 0;e < numcheckentities;e++)
1817 prvm_edict_t *check = checkentities[e];
1818 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1823 case MOVETYPE_FOLLOW:
1824 case MOVETYPE_NOCLIP:
1825 case MOVETYPE_FLY_WORLDONLY:
1831 if (PRVM_serveredictedict(check, owner) == pusherprog)
1834 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1837 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1839 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1840 check->priv.server->waterposition_forceupdate = true;
1842 checkcontents = SV_GenericHitSuperContentsMask(check);
1844 // if the entity is standing on the pusher, it will definitely be moved
1845 // if the entity is not standing on the pusher, but is in the pusher's
1846 // final position, move it
1847 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1849 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);
1850 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1851 if (!trace.startsolid)
1853 //Con_Printf("- not in solid\n");
1858 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1859 //VectorClear(pivot);
1864 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1865 VectorAdd (org, pivot, org);
1866 org2[0] = DotProduct (org, forward);
1867 org2[1] = DotProduct (org, left);
1868 org2[2] = DotProduct (org, up);
1869 VectorSubtract (org2, org, move);
1870 VectorAdd (move, move1, move);
1873 VectorCopy (move1, move);
1875 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1877 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1878 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1879 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1881 // physics objects need better collisions than this code can do
1882 if (movetype == MOVETYPE_PHYSICS)
1884 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1885 SV_LinkEdict(check);
1886 SV_LinkEdict_TouchAreaGrid(check);
1890 // try moving the contacted entity
1891 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1892 if(!SV_PushEntity (&trace, check, move, true, true))
1894 // entity "check" got teleported
1895 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1896 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1897 continue; // pushed enough
1899 // FIXME: turn players specially
1900 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1901 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1902 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1904 // this trace.fraction < 1 check causes items to fall off of pushers
1905 // if they pass under or through a wall
1906 // the groundentity check causes items to fall off of ledges
1907 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1908 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1910 // if it is still inside the pusher, block
1911 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);
1912 if (trace.startsolid)
1915 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1917 // hack to invoke all necessary movement triggers
1919 if(!SV_PushEntity(&trace2, check, move2, true, true))
1921 // entity "check" got teleported
1928 // still inside pusher, so it's really blocked
1931 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1933 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1936 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1937 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1941 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1942 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1943 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1944 SV_LinkEdict(pusher);
1946 // move back any entities we already moved
1947 for (i = 0;i < num_moved;i++)
1949 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1950 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1951 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1955 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1956 if (PRVM_serveredictfunction(pusher, blocked))
1958 PRVM_serverglobalfloat(time) = sv.time;
1959 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1960 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1961 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1966 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1967 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1968 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1977 static void SV_Physics_Pusher (prvm_edict_t *ent)
1979 prvm_prog_t *prog = SVVM_prog;
1980 float thinktime, oldltime, movetime;
1982 oldltime = PRVM_serveredictfloat(ent, ltime);
1984 thinktime = PRVM_serveredictfloat(ent, nextthink);
1985 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1987 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1992 movetime = sv.frametime;
1995 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
1996 SV_PushMove (ent, movetime);
1998 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
2000 PRVM_serveredictfloat(ent, nextthink) = 0;
2001 PRVM_serverglobalfloat(time) = sv.time;
2002 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2003 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
2004 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2010 ===============================================================================
2014 ===============================================================================
2017 static float unstickoffsets[] =
2019 // poutting -/+z changes first as they are least weird
2034 typedef enum unstickresult_e
2042 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2044 prvm_prog_t *prog = SVVM_prog;
2047 // if not stuck in a bmodel, just return
2048 if (!SV_TestEntityPosition(ent, vec3_origin))
2049 return UNSTICK_GOOD;
2051 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2053 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2055 VectorCopy(unstickoffsets + i, offset);
2057 //SV_LinkEdict_TouchAreaGrid(ent);
2058 return UNSTICK_UNSTUCK;
2062 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2063 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2065 for(i = 2; i <= maxunstick; ++i)
2067 VectorClear(offset);
2069 if (!SV_TestEntityPosition(ent, offset))
2072 //SV_LinkEdict_TouchAreaGrid(ent);
2073 return UNSTICK_UNSTUCK;
2076 if (!SV_TestEntityPosition(ent, offset))
2079 //SV_LinkEdict_TouchAreaGrid(ent);
2080 return UNSTICK_UNSTUCK;
2084 return UNSTICK_STUCK;
2087 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2089 prvm_prog_t *prog = SVVM_prog;
2091 switch(SV_UnstickEntityReturnOffset(ent, offset))
2095 case UNSTICK_UNSTUCK:
2096 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]);
2099 if (developer_extra.integer)
2100 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2103 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2112 This is a big hack to try and fix the rare case of getting stuck in the world
2116 static void SV_CheckStuck (prvm_edict_t *ent)
2118 prvm_prog_t *prog = SVVM_prog;
2121 switch(SV_UnstickEntityReturnOffset(ent, offset))
2124 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2126 case UNSTICK_UNSTUCK:
2127 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]);
2130 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2131 if (!SV_TestEntityPosition(ent, offset))
2133 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)));
2135 //SV_LinkEdict_TouchAreaGrid(ent);
2138 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2141 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2151 static qboolean SV_CheckWater (prvm_edict_t *ent)
2153 prvm_prog_t *prog = SVVM_prog;
2155 int nNativeContents;
2158 point[0] = PRVM_serveredictvector(ent, origin)[0];
2159 point[1] = PRVM_serveredictvector(ent, origin)[1];
2160 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2162 // DRESK - Support for Entity Contents Transition Event
2163 // NOTE: Some logic needed to be slightly re-ordered
2164 // to not affect performance and allow for the feature.
2166 // Acquire Super Contents Prior to Resets
2167 cont = SV_PointSuperContents(point);
2168 // Acquire Native Contents Here
2169 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2171 // DRESK - Support for Entity Contents Transition Event
2172 if(PRVM_serveredictfloat(ent, watertype))
2173 // Entity did NOT Spawn; Check
2174 SV_CheckContentsTransition(ent, nNativeContents);
2177 PRVM_serveredictfloat(ent, waterlevel) = 0;
2178 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2179 cont = SV_PointSuperContents(point);
2180 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2182 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2183 PRVM_serveredictfloat(ent, waterlevel) = 1;
2184 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2185 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2187 PRVM_serveredictfloat(ent, waterlevel) = 2;
2188 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2189 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2190 PRVM_serveredictfloat(ent, waterlevel) = 3;
2194 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2203 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2205 prvm_prog_t *prog = SVVM_prog;
2207 vec3_t forward, into, side;
2209 AngleVectors (PRVM_serveredictvector(ent, v_angle), forward, NULL, NULL);
2210 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2212 // cut the tangential velocity
2213 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2214 VectorScale (stepnormal, i, into);
2215 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2216 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2217 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2223 =====================
2226 Player has come to a dead stop, possibly due to the problem with limited
2227 float precision at some angle joins in the BSP hull.
2229 Try fixing by pushing one pixel in each direction.
2231 This is a hack, but in the interest of good gameplay...
2232 ======================
2234 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2239 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2242 for (i=0 ; i<8 ; i++)
2244 // try pushing a little in an axial direction
2247 case 0: dir[0] = 2; dir[1] = 0; break;
2248 case 1: dir[0] = 0; dir[1] = 2; break;
2249 case 2: dir[0] = -2; dir[1] = 0; break;
2250 case 3: dir[0] = 0; dir[1] = -2; break;
2251 case 4: dir[0] = 2; dir[1] = 2; break;
2252 case 5: dir[0] = -2; dir[1] = 2; break;
2253 case 6: dir[0] = 2; dir[1] = -2; break;
2254 case 7: dir[0] = -2; dir[1] = -2; break;
2257 SV_PushEntity (&trace, ent, dir, false, true);
2259 // retry the original move
2260 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2261 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2262 PRVM_serveredictvector(ent, velocity)[2] = 0;
2263 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2265 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2266 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2268 Con_DPrint("TryUnstick - success.\n");
2272 // go back to the original pos and try again
2273 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2277 VectorClear (PRVM_serveredictvector(ent, velocity));
2278 Con_DPrint("TryUnstick - failure.\n");
2284 =====================
2287 Only used by players
2288 ======================
2290 static void SV_WalkMove (prvm_edict_t *ent)
2292 prvm_prog_t *prog = SVVM_prog;
2295 //int originalmove_clip;
2296 int originalmove_flags;
2297 int originalmove_groundentity;
2298 int hitsupercontentsmask;
2300 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2301 trace_t downtrace, trace;
2302 qboolean applygravity;
2304 // if frametime is 0 (due to client sending the same timestamp twice),
2306 if (sv.frametime <= 0)
2309 if (sv_gameplayfix_unstickplayers.integer)
2310 SV_CheckStuck (ent);
2312 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2314 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2316 SV_CheckVelocity(ent);
2318 // do a regular slide move unless it looks like you ran into a step
2319 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2321 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2322 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2324 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2326 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2329 // only try this if there was no floor in the way in the trace (no,
2330 // this check seems to be not REALLY necessary, because if clip & 1,
2331 // our trace will hit that thing too)
2332 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2333 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2334 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2335 type = MOVE_MISSILE;
2336 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2337 type = MOVE_WORLDONLY;
2338 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2339 type = MOVE_NOMONSTERS; // only clip against bmodels
2342 trace = SV_TraceBox(upmove, PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2343 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2344 clip |= 1; // but we HAVE found a floor
2347 // if the move did not hit the ground at any point, we're not on ground
2349 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2351 SV_CheckVelocity(ent);
2353 SV_LinkEdict_TouchAreaGrid(ent);
2355 if(clip & 8) // teleport
2358 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2361 if (sv_nostep.integer)
2364 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2365 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2366 //originalmove_clip = clip;
2367 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2368 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2370 // if move didn't block on a step, return
2373 // if move was not trying to move into the step, return
2374 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2377 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2379 // return if gibbed by a trigger
2380 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2383 // only step up while jumping if that is enabled
2384 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2385 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2389 // try moving up and forward to go up a step
2390 // back to start pos
2391 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2392 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2395 VectorClear (upmove);
2396 upmove[2] = sv_stepheight.value;
2397 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2399 // we got teleported when upstepping... must abort the move
2404 PRVM_serveredictvector(ent, velocity)[2] = 0;
2405 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2406 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2409 // we got teleported when upstepping... must abort the move
2410 // note that z velocity handling may not be what QC expects here, but we cannot help it
2414 SV_CheckVelocity(ent);
2416 SV_LinkEdict_TouchAreaGrid(ent);
2418 // check for stuckness, possibly due to the limited precision of floats
2419 // in the clipping hulls
2421 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2422 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2424 //Con_Printf("wall\n");
2425 // stepping up didn't make any progress, revert to original move
2426 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2427 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2428 //clip = originalmove_clip;
2429 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2430 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2431 // now try to unstick if needed
2432 //clip = SV_TryUnstick (ent, oldvel);
2436 //Con_Printf("step - ");
2438 // extra friction based on view angle
2439 if (clip & 2 && sv_wallfriction.integer)
2440 SV_WallFriction (ent, stepnormal);
2442 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2443 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))
2447 VectorClear (downmove);
2448 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2449 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2451 // we got teleported when downstepping... must abort the move
2455 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2457 // this has been disabled so that you can't jump when you are stepping
2458 // up while already jumping (also known as the Quake2 double jump bug)
2460 // LordHavoc: disabled this check so you can walk on monsters/players
2461 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2463 //Con_Printf("onground\n");
2464 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2465 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2471 //Con_Printf("slope\n");
2472 // if the push down didn't end up on good ground, use the move without
2473 // the step up. This happens near wall / slope combinations, and can
2474 // cause the player to hop up higher on a slope too steep to climb
2475 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2476 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2477 //clip = originalmove_clip;
2478 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2479 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2482 SV_CheckVelocity(ent);
2484 SV_LinkEdict_TouchAreaGrid(ent);
2487 //============================================================================
2493 Entities that are "stuck" to another entity
2496 static void SV_Physics_Follow (prvm_edict_t *ent)
2498 prvm_prog_t *prog = SVVM_prog;
2499 vec3_t vf, vr, vu, angles, v;
2503 if (!SV_RunThink (ent))
2506 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2507 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2508 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])
2510 // quick case for no rotation
2511 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2515 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2516 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2517 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2518 AngleVectors (angles, vf, vr, vu);
2519 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];
2520 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];
2521 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];
2522 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2523 angles[1] = PRVM_serveredictvector(e, angles)[1];
2524 angles[2] = PRVM_serveredictvector(e, angles)[2];
2525 AngleVectors (angles, vf, vr, vu);
2526 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2527 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2528 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2530 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2532 //SV_LinkEdict_TouchAreaGrid(ent);
2536 ==============================================================================
2540 ==============================================================================
2545 SV_CheckWaterTransition
2549 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2551 prvm_prog_t *prog = SVVM_prog;
2552 // 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
2554 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(PRVM_serveredictvector(ent, origin)));
2555 if (!PRVM_serveredictfloat(ent, watertype))
2557 // just spawned here
2558 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2560 PRVM_serveredictfloat(ent, watertype) = cont;
2561 PRVM_serveredictfloat(ent, waterlevel) = 1;
2565 // DRESK - Support for Entity Contents Transition Event
2566 // NOTE: Call here BEFORE updating the watertype below,
2567 // and suppress watersplash sound if a valid function
2568 // call was made to allow for custom "splash" sounds.
2569 else if( !SV_CheckContentsTransition(ent, cont) )
2570 { // Contents Transition Function Invalid; Potentially Play Water Sound
2571 // check if the entity crossed into or out of water
2572 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2573 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2576 if (cont <= CONTENTS_WATER)
2578 PRVM_serveredictfloat(ent, watertype) = cont;
2579 PRVM_serveredictfloat(ent, waterlevel) = 1;
2583 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2584 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2592 Toss, bounce, and fly movement. When onground, do nothing.
2596 void SV_Physics_Toss (prvm_edict_t *ent)
2598 prvm_prog_t *prog = SVVM_prog;
2603 prvm_edict_t *groundentity;
2604 float d, ent_gravity;
2608 // if onground, return without moving
2609 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2611 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2612 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2614 // don't stick to ground if onground and moving upward
2615 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2617 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2619 // we can trust FL_ONGROUND if groundentity is world because it never moves
2622 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2624 // if ent was supported by a brush model on previous frame,
2625 // and groundentity is now freed, set groundentity to 0 (world)
2626 // which leaves it suspended in the air
2627 PRVM_serveredictedict(ent, groundentity) = 0;
2628 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2631 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2633 // don't slide if still touching the groundentity
2637 ent->priv.server->suspendedinairflag = false;
2639 SV_CheckVelocity (ent);
2642 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2643 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2646 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2648 movetime = sv.frametime;
2649 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2652 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2653 if(!SV_PushEntity(&trace, ent, move, true, true))
2654 return; // teleported
2655 if (ent->priv.server->free)
2657 if (trace.bmodelstartsolid)
2659 // try to unstick the entity
2660 if (sv_gameplayfix_unstickentities.integer)
2661 SV_UnstickEntity(ent);
2662 if(!SV_PushEntity(&trace, ent, move, false, true))
2663 return; // teleported
2664 if (ent->priv.server->free)
2667 if (trace.fraction == 1)
2669 movetime *= 1 - min(1, trace.fraction);
2670 switch((int)PRVM_serveredictfloat(ent, movetype))
2672 case MOVETYPE_BOUNCEMISSILE:
2673 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2675 bouncefactor = 1.0f;
2677 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2678 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2679 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2682 case MOVETYPE_BOUNCE:
2683 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2685 bouncefactor = 0.5f;
2687 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2689 bouncestop = 60.0f / 800.0f;
2691 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2692 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2695 // LordHavoc: fixed grenades not bouncing when fired down a slope
2696 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2697 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2699 d = PRVM_serveredictvector(ent, velocity)[2];
2700 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2702 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2703 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2704 VectorClear(PRVM_serveredictvector(ent, velocity));
2705 VectorClear(PRVM_serveredictvector(ent, avelocity));
2710 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2711 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2716 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2717 if (trace.plane.normal[2] > 0.7)
2719 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2720 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2721 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2722 ent->priv.server->suspendedinairflag = true;
2723 VectorClear (PRVM_serveredictvector(ent, velocity));
2724 VectorClear (PRVM_serveredictvector(ent, avelocity));
2727 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2733 // check for in water
2734 SV_CheckWaterTransition (ent);
2738 ===============================================================================
2742 ===============================================================================
2749 Monsters freefall when they don't have a ground entity, otherwise
2750 all movement is done with discrete steps.
2752 This is also used for objects that have become still on the ground, but
2753 will fall if the floor is pulled out from under them.
2756 static void SV_Physics_Step (prvm_edict_t *ent)
2758 prvm_prog_t *prog = SVVM_prog;
2759 int flags = (int)PRVM_serveredictfloat(ent, flags);
2762 // Backup Velocity in the event that movetypesteplandevent is called,
2763 // to provide a parameter with the entity's velocity at impact.
2764 vec3_t backupVelocity;
2765 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2766 // don't fall at all if fly/swim
2767 if (!(flags & (FL_FLY | FL_SWIM)))
2769 if (flags & FL_ONGROUND)
2771 // freefall if onground and moving upward
2772 // freefall if not standing on a world surface (it may be a lift or trap door)
2773 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2775 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2776 SV_CheckVelocity(ent);
2777 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2779 SV_LinkEdict_TouchAreaGrid(ent);
2780 ent->priv.server->waterposition_forceupdate = true;
2785 // freefall if not onground
2786 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2788 SV_CheckVelocity(ent);
2789 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2791 SV_LinkEdict_TouchAreaGrid(ent);
2794 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2796 // DRESK - Check for Entity Land Event Function
2797 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2798 { // Valid Function; Execute
2799 // Prepare Parameters
2800 // Assign Velocity at Impact
2801 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2802 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2803 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2805 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2807 PRVM_serverglobalfloat(time) = sv.time;
2808 // Execute VM Function
2809 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2812 // Check for Engine Landing Sound
2813 if(sv_sound_land.string)
2814 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2816 ent->priv.server->waterposition_forceupdate = true;
2821 if (!SV_RunThink(ent))
2824 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2826 ent->priv.server->waterposition_forceupdate = false;
2827 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2828 SV_CheckWaterTransition(ent);
2832 //============================================================================
2834 static void SV_Physics_Entity (prvm_edict_t *ent)
2836 prvm_prog_t *prog = SVVM_prog;
2837 // don't run think/move on newly spawned projectiles as it messes up
2838 // movement interpolation and rocket trails, and is inconsistent with
2839 // respect to entities spawned in the same frame
2840 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2841 // but if it spawns a lower numbered ent, it doesn't - this never moves
2842 // ents in the first frame regardless)
2843 qboolean runmove = ent->priv.server->move;
2844 ent->priv.server->move = true;
2845 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2847 switch ((int) PRVM_serveredictfloat(ent, movetype))
2850 case MOVETYPE_FAKEPUSH:
2851 SV_Physics_Pusher (ent);
2854 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2855 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2858 case MOVETYPE_FOLLOW:
2859 SV_Physics_Follow (ent);
2861 case MOVETYPE_NOCLIP:
2862 if (SV_RunThink(ent))
2865 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2866 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2871 SV_Physics_Step (ent);
2874 if (SV_RunThink (ent))
2878 case MOVETYPE_BOUNCE:
2879 case MOVETYPE_BOUNCEMISSILE:
2880 case MOVETYPE_FLYMISSILE:
2882 case MOVETYPE_FLY_WORLDONLY:
2884 if (SV_RunThink (ent))
2885 SV_Physics_Toss (ent);
2887 case MOVETYPE_PHYSICS:
2888 if (SV_RunThink(ent))
2891 SV_LinkEdict_TouchAreaGrid(ent);
2895 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2900 void SV_Physics_ClientMove(void)
2902 prvm_prog_t *prog = SVVM_prog;
2904 ent = host_client->edict;
2906 // call player physics, this needs the proper frametime
2907 PRVM_serverglobalfloat(frametime) = sv.frametime;
2910 // call standard client pre-think, with frametime = 0
2911 PRVM_serverglobalfloat(time) = sv.time;
2912 PRVM_serverglobalfloat(frametime) = 0;
2913 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2914 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2915 PRVM_serverglobalfloat(frametime) = sv.frametime;
2917 // make sure the velocity is sane (not a NaN)
2918 SV_CheckVelocity(ent);
2920 // perform MOVETYPE_WALK behavior
2923 // call standard player post-think, with frametime = 0
2924 PRVM_serverglobalfloat(time) = sv.time;
2925 PRVM_serverglobalfloat(frametime) = 0;
2926 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2927 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2928 PRVM_serverglobalfloat(frametime) = sv.frametime;
2930 if(PRVM_serveredictfloat(ent, fixangle))
2932 // angle fixing was requested by physics code...
2933 // so store the current angles for later use
2934 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2935 host_client->fixangle_angles_set = TRUE;
2937 // and clear fixangle for the next frame
2938 PRVM_serveredictfloat(ent, fixangle) = 0;
2942 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2944 prvm_prog_t *prog = SVVM_prog;
2945 // don't do physics on disconnected clients, FrikBot relies on this
2946 if (!host_client->spawned)
2949 // make sure the velocity is sane (not a NaN)
2950 SV_CheckVelocity(ent);
2952 // don't run physics here if running asynchronously
2953 if (host_client->clmovement_inputtimeout <= 0)
2956 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2959 // make sure the velocity is still sane (not a NaN)
2960 SV_CheckVelocity(ent);
2962 // call standard client pre-think
2963 PRVM_serverglobalfloat(time) = sv.time;
2964 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2965 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2967 // make sure the velocity is still sane (not a NaN)
2968 SV_CheckVelocity(ent);
2971 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2973 prvm_prog_t *prog = SVVM_prog;
2974 // don't do physics on disconnected clients, FrikBot relies on this
2975 if (!host_client->spawned)
2978 // make sure the velocity is sane (not a NaN)
2979 SV_CheckVelocity(ent);
2981 // call standard player post-think
2982 PRVM_serverglobalfloat(time) = sv.time;
2983 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2984 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2986 // make sure the velocity is still sane (not a NaN)
2987 SV_CheckVelocity(ent);
2989 if(PRVM_serveredictfloat(ent, fixangle))
2991 // angle fixing was requested by physics code...
2992 // so store the current angles for later use
2993 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2994 host_client->fixangle_angles_set = TRUE;
2996 // and clear fixangle for the next frame
2997 PRVM_serveredictfloat(ent, fixangle) = 0;
3000 // decrement the countdown variable used to decide when to go back to
3001 // synchronous physics
3002 if (host_client->clmovement_inputtimeout > sv.frametime)
3003 host_client->clmovement_inputtimeout -= sv.frametime;
3005 host_client->clmovement_inputtimeout = 0;
3008 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3010 prvm_prog_t *prog = SVVM_prog;
3011 // don't do physics on disconnected clients, FrikBot relies on this
3012 if (!host_client->spawned)
3014 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3018 // make sure the velocity is sane (not a NaN)
3019 SV_CheckVelocity(ent);
3021 switch ((int) PRVM_serveredictfloat(ent, movetype))
3024 case MOVETYPE_FAKEPUSH:
3025 SV_Physics_Pusher (ent);
3028 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3029 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3032 case MOVETYPE_FOLLOW:
3033 SV_Physics_Follow (ent);
3035 case MOVETYPE_NOCLIP:
3038 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3039 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3042 SV_Physics_Step (ent);
3046 // don't run physics here if running asynchronously
3047 if (host_client->clmovement_inputtimeout <= 0)
3051 case MOVETYPE_BOUNCE:
3052 case MOVETYPE_BOUNCEMISSILE:
3053 case MOVETYPE_FLYMISSILE:
3056 SV_Physics_Toss (ent);
3059 case MOVETYPE_FLY_WORLDONLY:
3063 case MOVETYPE_PHYSICS:
3067 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3071 SV_CheckVelocity (ent);
3074 SV_LinkEdict_TouchAreaGrid(ent);
3076 SV_CheckVelocity (ent);
3085 void SV_Physics (void)
3087 prvm_prog_t *prog = SVVM_prog;
3091 // let the progs know that a new frame has started
3092 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3093 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3094 PRVM_serverglobalfloat(time) = sv.time;
3095 PRVM_serverglobalfloat(frametime) = sv.frametime;
3096 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3098 // run physics engine
3099 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3102 // treat each object in turn
3105 // if force_retouch, relink all the entities
3106 if (PRVM_serverglobalfloat(force_retouch) > 0)
3107 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3108 if (!ent->priv.server->free)
3109 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3111 if (sv_gameplayfix_consistentplayerprethink.integer)
3113 // run physics on the client entities in 3 stages
3114 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3115 if (!ent->priv.server->free)
3116 SV_Physics_ClientEntity_PreThink(ent);
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(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_PostThink(ent);
3128 // run physics on the client entities
3129 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3131 if (!ent->priv.server->free)
3133 SV_Physics_ClientEntity_PreThink(ent);
3134 SV_Physics_ClientEntity(ent);
3135 SV_Physics_ClientEntity_PostThink(ent);
3140 // run physics on all the non-client entities
3141 if (!sv_freezenonclients.integer)
3143 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3144 if (!ent->priv.server->free)
3145 SV_Physics_Entity(ent);
3146 // make a second pass to see if any ents spawned this frame and make
3147 // sure they run their move/think
3148 if (sv_gameplayfix_delayprojectiles.integer < 0)
3149 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3150 if (!ent->priv.server->move && !ent->priv.server->free)
3151 SV_Physics_Entity(ent);
3154 if (PRVM_serverglobalfloat(force_retouch) > 0)
3155 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3157 // LordHavoc: endframe support
3158 if (PRVM_serverfunction(EndFrame))
3160 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3161 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3162 PRVM_serverglobalfloat(time) = sv.time;
3163 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3166 // decrement prog->num_edicts if the highest number entities died
3167 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3169 if (!sv_freezenonclients.integer)
3170 sv.time += sv.frametime;