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;
1603 vec3_t original, original_velocity;
1607 solid = (int)PRVM_serveredictfloat(ent, solid);
1608 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1609 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1610 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1612 // move start position out of solids
1613 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1615 SV_NudgeOutOfSolid(ent);
1618 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1619 VectorAdd(start, push, end);
1621 if (movetype == MOVETYPE_FLYMISSILE)
1622 type = MOVE_MISSILE;
1623 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1624 type = MOVE_WORLDONLY;
1625 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1626 type = MOVE_NOMONSTERS; // only clip against bmodels
1630 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1631 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1634 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1636 VectorCopy(PRVM_serveredictvector(ent, origin), original);
1637 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1642 if(!trace->startsolid)
1643 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)
1645 Con_Printf("something eeeeevil happened\n");
1650 SV_LinkEdict_TouchAreaGrid(ent);
1652 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))))
1653 SV_Impact (ent, trace);
1655 return VectorCompare(PRVM_serveredictvector(ent, origin), original) && VectorCompare(PRVM_serveredictvector(ent, velocity), original_velocity);
1665 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1667 prvm_prog_t *prog = SVVM_prog;
1669 int pusherowner, pusherprog;
1672 float savesolid, movetime2, pushltime;
1673 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1675 int numcheckentities;
1676 static prvm_edict_t *checkentities[MAX_EDICTS];
1677 dp_model_t *pushermodel;
1678 trace_t trace, trace2;
1679 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1680 static unsigned short moved_edicts[MAX_EDICTS];
1683 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])
1685 PRVM_serveredictfloat(pusher, ltime) += movetime;
1689 switch ((int) PRVM_serveredictfloat(pusher, solid))
1691 // LordHavoc: valid pusher types
1694 case SOLID_SLIDEBOX:
1695 case SOLID_CORPSE: // LordHavoc: this would be weird...
1697 // LordHavoc: no collisions
1700 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1701 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1702 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1703 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1704 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1705 PRVM_serveredictfloat(pusher, ltime) += movetime;
1706 SV_LinkEdict(pusher);
1709 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1712 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1713 if (index < 1 || index >= MAX_MODELS)
1715 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1718 pushermodel = SV_GetModelByIndex(index);
1719 pusherowner = PRVM_serveredictedict(pusher, owner);
1720 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1722 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1724 movetime2 = movetime;
1725 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1726 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1727 if (moveangle[0] || moveangle[2])
1729 for (i = 0;i < 3;i++)
1733 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1734 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1738 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1739 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1743 else if (moveangle[1])
1745 for (i = 0;i < 3;i++)
1749 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1750 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1754 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1755 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1761 for (i = 0;i < 3;i++)
1765 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1766 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1770 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1771 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1776 VectorNegate (moveangle, a);
1777 AngleVectorsFLU (a, forward, left, up);
1779 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1780 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1781 pushltime = PRVM_serveredictfloat(pusher, ltime);
1783 // move the pusher to its final position
1785 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1786 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1787 PRVM_serveredictfloat(pusher, ltime) += movetime;
1788 SV_LinkEdict(pusher);
1790 pushermodel = SV_GetModelFromEdict(pusher);
1791 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);
1792 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1794 savesolid = PRVM_serveredictfloat(pusher, solid);
1796 // see if any solid entities are inside the final position
1799 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1800 numcheckentities = 0;
1801 else // MOVETYPE_PUSH
1802 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1803 for (e = 0;e < numcheckentities;e++)
1805 prvm_edict_t *check = checkentities[e];
1806 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1811 case MOVETYPE_FOLLOW:
1812 case MOVETYPE_NOCLIP:
1813 case MOVETYPE_FLY_WORLDONLY:
1819 if (PRVM_serveredictedict(check, owner) == pusherprog)
1822 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1825 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1827 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1828 check->priv.server->waterposition_forceupdate = true;
1830 checkcontents = SV_GenericHitSuperContentsMask(check);
1832 // if the entity is standing on the pusher, it will definitely be moved
1833 // if the entity is not standing on the pusher, but is in the pusher's
1834 // final position, move it
1835 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1837 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);
1838 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1839 if (!trace.startsolid)
1841 //Con_Printf("- not in solid\n");
1846 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1847 //VectorClear(pivot);
1852 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1853 VectorAdd (org, pivot, org);
1854 org2[0] = DotProduct (org, forward);
1855 org2[1] = DotProduct (org, left);
1856 org2[2] = DotProduct (org, up);
1857 VectorSubtract (org2, org, move);
1858 VectorAdd (move, move1, move);
1861 VectorCopy (move1, move);
1863 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1865 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1866 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1867 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1869 // physics objects need better collisions than this code can do
1870 if (movetype == MOVETYPE_PHYSICS)
1872 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1873 SV_LinkEdict(check);
1874 SV_LinkEdict_TouchAreaGrid(check);
1878 // try moving the contacted entity
1879 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1880 if(!SV_PushEntity (&trace, check, move, true, true))
1882 // entity "check" got teleported
1883 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1884 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1885 continue; // pushed enough
1887 // FIXME: turn players specially
1888 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1889 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1890 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1892 // this trace.fraction < 1 check causes items to fall off of pushers
1893 // if they pass under or through a wall
1894 // the groundentity check causes items to fall off of ledges
1895 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1896 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1898 // if it is still inside the pusher, block
1899 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);
1900 if (trace.startsolid)
1903 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1905 // hack to invoke all necessary movement triggers
1907 if(!SV_PushEntity(&trace2, check, move2, true, true))
1909 // entity "check" got teleported
1916 // still inside pusher, so it's really blocked
1919 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1921 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1924 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1925 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1929 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1930 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1931 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1932 SV_LinkEdict(pusher);
1934 // move back any entities we already moved
1935 for (i = 0;i < num_moved;i++)
1937 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1938 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1939 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1943 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1944 if (PRVM_serveredictfunction(pusher, blocked))
1946 PRVM_serverglobalfloat(time) = sv.time;
1947 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1948 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1949 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1954 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1955 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1956 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1965 static void SV_Physics_Pusher (prvm_edict_t *ent)
1967 prvm_prog_t *prog = SVVM_prog;
1968 float thinktime, oldltime, movetime;
1970 oldltime = PRVM_serveredictfloat(ent, ltime);
1972 thinktime = PRVM_serveredictfloat(ent, nextthink);
1973 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1975 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1980 movetime = sv.frametime;
1983 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
1984 SV_PushMove (ent, movetime);
1986 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
1988 PRVM_serveredictfloat(ent, nextthink) = 0;
1989 PRVM_serverglobalfloat(time) = sv.time;
1990 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1991 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1992 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1998 ===============================================================================
2002 ===============================================================================
2005 static float unstickoffsets[] =
2007 // poutting -/+z changes first as they are least weird
2022 typedef enum unstickresult_e
2030 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2032 prvm_prog_t *prog = SVVM_prog;
2035 // if not stuck in a bmodel, just return
2036 if (!SV_TestEntityPosition(ent, vec3_origin))
2037 return UNSTICK_GOOD;
2039 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2041 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2043 VectorCopy(unstickoffsets + i, offset);
2045 //SV_LinkEdict_TouchAreaGrid(ent);
2046 return UNSTICK_UNSTUCK;
2050 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2051 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2053 for(i = 2; i <= maxunstick; ++i)
2055 VectorClear(offset);
2057 if (!SV_TestEntityPosition(ent, offset))
2060 //SV_LinkEdict_TouchAreaGrid(ent);
2061 return UNSTICK_UNSTUCK;
2064 if (!SV_TestEntityPosition(ent, offset))
2067 //SV_LinkEdict_TouchAreaGrid(ent);
2068 return UNSTICK_UNSTUCK;
2072 return UNSTICK_STUCK;
2075 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2077 prvm_prog_t *prog = SVVM_prog;
2079 switch(SV_UnstickEntityReturnOffset(ent, offset))
2083 case UNSTICK_UNSTUCK:
2084 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]);
2087 if (developer_extra.integer)
2088 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2091 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2100 This is a big hack to try and fix the rare case of getting stuck in the world
2104 static void SV_CheckStuck (prvm_edict_t *ent)
2106 prvm_prog_t *prog = SVVM_prog;
2109 switch(SV_UnstickEntityReturnOffset(ent, offset))
2112 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2114 case UNSTICK_UNSTUCK:
2115 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]);
2118 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2119 if (!SV_TestEntityPosition(ent, offset))
2121 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)));
2123 //SV_LinkEdict_TouchAreaGrid(ent);
2126 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2129 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2139 static qboolean SV_CheckWater (prvm_edict_t *ent)
2141 prvm_prog_t *prog = SVVM_prog;
2143 int nNativeContents;
2146 point[0] = PRVM_serveredictvector(ent, origin)[0];
2147 point[1] = PRVM_serveredictvector(ent, origin)[1];
2148 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2150 // DRESK - Support for Entity Contents Transition Event
2151 // NOTE: Some logic needed to be slightly re-ordered
2152 // to not affect performance and allow for the feature.
2154 // Acquire Super Contents Prior to Resets
2155 cont = SV_PointSuperContents(point);
2156 // Acquire Native Contents Here
2157 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2159 // DRESK - Support for Entity Contents Transition Event
2160 if(PRVM_serveredictfloat(ent, watertype))
2161 // Entity did NOT Spawn; Check
2162 SV_CheckContentsTransition(ent, nNativeContents);
2165 PRVM_serveredictfloat(ent, waterlevel) = 0;
2166 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2167 cont = SV_PointSuperContents(point);
2168 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2170 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2171 PRVM_serveredictfloat(ent, waterlevel) = 1;
2172 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2173 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2175 PRVM_serveredictfloat(ent, waterlevel) = 2;
2176 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2177 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2178 PRVM_serveredictfloat(ent, waterlevel) = 3;
2182 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2191 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2193 prvm_prog_t *prog = SVVM_prog;
2195 vec3_t forward, into, side;
2197 AngleVectors (PRVM_serveredictvector(ent, v_angle), forward, NULL, NULL);
2198 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2200 // cut the tangential velocity
2201 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2202 VectorScale (stepnormal, i, into);
2203 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2204 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2205 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2211 =====================
2214 Player has come to a dead stop, possibly due to the problem with limited
2215 float precision at some angle joins in the BSP hull.
2217 Try fixing by pushing one pixel in each direction.
2219 This is a hack, but in the interest of good gameplay...
2220 ======================
2222 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2227 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2230 for (i=0 ; i<8 ; i++)
2232 // try pushing a little in an axial direction
2235 case 0: dir[0] = 2; dir[1] = 0; break;
2236 case 1: dir[0] = 0; dir[1] = 2; break;
2237 case 2: dir[0] = -2; dir[1] = 0; break;
2238 case 3: dir[0] = 0; dir[1] = -2; break;
2239 case 4: dir[0] = 2; dir[1] = 2; break;
2240 case 5: dir[0] = -2; dir[1] = 2; break;
2241 case 6: dir[0] = 2; dir[1] = -2; break;
2242 case 7: dir[0] = -2; dir[1] = -2; break;
2245 SV_PushEntity (&trace, ent, dir, false, true);
2247 // retry the original move
2248 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2249 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2250 PRVM_serveredictvector(ent, velocity)[2] = 0;
2251 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2253 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2254 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2256 Con_DPrint("TryUnstick - success.\n");
2260 // go back to the original pos and try again
2261 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2265 VectorClear (PRVM_serveredictvector(ent, velocity));
2266 Con_DPrint("TryUnstick - failure.\n");
2272 =====================
2275 Only used by players
2276 ======================
2278 static void SV_WalkMove (prvm_edict_t *ent)
2280 prvm_prog_t *prog = SVVM_prog;
2283 //int originalmove_clip;
2284 int originalmove_flags;
2285 int originalmove_groundentity;
2286 int hitsupercontentsmask;
2288 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2289 trace_t downtrace, trace;
2290 qboolean applygravity;
2292 // if frametime is 0 (due to client sending the same timestamp twice),
2294 if (sv.frametime <= 0)
2297 if (sv_gameplayfix_unstickplayers.integer)
2298 SV_CheckStuck (ent);
2300 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2302 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2304 SV_CheckVelocity(ent);
2306 // do a regular slide move unless it looks like you ran into a step
2307 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2309 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2310 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2312 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2314 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2317 // only try this if there was no floor in the way in the trace (no,
2318 // this check seems to be not REALLY necessary, because if clip & 1,
2319 // our trace will hit that thing too)
2320 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2321 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2322 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2323 type = MOVE_MISSILE;
2324 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2325 type = MOVE_WORLDONLY;
2326 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2327 type = MOVE_NOMONSTERS; // only clip against bmodels
2330 trace = SV_TraceBox(upmove, PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2331 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2332 clip |= 1; // but we HAVE found a floor
2335 // if the move did not hit the ground at any point, we're not on ground
2337 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2339 SV_CheckVelocity(ent);
2341 SV_LinkEdict_TouchAreaGrid(ent);
2343 if(clip & 8) // teleport
2346 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2349 if (sv_nostep.integer)
2352 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2353 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2354 //originalmove_clip = clip;
2355 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2356 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2358 // if move didn't block on a step, return
2361 // if move was not trying to move into the step, return
2362 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2365 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2367 // return if gibbed by a trigger
2368 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2371 // only step up while jumping if that is enabled
2372 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2373 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2377 // try moving up and forward to go up a step
2378 // back to start pos
2379 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2380 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2383 VectorClear (upmove);
2384 upmove[2] = sv_stepheight.value;
2385 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2387 // we got teleported when upstepping... must abort the move
2392 PRVM_serveredictvector(ent, velocity)[2] = 0;
2393 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2394 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2397 // we got teleported when upstepping... must abort the move
2398 // note that z velocity handling may not be what QC expects here, but we cannot help it
2402 SV_CheckVelocity(ent);
2404 SV_LinkEdict_TouchAreaGrid(ent);
2406 // check for stuckness, possibly due to the limited precision of floats
2407 // in the clipping hulls
2409 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2410 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2412 //Con_Printf("wall\n");
2413 // stepping up didn't make any progress, revert to original move
2414 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2415 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2416 //clip = originalmove_clip;
2417 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2418 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2419 // now try to unstick if needed
2420 //clip = SV_TryUnstick (ent, oldvel);
2424 //Con_Printf("step - ");
2426 // extra friction based on view angle
2427 if (clip & 2 && sv_wallfriction.integer)
2428 SV_WallFriction (ent, stepnormal);
2430 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2431 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))
2435 VectorClear (downmove);
2436 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2437 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2439 // we got teleported when downstepping... must abort the move
2443 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2445 // this has been disabled so that you can't jump when you are stepping
2446 // up while already jumping (also known as the Quake2 double jump bug)
2448 // LordHavoc: disabled this check so you can walk on monsters/players
2449 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2451 //Con_Printf("onground\n");
2452 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2453 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2459 //Con_Printf("slope\n");
2460 // if the push down didn't end up on good ground, use the move without
2461 // the step up. This happens near wall / slope combinations, and can
2462 // cause the player to hop up higher on a slope too steep to climb
2463 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2464 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2465 //clip = originalmove_clip;
2466 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2467 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2470 SV_CheckVelocity(ent);
2472 SV_LinkEdict_TouchAreaGrid(ent);
2475 //============================================================================
2481 Entities that are "stuck" to another entity
2484 static void SV_Physics_Follow (prvm_edict_t *ent)
2486 prvm_prog_t *prog = SVVM_prog;
2487 vec3_t vf, vr, vu, angles, v;
2491 if (!SV_RunThink (ent))
2494 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2495 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2496 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])
2498 // quick case for no rotation
2499 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2503 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2504 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2505 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2506 AngleVectors (angles, vf, vr, vu);
2507 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];
2508 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];
2509 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];
2510 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2511 angles[1] = PRVM_serveredictvector(e, angles)[1];
2512 angles[2] = PRVM_serveredictvector(e, angles)[2];
2513 AngleVectors (angles, vf, vr, vu);
2514 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2515 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2516 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2518 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2520 //SV_LinkEdict_TouchAreaGrid(ent);
2524 ==============================================================================
2528 ==============================================================================
2533 SV_CheckWaterTransition
2537 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2539 prvm_prog_t *prog = SVVM_prog;
2540 // 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
2542 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(PRVM_serveredictvector(ent, origin)));
2543 if (!PRVM_serveredictfloat(ent, watertype))
2545 // just spawned here
2546 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2548 PRVM_serveredictfloat(ent, watertype) = cont;
2549 PRVM_serveredictfloat(ent, waterlevel) = 1;
2553 // DRESK - Support for Entity Contents Transition Event
2554 // NOTE: Call here BEFORE updating the watertype below,
2555 // and suppress watersplash sound if a valid function
2556 // call was made to allow for custom "splash" sounds.
2557 else if( !SV_CheckContentsTransition(ent, cont) )
2558 { // Contents Transition Function Invalid; Potentially Play Water Sound
2559 // check if the entity crossed into or out of water
2560 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2561 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2564 if (cont <= CONTENTS_WATER)
2566 PRVM_serveredictfloat(ent, watertype) = cont;
2567 PRVM_serveredictfloat(ent, waterlevel) = 1;
2571 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2572 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2580 Toss, bounce, and fly movement. When onground, do nothing.
2584 void SV_Physics_Toss (prvm_edict_t *ent)
2586 prvm_prog_t *prog = SVVM_prog;
2591 prvm_edict_t *groundentity;
2593 // if onground, return without moving
2594 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2596 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2597 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2599 // don't stick to ground if onground and moving upward
2600 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2602 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2604 // we can trust FL_ONGROUND if groundentity is world because it never moves
2607 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2609 // if ent was supported by a brush model on previous frame,
2610 // and groundentity is now freed, set groundentity to 0 (world)
2611 // which leaves it suspended in the air
2612 PRVM_serveredictedict(ent, groundentity) = 0;
2613 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2616 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2618 // don't slide if still touching the groundentity
2622 ent->priv.server->suspendedinairflag = false;
2624 SV_CheckVelocity (ent);
2627 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2628 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2631 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2633 movetime = sv.frametime;
2634 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2637 VectorScale (PRVM_serveredictvector(ent, velocity), movetime, move);
2638 if(!SV_PushEntity (&trace, ent, move, true, true))
2639 return; // teleported
2640 if (ent->priv.server->free)
2642 if (trace.bmodelstartsolid)
2644 // try to unstick the entity
2645 if (sv_gameplayfix_unstickentities.integer)
2646 SV_UnstickEntity(ent);
2647 if(!SV_PushEntity (&trace, ent, move, false, true))
2648 return; // teleported
2649 if (ent->priv.server->free)
2652 if (trace.fraction == 1)
2654 movetime *= 1 - min(1, trace.fraction);
2655 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCEMISSILE)
2658 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2660 bouncefactor = 1.0f;
2662 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2663 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2665 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2667 float d, ent_gravity;
2671 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2673 bouncefactor = 0.5f;
2675 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2677 bouncestop = 60.0f / 800.0f;
2679 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2680 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2683 // LordHavoc: fixed grenades not bouncing when fired down a slope
2684 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2686 d = DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity));
2687 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2689 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2690 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2691 VectorClear (PRVM_serveredictvector(ent, velocity));
2692 VectorClear (PRVM_serveredictvector(ent, avelocity));
2695 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2699 if (trace.plane.normal[2] > 0.7 && PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * bouncestop * ent_gravity)
2701 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2702 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2703 VectorClear (PRVM_serveredictvector(ent, velocity));
2704 VectorClear (PRVM_serveredictvector(ent, avelocity));
2707 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2712 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2713 if (trace.plane.normal[2] > 0.7)
2715 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2716 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2717 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2718 ent->priv.server->suspendedinairflag = true;
2719 VectorClear (PRVM_serveredictvector(ent, velocity));
2720 VectorClear (PRVM_serveredictvector(ent, avelocity));
2723 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2725 if (!sv_gameplayfix_slidemoveprojectiles.integer || (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_BOUNCE && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCEMISSILE) || ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
2729 // check for in water
2730 SV_CheckWaterTransition (ent);
2734 ===============================================================================
2738 ===============================================================================
2745 Monsters freefall when they don't have a ground entity, otherwise
2746 all movement is done with discrete steps.
2748 This is also used for objects that have become still on the ground, but
2749 will fall if the floor is pulled out from under them.
2752 static void SV_Physics_Step (prvm_edict_t *ent)
2754 prvm_prog_t *prog = SVVM_prog;
2755 int flags = (int)PRVM_serveredictfloat(ent, flags);
2758 // Backup Velocity in the event that movetypesteplandevent is called,
2759 // to provide a parameter with the entity's velocity at impact.
2760 vec3_t backupVelocity;
2761 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2762 // don't fall at all if fly/swim
2763 if (!(flags & (FL_FLY | FL_SWIM)))
2765 if (flags & FL_ONGROUND)
2767 // freefall if onground and moving upward
2768 // freefall if not standing on a world surface (it may be a lift or trap door)
2769 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2771 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2772 SV_CheckVelocity(ent);
2773 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2775 SV_LinkEdict_TouchAreaGrid(ent);
2776 ent->priv.server->waterposition_forceupdate = true;
2781 // freefall if not onground
2782 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2784 SV_CheckVelocity(ent);
2785 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2787 SV_LinkEdict_TouchAreaGrid(ent);
2790 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2792 // DRESK - Check for Entity Land Event Function
2793 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2794 { // Valid Function; Execute
2795 // Prepare Parameters
2796 // Assign Velocity at Impact
2797 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2798 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2799 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2801 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2803 PRVM_serverglobalfloat(time) = sv.time;
2804 // Execute VM Function
2805 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2808 // Check for Engine Landing Sound
2809 if(sv_sound_land.string)
2810 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2812 ent->priv.server->waterposition_forceupdate = true;
2817 if (!SV_RunThink(ent))
2820 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2822 ent->priv.server->waterposition_forceupdate = false;
2823 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2824 SV_CheckWaterTransition(ent);
2828 //============================================================================
2830 static void SV_Physics_Entity (prvm_edict_t *ent)
2832 prvm_prog_t *prog = SVVM_prog;
2833 // don't run think/move on newly spawned projectiles as it messes up
2834 // movement interpolation and rocket trails, and is inconsistent with
2835 // respect to entities spawned in the same frame
2836 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2837 // but if it spawns a lower numbered ent, it doesn't - this never moves
2838 // ents in the first frame regardless)
2839 qboolean runmove = ent->priv.server->move;
2840 ent->priv.server->move = true;
2841 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2843 switch ((int) PRVM_serveredictfloat(ent, movetype))
2846 case MOVETYPE_FAKEPUSH:
2847 SV_Physics_Pusher (ent);
2850 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2851 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2854 case MOVETYPE_FOLLOW:
2855 SV_Physics_Follow (ent);
2857 case MOVETYPE_NOCLIP:
2858 if (SV_RunThink(ent))
2861 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2862 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2867 SV_Physics_Step (ent);
2870 if (SV_RunThink (ent))
2874 case MOVETYPE_BOUNCE:
2875 case MOVETYPE_BOUNCEMISSILE:
2876 case MOVETYPE_FLYMISSILE:
2878 case MOVETYPE_FLY_WORLDONLY:
2880 if (SV_RunThink (ent))
2881 SV_Physics_Toss (ent);
2883 case MOVETYPE_PHYSICS:
2884 if (SV_RunThink(ent))
2887 SV_LinkEdict_TouchAreaGrid(ent);
2891 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2896 void SV_Physics_ClientMove(void)
2898 prvm_prog_t *prog = SVVM_prog;
2900 ent = host_client->edict;
2902 // call player physics, this needs the proper frametime
2903 PRVM_serverglobalfloat(frametime) = sv.frametime;
2906 // call standard client pre-think, with frametime = 0
2907 PRVM_serverglobalfloat(time) = sv.time;
2908 PRVM_serverglobalfloat(frametime) = 0;
2909 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2910 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2911 PRVM_serverglobalfloat(frametime) = sv.frametime;
2913 // make sure the velocity is sane (not a NaN)
2914 SV_CheckVelocity(ent);
2916 // perform MOVETYPE_WALK behavior
2919 // call standard player post-think, with frametime = 0
2920 PRVM_serverglobalfloat(time) = sv.time;
2921 PRVM_serverglobalfloat(frametime) = 0;
2922 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2923 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2924 PRVM_serverglobalfloat(frametime) = sv.frametime;
2926 if(PRVM_serveredictfloat(ent, fixangle))
2928 // angle fixing was requested by physics code...
2929 // so store the current angles for later use
2930 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2931 host_client->fixangle_angles_set = TRUE;
2933 // and clear fixangle for the next frame
2934 PRVM_serveredictfloat(ent, fixangle) = 0;
2938 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2940 prvm_prog_t *prog = SVVM_prog;
2941 // don't do physics on disconnected clients, FrikBot relies on this
2942 if (!host_client->spawned)
2945 // make sure the velocity is sane (not a NaN)
2946 SV_CheckVelocity(ent);
2948 // don't run physics here if running asynchronously
2949 if (host_client->clmovement_inputtimeout <= 0)
2952 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2955 // make sure the velocity is still sane (not a NaN)
2956 SV_CheckVelocity(ent);
2958 // call standard client pre-think
2959 PRVM_serverglobalfloat(time) = sv.time;
2960 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2961 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2963 // make sure the velocity is still sane (not a NaN)
2964 SV_CheckVelocity(ent);
2967 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2969 prvm_prog_t *prog = SVVM_prog;
2970 // don't do physics on disconnected clients, FrikBot relies on this
2971 if (!host_client->spawned)
2974 // make sure the velocity is sane (not a NaN)
2975 SV_CheckVelocity(ent);
2977 // call standard player post-think
2978 PRVM_serverglobalfloat(time) = sv.time;
2979 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2980 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2982 // make sure the velocity is still sane (not a NaN)
2983 SV_CheckVelocity(ent);
2985 if(PRVM_serveredictfloat(ent, fixangle))
2987 // angle fixing was requested by physics code...
2988 // so store the current angles for later use
2989 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2990 host_client->fixangle_angles_set = TRUE;
2992 // and clear fixangle for the next frame
2993 PRVM_serveredictfloat(ent, fixangle) = 0;
2996 // decrement the countdown variable used to decide when to go back to
2997 // synchronous physics
2998 if (host_client->clmovement_inputtimeout > sv.frametime)
2999 host_client->clmovement_inputtimeout -= sv.frametime;
3001 host_client->clmovement_inputtimeout = 0;
3004 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3006 prvm_prog_t *prog = SVVM_prog;
3007 // don't do physics on disconnected clients, FrikBot relies on this
3008 if (!host_client->spawned)
3010 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3014 // make sure the velocity is sane (not a NaN)
3015 SV_CheckVelocity(ent);
3017 switch ((int) PRVM_serveredictfloat(ent, movetype))
3020 case MOVETYPE_FAKEPUSH:
3021 SV_Physics_Pusher (ent);
3024 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3025 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3028 case MOVETYPE_FOLLOW:
3029 SV_Physics_Follow (ent);
3031 case MOVETYPE_NOCLIP:
3034 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3035 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3038 SV_Physics_Step (ent);
3042 // don't run physics here if running asynchronously
3043 if (host_client->clmovement_inputtimeout <= 0)
3047 case MOVETYPE_BOUNCE:
3048 case MOVETYPE_BOUNCEMISSILE:
3049 case MOVETYPE_FLYMISSILE:
3052 SV_Physics_Toss (ent);
3055 case MOVETYPE_FLY_WORLDONLY:
3059 case MOVETYPE_PHYSICS:
3063 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3067 SV_CheckVelocity (ent);
3070 SV_LinkEdict_TouchAreaGrid(ent);
3072 SV_CheckVelocity (ent);
3081 void SV_Physics (void)
3083 prvm_prog_t *prog = SVVM_prog;
3087 // let the progs know that a new frame has started
3088 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3089 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3090 PRVM_serverglobalfloat(time) = sv.time;
3091 PRVM_serverglobalfloat(frametime) = sv.frametime;
3092 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3094 // run physics engine
3095 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3098 // treat each object in turn
3101 // if force_retouch, relink all the entities
3102 if (PRVM_serverglobalfloat(force_retouch) > 0)
3103 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3104 if (!ent->priv.server->free)
3105 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3107 if (sv_gameplayfix_consistentplayerprethink.integer)
3109 // run physics on the client entities in 3 stages
3110 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3111 if (!ent->priv.server->free)
3112 SV_Physics_ClientEntity_PreThink(ent);
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(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_PostThink(ent);
3124 // run physics on the client entities
3125 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3127 if (!ent->priv.server->free)
3129 SV_Physics_ClientEntity_PreThink(ent);
3130 SV_Physics_ClientEntity(ent);
3131 SV_Physics_ClientEntity_PostThink(ent);
3136 // run physics on all the non-client entities
3137 if (!sv_freezenonclients.integer)
3139 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3140 if (!ent->priv.server->free)
3141 SV_Physics_Entity(ent);
3142 // make a second pass to see if any ents spawned this frame and make
3143 // sure they run their move/think
3144 if (sv_gameplayfix_delayprojectiles.integer < 0)
3145 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3146 if (!ent->priv.server->move && !ent->priv.server->free)
3147 SV_Physics_Entity(ent);
3150 if (PRVM_serverglobalfloat(force_retouch) > 0)
3151 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3153 // LordHavoc: endframe support
3154 if (PRVM_serverfunction(EndFrame))
3156 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3157 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3158 PRVM_serverglobalfloat(time) = sv.time;
3159 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3162 // decrement prog->num_edicts if the highest number entities died
3163 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3165 if (!sv_freezenonclients.integer)
3166 sv.time += sv.frametime;