2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
29 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38 solid_edge items only clip against bsp models.
42 #define MOVE_EPSILON 0.01
44 void SV_Physics_Toss (prvm_edict_t *ent);
46 int SV_GetPitchSign(prvm_edict_t *ent)
50 (model = SV_GetModelFromEdict(ent))
52 model->type == mod_alias
55 (((unsigned char)PRVM_serveredictfloat(ent, pflags)) & PFLAGS_FULLDYNAMIC)
57 ((gamemode == GAME_TENEBRAE) && ((unsigned int)PRVM_serveredictfloat(ent, effects) & (16 | 32)))
65 ===============================================================================
69 ===============================================================================
72 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
76 int dphitcontentsmask = (int)PRVM_serveredictfloat(passedict, dphitcontentsmask);
77 if (dphitcontentsmask)
78 return dphitcontentsmask;
79 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_SLIDEBOX)
81 if ((int)PRVM_serveredictfloat(passedict, flags) & FL_MONSTER)
82 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
84 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
86 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_CORPSE)
87 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
88 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_TRIGGER)
89 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
91 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
94 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
102 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
104 int i, bodysupercontents;
107 prvm_edict_t *traceowner, *touch;
109 // bounding box of entire move area
110 vec3_t clipboxmins, clipboxmaxs;
111 // size when clipping against monsters
112 vec3_t clipmins2, clipmaxs2;
113 // start and end origin of move
117 // matrices to transform into/out of other entity's space
118 matrix4x4_t matrix, imatrix;
119 // model of other entity
121 // list of entities to test for collisions
123 static prvm_edict_t *touchedicts[MAX_EDICTS];
125 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
127 VectorCopy(start, clipstart);
128 VectorClear(clipmins2);
129 VectorClear(clipmaxs2);
130 #if COLLISIONPARANOID >= 3
131 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
135 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
136 cliptrace.bmodelstartsolid = cliptrace.startsolid;
137 if (cliptrace.startsolid || cliptrace.fraction < 1)
138 cliptrace.ent = prog->edicts;
139 if (type == MOVE_WORLDONLY)
142 if (type == MOVE_MISSILE)
144 // LordHavoc: modified this, was = -15, now -= 15
145 for (i = 0;i < 3;i++)
152 // create the bounding box of the entire move
153 for (i = 0;i < 3;i++)
155 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
156 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
159 // debug override to test against everything
160 if (sv_debugmove.integer)
162 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
163 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
166 // if the passedict is world, make it NULL (to avoid two checks each time)
167 if (passedict == prog->edicts)
169 // precalculate prog value for passedict for comparisons
170 passedictprog = PRVM_EDICT_TO_PROG(passedict);
171 // precalculate passedict's owner edict pointer for comparisons
172 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
175 // because this uses World_EntitiestoBox, we know all entity boxes overlap
176 // the clip region, so we can skip culling checks in the loop below
177 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
178 if (numtouchedicts > MAX_EDICTS)
180 // this never happens
181 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
182 numtouchedicts = MAX_EDICTS;
184 for (i = 0;i < numtouchedicts;i++)
186 touch = touchedicts[i];
188 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
190 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
195 // don't clip against self
196 if (passedict == touch)
198 // don't clip owned entities against owner
199 if (traceowner == touch)
201 // don't clip owner against owned entities
202 if (passedictprog == PRVM_serveredictedict(touch, owner))
204 // don't clip points against points (they can't collide)
205 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
209 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
211 // might interact, so do an exact clip
213 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
215 model = SV_GetModelFromEdict(touch);
216 pitchsign = SV_GetPitchSign(touch);
219 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);
221 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
222 Matrix4x4_Invert_Simple(&imatrix, &matrix);
223 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
224 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
225 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
226 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
227 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);
229 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);
231 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
243 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
244 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
246 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
249 int i, bodysupercontents;
252 prvm_edict_t *traceowner, *touch;
254 // bounding box of entire move area
255 vec3_t clipboxmins, clipboxmaxs;
256 // size when clipping against monsters
257 vec3_t clipmins2, clipmaxs2;
258 // start and end origin of move
259 vec3_t clipstart, clipend;
262 // matrices to transform into/out of other entity's space
263 matrix4x4_t matrix, imatrix;
264 // model of other entity
266 // list of entities to test for collisions
268 static prvm_edict_t *touchedicts[MAX_EDICTS];
269 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
273 if (VectorCompare(start, pEnd))
274 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
276 if(collision_endposnudge.value > 0)
278 // TRICK: make the trace 1 qu longer!
279 VectorSubtract(pEnd, start, end);
280 len = VectorNormalizeLength(end);
281 VectorMA(pEnd, collision_endposnudge.value, end, end);
284 VectorCopy(pEnd, end);
286 if (VectorCompare(start, end))
287 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
290 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
292 VectorCopy(start, clipstart);
293 VectorCopy(end, clipend);
294 VectorClear(clipmins2);
295 VectorClear(clipmaxs2);
296 #if COLLISIONPARANOID >= 3
297 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
301 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, false);
302 cliptrace.bmodelstartsolid = cliptrace.startsolid;
303 if (cliptrace.startsolid || cliptrace.fraction < 1)
304 cliptrace.ent = prog->edicts;
305 if (type == MOVE_WORLDONLY)
308 if (type == MOVE_MISSILE)
310 // LordHavoc: modified this, was = -15, now -= 15
311 for (i = 0;i < 3;i++)
318 // create the bounding box of the entire move
319 for (i = 0;i < 3;i++)
321 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
322 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
325 // debug override to test against everything
326 if (sv_debugmove.integer)
328 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
329 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
332 // if the passedict is world, make it NULL (to avoid two checks each time)
333 if (passedict == prog->edicts)
335 // precalculate prog value for passedict for comparisons
336 passedictprog = PRVM_EDICT_TO_PROG(passedict);
337 // precalculate passedict's owner edict pointer for comparisons
338 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
341 // because this uses World_EntitiestoBox, we know all entity boxes overlap
342 // the clip region, so we can skip culling checks in the loop below
343 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
344 if (numtouchedicts > MAX_EDICTS)
346 // this never happens
347 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
348 numtouchedicts = MAX_EDICTS;
350 for (i = 0;i < numtouchedicts;i++)
352 touch = touchedicts[i];
354 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
356 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
361 // don't clip against self
362 if (passedict == touch)
364 // don't clip owned entities against owner
365 if (traceowner == touch)
367 // don't clip owner against owned entities
368 if (passedictprog == PRVM_serveredictedict(touch, owner))
370 // don't clip points against points (they can't collide)
371 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
375 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
377 // might interact, so do an exact clip
379 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
381 model = SV_GetModelFromEdict(touch);
382 pitchsign = SV_GetPitchSign(touch);
385 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);
387 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
388 Matrix4x4_Invert_Simple(&imatrix, &matrix);
389 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
390 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
391 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
392 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
393 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);
395 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);
397 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
401 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
402 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
403 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
413 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
414 #if COLLISIONPARANOID >= 1
415 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
417 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)
420 #if COLLISIONPARANOID >= 1
421 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
423 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 vec3_t hullmins, hullmaxs;
428 int i, bodysupercontents;
432 prvm_edict_t *traceowner, *touch;
434 // bounding box of entire move area
435 vec3_t clipboxmins, clipboxmaxs;
436 // size of the moving object
437 vec3_t clipmins, clipmaxs;
438 // size when clipping against monsters
439 vec3_t clipmins2, clipmaxs2;
440 // start and end origin of move
441 vec3_t clipstart, clipend;
444 // matrices to transform into/out of other entity's space
445 matrix4x4_t matrix, imatrix;
446 // model of other entity
448 // list of entities to test for collisions
450 static prvm_edict_t *touchedicts[MAX_EDICTS];
451 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
455 if (VectorCompare(mins, maxs))
457 vec3_t shiftstart, shiftend;
458 VectorAdd(start, mins, shiftstart);
459 VectorAdd(pEnd, mins, shiftend);
460 if (VectorCompare(start, pEnd))
461 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
463 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
464 VectorSubtract(trace.endpos, mins, trace.endpos);
468 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
470 // TRICK: make the trace 1 qu longer!
471 VectorSubtract(pEnd, start, end);
472 len = VectorNormalizeLength(end);
473 VectorMA(pEnd, collision_endposnudge.value, end, end);
476 VectorCopy(pEnd, end);
478 if (VectorCompare(mins, maxs))
480 vec3_t shiftstart, shiftend;
481 VectorAdd(start, mins, shiftstart);
482 VectorAdd(end, mins, shiftend);
483 if (VectorCompare(start, end))
484 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
486 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
487 VectorSubtract(trace.endpos, mins, trace.endpos);
492 VectorCopy(start, clipstart);
493 VectorCopy(end, clipend);
494 VectorCopy(mins, clipmins);
495 VectorCopy(maxs, clipmaxs);
496 VectorCopy(mins, clipmins2);
497 VectorCopy(maxs, clipmaxs2);
498 #if COLLISIONPARANOID >= 3
499 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
503 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
504 cliptrace.bmodelstartsolid = cliptrace.startsolid;
505 if (cliptrace.startsolid || cliptrace.fraction < 1)
506 cliptrace.ent = prog->edicts;
507 if (type == MOVE_WORLDONLY)
510 if (type == MOVE_MISSILE)
512 // LordHavoc: modified this, was = -15, now -= 15
513 for (i = 0;i < 3;i++)
520 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
521 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
522 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
525 VectorCopy(clipmins, hullmins);
526 VectorCopy(clipmaxs, hullmaxs);
529 // create the bounding box of the entire move
530 for (i = 0;i < 3;i++)
532 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
533 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
536 // debug override to test against everything
537 if (sv_debugmove.integer)
539 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
540 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
543 // if the passedict is world, make it NULL (to avoid two checks each time)
544 if (passedict == prog->edicts)
546 // precalculate prog value for passedict for comparisons
547 passedictprog = PRVM_EDICT_TO_PROG(passedict);
548 // figure out whether this is a point trace for comparisons
549 pointtrace = VectorCompare(clipmins, clipmaxs);
550 // precalculate passedict's owner edict pointer for comparisons
551 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
554 // because this uses World_EntitiestoBox, we know all entity boxes overlap
555 // the clip region, so we can skip culling checks in the loop below
556 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
557 if (numtouchedicts > MAX_EDICTS)
559 // this never happens
560 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
561 numtouchedicts = MAX_EDICTS;
563 for (i = 0;i < numtouchedicts;i++)
565 touch = touchedicts[i];
567 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
569 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
574 // don't clip against self
575 if (passedict == touch)
577 // don't clip owned entities against owner
578 if (traceowner == touch)
580 // don't clip owner against owned entities
581 if (passedictprog == PRVM_serveredictedict(touch, owner))
583 // don't clip points against points (they can't collide)
584 if (pointtrace && VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
588 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
590 // might interact, so do an exact clip
592 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
594 model = SV_GetModelFromEdict(touch);
595 pitchsign = SV_GetPitchSign(touch);
598 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);
600 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
601 Matrix4x4_Invert_Simple(&imatrix, &matrix);
602 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
603 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
604 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
605 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
606 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);
608 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);
610 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
614 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
615 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
616 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
621 #if COLLISIONPARANOID >= 1
622 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)
627 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
630 VectorCopy(trace.endpos, temp);
631 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
632 #if COLLISIONPARANOID < 3
633 if (trace.startsolid || endstuck)
635 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" : "");
641 int SV_PointSuperContents(const vec3_t point)
643 int supercontents = 0;
647 // matrices to transform into/out of other entity's space
648 matrix4x4_t matrix, imatrix;
649 // model of other entity
652 // list of entities to test for collisions
654 static prvm_edict_t *touchedicts[MAX_EDICTS];
656 // get world supercontents at this point
657 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
658 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
660 // if sv_gameplayfix_swiminbmodels is off we're done
661 if (!sv_gameplayfix_swiminbmodels.integer)
662 return supercontents;
664 // get list of entities at this point
665 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
666 if (numtouchedicts > MAX_EDICTS)
668 // this never happens
669 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
670 numtouchedicts = MAX_EDICTS;
672 for (i = 0;i < numtouchedicts;i++)
674 touch = touchedicts[i];
676 // we only care about SOLID_BSP for pointcontents
677 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
680 // might interact, so do an exact clip
681 model = SV_GetModelFromEdict(touch);
682 if (!model || !model->PointSuperContents)
684 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);
685 Matrix4x4_Invert_Simple(&imatrix, &matrix);
686 Matrix4x4_Transform(&imatrix, point, transformed);
687 frame = (int)PRVM_serveredictfloat(touch, frame);
688 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
691 return supercontents;
695 ===============================================================================
697 Linking entities into the world culling system
699 ===============================================================================
702 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
704 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
705 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
706 PRVM_serverglobalfloat(time) = sv.time;
707 PRVM_serverglobalfloat(trace_allsolid) = false;
708 PRVM_serverglobalfloat(trace_startsolid) = false;
709 PRVM_serverglobalfloat(trace_fraction) = 1;
710 PRVM_serverglobalfloat(trace_inwater) = false;
711 PRVM_serverglobalfloat(trace_inopen) = true;
712 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
713 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
714 PRVM_serverglobalfloat(trace_plane_dist) = 0;
715 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
716 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
717 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
718 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
719 PRVM_serverglobalstring(trace_dphittexturename) = 0;
720 PRVM_ExecuteProgram (PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
723 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
725 int i, numtouchedicts, old_self, old_other;
727 static prvm_edict_t *touchedicts[MAX_EDICTS];
729 if (ent == prog->edicts)
730 return; // don't add the world
732 if (ent->priv.server->free)
735 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
738 // build a list of edicts to touch, because the link loop can be corrupted
739 // by IncreaseEdicts called during touch functions
740 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
741 if (numtouchedicts > MAX_EDICTS)
743 // this never happens
744 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
745 numtouchedicts = MAX_EDICTS;
748 old_self = PRVM_serverglobaledict(self);
749 old_other = PRVM_serverglobaledict(other);
750 for (i = 0;i < numtouchedicts;i++)
752 touch = touchedicts[i];
753 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
755 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
758 PRVM_serverglobaledict(self) = old_self;
759 PRVM_serverglobaledict(other) = old_other;
762 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
766 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
768 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
769 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
770 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
771 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];
772 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];
773 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
774 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];
775 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];
776 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
777 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];
778 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];
779 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
780 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];
781 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];
782 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
783 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];
784 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];
785 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
786 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];
787 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
788 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
789 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];
790 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];
799 void SV_LinkEdict (prvm_edict_t *ent)
805 if (ent == prog->edicts)
806 return; // don't add the world
808 if (ent->priv.server->free)
811 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
812 if (modelindex < 0 || modelindex >= MAX_MODELS)
814 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
817 model = SV_GetModelByIndex(modelindex);
819 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
820 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
821 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
825 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
827 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
828 // TODO special handling for spheres?
829 RotateBBox(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), PRVM_serveredictvector(ent, angles), mins, maxs);
830 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
831 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
833 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
837 if (!model->TraceBox)
838 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
840 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
842 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
843 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
845 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
847 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
848 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
852 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
853 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
858 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
859 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
860 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
865 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
866 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
870 // to make items easier to pick up and allow them to be grabbed off
871 // of shelves, the abs sizes are expanded
873 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
884 // because movement is clipped an epsilon away from an actual edge,
885 // we must fully check even when bounding boxes don't quite touch
894 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
895 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
897 World_LinkEdict(&sv.world, ent, mins, maxs);
901 ===============================================================================
905 ===============================================================================
910 SV_TestEntityPosition
912 returns true if the entity is in solid currently
915 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
920 contents = SV_GenericHitSuperContentsMask(ent);
921 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
922 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);
923 if (trace.startsupercontents & contents)
927 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
929 // q1bsp/hlbsp use hulls and if the entity does not exactly match
930 // a hull size it is incorrectly tested, so this code tries to
931 // 'fix' it slightly...
932 // FIXME: this breaks entities larger than the hull size
935 VectorAdd(org, PRVM_serveredictvector(ent, mins), m1);
936 VectorAdd(org, PRVM_serveredictvector(ent, maxs), m2);
937 VectorSubtract(m2, m1, s);
938 #define EPSILON (1.0f / 32.0f)
939 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
940 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
941 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
942 for (i = 0;i < 8;i++)
944 v[0] = (i & 1) ? m2[0] : m1[0];
945 v[1] = (i & 2) ? m2[1] : m1[1];
946 v[2] = (i & 4) ? m2[2] : m1[2];
947 if (SV_PointSuperContents(v) & contents)
952 // if the trace found a better position for the entity, move it there
953 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
956 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
957 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
959 // verify if the endpos is REALLY outside solid
960 VectorCopy(trace.endpos, org);
961 trace = SV_TraceBox(org, PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), org, MOVE_NOMONSTERS, ent, contents);
963 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
965 VectorCopy(org, PRVM_serveredictvector(ent, origin));
976 void SV_CheckAllEnts (void)
981 // see if any solid entities are inside the final position
982 check = PRVM_NEXT_EDICT(prog->edicts);
983 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
985 if (check->priv.server->free)
987 if (PRVM_serveredictfloat(check, movetype) == MOVETYPE_PUSH
988 || PRVM_serveredictfloat(check, movetype) == MOVETYPE_NONE
989 || PRVM_serveredictfloat(check, movetype) == MOVETYPE_FOLLOW
990 || PRVM_serveredictfloat(check, movetype) == MOVETYPE_NOCLIP
991 || PRVM_serveredictfloat(check, movetype) == MOVETYPE_FLY_WORLDONLY)
994 if (SV_TestEntityPosition (check, vec3_origin))
995 Con_Print("entity in invalid position\n");
999 // DRESK - Support for Entity Contents Transition Event
1002 SV_CheckContentsTransition
1004 returns true if entity had a valid contentstransition function call
1007 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
1009 int bValidFunctionCall;
1011 // Default Valid Function Call to False
1012 bValidFunctionCall = false;
1014 if(PRVM_serveredictfloat(ent, watertype) != nContents)
1015 { // Changed Contents
1016 // Acquire Contents Transition Function from QC
1017 if(PRVM_serveredictfunction(ent, contentstransition))
1018 { // Valid Function; Execute
1019 // Assign Valid Function
1020 bValidFunctionCall = true;
1021 // Prepare Parameters (Original Contents, New Contents)
1022 // Original Contents
1023 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
1025 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1027 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1028 // Execute VM Function
1029 PRVM_ExecuteProgram(PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1033 // Return if Function Call was Valid
1034 return bValidFunctionCall;
1043 void SV_CheckVelocity (prvm_edict_t *ent)
1051 for (i=0 ; i<3 ; i++)
1053 if (IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1055 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)));
1056 PRVM_serveredictvector(ent, velocity)[i] = 0;
1058 if (IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1060 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)));
1061 PRVM_serveredictvector(ent, origin)[i] = 0;
1065 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1066 // player_run/player_stand1 does not horribly malfunction if the
1067 // velocity becomes a denormalized float
1068 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0001)
1069 VectorClear(PRVM_serveredictvector(ent, velocity));
1071 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1072 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1073 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1075 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1076 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1077 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1078 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1086 Runs thinking code if time. There is some play in the exact time the think
1087 function will be called, because it is called before any movement is done
1088 in a frame. Not used for pushmove objects, because they must be exact.
1089 Returns false if the entity removed itself.
1092 qboolean SV_RunThink (prvm_edict_t *ent)
1096 // don't let things stay in the past.
1097 // it is possible to start that way by a trigger with a local time.
1098 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1101 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1103 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1104 PRVM_serveredictfloat(ent, nextthink) = 0;
1105 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1106 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1107 PRVM_ExecuteProgram (PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1108 // mods often set nextthink to time to cause a think every frame,
1109 // we don't want to loop in that case, so exit if the new nextthink is
1110 // <= the time the qc was told, also exit if it is past the end of the
1112 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1115 return !ent->priv.server->free;
1122 Two entities have touched, so run their touch functions
1125 extern void VM_SetTraceGlobals(const trace_t *trace);
1126 extern sizebuf_t vm_tempstringsbuf;
1127 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1129 int restorevm_tempstringsbuf_cursize;
1130 int old_self, old_other;
1131 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1133 old_self = PRVM_serverglobaledict(self);
1134 old_other = PRVM_serverglobaledict(other);
1135 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1137 VM_SetTraceGlobals(trace);
1139 PRVM_serverglobalfloat(time) = sv.time;
1140 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1142 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1143 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1144 PRVM_ExecuteProgram (PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1147 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1149 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1150 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1151 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1152 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1153 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1154 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1155 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1156 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1157 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1158 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1159 PRVM_ExecuteProgram (PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1162 PRVM_serverglobaledict(self) = old_self;
1163 PRVM_serverglobaledict(other) = old_other;
1164 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1172 Slide off of the impacting object
1173 returns the blocked flags (1 = floor, 2 = step / wall)
1176 #define STOP_EPSILON 0.1
1177 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1182 backoff = -DotProduct (in, normal) * overbounce;
1183 VectorMA(in, backoff, normal, out);
1185 for (i = 0;i < 3;i++)
1186 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1195 The basic solid body movement clip that slides along multiple planes
1196 Returns the clipflags if the velocity was modified (hit something solid)
1200 8 = teleported by touch method
1201 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1204 static float SV_Gravity (prvm_edict_t *ent);
1205 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1206 #define MAX_CLIP_PLANES 5
1207 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1209 int blocked, bumpcount;
1210 int i, j, numplanes;
1211 float d, time_left, gravity;
1212 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1221 if(sv_gameplayfix_nogravityonground.integer)
1222 if((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
1223 applygravity = false;
1227 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1229 gravity = SV_Gravity(ent) * 0.5f;
1230 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1234 applygravity = false;
1235 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
1239 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1240 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1243 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1245 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1248 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1249 if(!SV_PushEntity(&trace, ent, push, false, false))
1251 // we got teleported by a touch function
1252 // let's abort the move
1257 if (trace.fraction == 1)
1259 if (trace.plane.normal[2])
1261 if (trace.plane.normal[2] > 0.7)
1268 Con_Printf ("SV_FlyMove: !trace.ent");
1269 trace.ent = prog->edicts;
1272 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1273 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1276 else if (stepheight)
1278 // step - handle it immediately
1284 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1285 VectorSet(steppush, 0, 0, stepheight);
1286 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1287 if(!SV_PushEntity(&steptrace, ent, steppush, false, false))
1292 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1293 if(!SV_PushEntity(&steptrace2, ent, push, false, false))
1298 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1299 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1300 if(!SV_PushEntity(&steptrace3, ent, steppush, false, false))
1305 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1306 // accept the new position if it made some progress...
1307 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1309 //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]);
1311 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1312 time_left *= 1 - trace.fraction;
1318 //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]);
1319 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1324 // step - return it to caller
1326 // save the trace for player extrafriction
1328 VectorCopy(trace.plane.normal, stepnormal);
1330 if (trace.fraction >= 0.001)
1332 // actually covered some distance
1333 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1337 time_left *= 1 - trace.fraction;
1339 // clipped to another plane
1340 if (numplanes >= MAX_CLIP_PLANES)
1342 // this shouldn't really happen
1343 VectorClear(PRVM_serveredictvector(ent, velocity));
1349 for (i = 0;i < numplanes;i++)
1350 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1354 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1359 VectorCopy(trace.plane.normal, planes[numplanes]);
1362 // modify original_velocity so it parallels all of the clip planes
1363 for (i = 0;i < numplanes;i++)
1365 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1366 for (j = 0;j < numplanes;j++)
1371 if (DotProduct(new_velocity, planes[j]) < 0)
1381 // go along this plane
1382 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1386 // go along the crease
1389 VectorClear(PRVM_serveredictvector(ent, velocity));
1393 CrossProduct(planes[0], planes[1], dir);
1394 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1395 VectorNormalize(dir);
1396 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1397 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1400 // if current velocity is against the original velocity,
1401 // stop dead to avoid tiny occilations in sloping corners
1402 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1404 VectorClear(PRVM_serveredictvector(ent, velocity));
1409 //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]);
1412 if ((blocked & 1) == 0 && bumpcount > 1)
1414 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1415 // flag ONGROUND if there's ground under it
1416 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask);
1420 // LordHavoc: this came from QW and allows you to get out of water more easily
1421 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1422 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1423 if (applygravity && !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1424 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1434 static float SV_Gravity (prvm_edict_t *ent)
1438 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1441 return ent_gravity * sv_gravity.value * sv.frametime;
1446 ===============================================================================
1450 ===============================================================================
1453 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1458 vec3_t stuckmins, stuckmaxs;
1459 vec3_t goodmins, goodmaxs;
1463 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1464 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1465 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1466 VectorCopy(pivot, goodmins);
1467 VectorCopy(pivot, goodmaxs);
1468 for (bump = 0;bump < 6;bump++)
1470 int coord = 2-(bump >> 1);
1471 //int coord = (bump >> 1);
1472 int dir = (bump & 1);
1475 for(subbump = 0; ; ++subbump)
1477 VectorCopy(stuckorigin, testorigin);
1481 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1486 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1489 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1490 if (stucktrace.bmodelstartsolid)
1492 // BAD BAD, can't fix that
1496 if (stucktrace.fraction >= 1)
1501 // BAD BAD, can't fix that
1505 // we hit something... let's move out of it
1506 VectorSubtract(stucktrace.endpos, testorigin, move);
1507 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1508 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1512 Con_Printf("subbump: %d\n", subbump);
1518 goodmaxs[coord] = stuckmaxs[coord];
1523 goodmins[coord] = stuckmins[coord];
1528 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1533 static qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1538 vec3_t stuckmins, stuckmaxs;
1540 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1541 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1542 separation = 0.0f; // when using hulls, it can not be enlarged
1543 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1544 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1545 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1546 stuckmins[0] -= separation;
1547 stuckmins[1] -= separation;
1548 stuckmins[2] -= separation;
1549 stuckmaxs[0] += separation;
1550 stuckmaxs[1] += separation;
1551 stuckmaxs[2] += separation;
1552 for (bump = 0;bump < 10;bump++)
1554 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1555 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1557 // found a good location, use it
1558 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1561 nudge = -stucktrace.startdepth;
1562 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1571 Does not change the entities velocity at all
1572 The trace struct is filled with the trace that has been done.
1573 Returns true if the push did not result in the entity being teleported by QC code.
1576 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1582 vec3_t original, original_velocity;
1586 solid = (int)PRVM_serveredictfloat(ent, solid);
1587 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1588 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1589 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1591 // move start position out of solids
1592 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1594 SV_NudgeOutOfSolid(ent);
1597 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1598 VectorAdd(start, push, end);
1600 if (movetype == MOVETYPE_FLYMISSILE)
1601 type = MOVE_MISSILE;
1602 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1603 type = MOVE_WORLDONLY;
1604 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1605 type = MOVE_NOMONSTERS; // only clip against bmodels
1609 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1610 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1613 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1615 VectorCopy(PRVM_serveredictvector(ent, origin), original);
1616 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1621 if(!trace->startsolid)
1622 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)
1624 Con_Printf("something eeeeevil happened\n");
1629 SV_LinkEdict_TouchAreaGrid(ent);
1631 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))))
1632 SV_Impact (ent, trace);
1634 return VectorCompare(PRVM_serveredictvector(ent, origin), original) && VectorCompare(PRVM_serveredictvector(ent, velocity), original_velocity);
1644 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1647 int pusherowner, pusherprog;
1650 float savesolid, movetime2, pushltime;
1651 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1653 int numcheckentities;
1654 static prvm_edict_t *checkentities[MAX_EDICTS];
1655 dp_model_t *pushermodel;
1656 trace_t trace, trace2;
1657 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1658 static unsigned short moved_edicts[MAX_EDICTS];
1661 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])
1663 PRVM_serveredictfloat(pusher, ltime) += movetime;
1667 switch ((int) PRVM_serveredictfloat(pusher, solid))
1669 // LordHavoc: valid pusher types
1672 case SOLID_SLIDEBOX:
1673 case SOLID_CORPSE: // LordHavoc: this would be weird...
1675 // LordHavoc: no collisions
1678 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1679 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1680 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1681 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1682 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1683 PRVM_serveredictfloat(pusher, ltime) += movetime;
1684 SV_LinkEdict(pusher);
1687 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1690 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1691 if (index < 1 || index >= MAX_MODELS)
1693 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1696 pushermodel = SV_GetModelByIndex(index);
1697 pusherowner = PRVM_serveredictedict(pusher, owner);
1698 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1700 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1702 movetime2 = movetime;
1703 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1704 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1705 if (moveangle[0] || moveangle[2])
1707 for (i = 0;i < 3;i++)
1711 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1712 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1716 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1717 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1721 else if (moveangle[1])
1723 for (i = 0;i < 3;i++)
1727 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1728 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1732 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1733 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1739 for (i = 0;i < 3;i++)
1743 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1744 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1748 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1749 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1754 VectorNegate (moveangle, a);
1755 AngleVectorsFLU (a, forward, left, up);
1757 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1758 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1759 pushltime = PRVM_serveredictfloat(pusher, ltime);
1761 // move the pusher to its final position
1763 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1764 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1765 PRVM_serveredictfloat(pusher, ltime) += movetime;
1766 SV_LinkEdict(pusher);
1768 pushermodel = SV_GetModelFromEdict(pusher);
1769 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);
1770 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1772 savesolid = PRVM_serveredictfloat(pusher, solid);
1774 // see if any solid entities are inside the final position
1777 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1778 numcheckentities = 0;
1779 else // MOVETYPE_PUSH
1780 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1781 for (e = 0;e < numcheckentities;e++)
1783 prvm_edict_t *check = checkentities[e];
1784 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1789 case MOVETYPE_FOLLOW:
1790 case MOVETYPE_NOCLIP:
1791 case MOVETYPE_FLY_WORLDONLY:
1797 if (PRVM_serveredictedict(check, owner) == pusherprog)
1800 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1803 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1805 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1806 check->priv.server->waterposition_forceupdate = true;
1808 checkcontents = SV_GenericHitSuperContentsMask(check);
1810 // if the entity is standing on the pusher, it will definitely be moved
1811 // if the entity is not standing on the pusher, but is in the pusher's
1812 // final position, move it
1813 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1815 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);
1816 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1817 if (!trace.startsolid)
1819 //Con_Printf("- not in solid\n");
1824 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1825 //VectorClear(pivot);
1830 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1831 VectorAdd (org, pivot, org);
1832 org2[0] = DotProduct (org, forward);
1833 org2[1] = DotProduct (org, left);
1834 org2[2] = DotProduct (org, up);
1835 VectorSubtract (org2, org, move);
1836 VectorAdd (move, move1, move);
1839 VectorCopy (move1, move);
1841 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1843 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1844 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1845 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1847 // physics objects need better collisions than this code can do
1848 if (movetype == MOVETYPE_PHYSICS)
1850 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1851 SV_LinkEdict(check);
1852 SV_LinkEdict_TouchAreaGrid(check);
1856 // try moving the contacted entity
1857 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1858 if(!SV_PushEntity (&trace, check, move, true, true))
1860 // entity "check" got teleported
1861 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1862 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1863 continue; // pushed enough
1865 // FIXME: turn players specially
1866 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1867 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1868 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1870 // this trace.fraction < 1 check causes items to fall off of pushers
1871 // if they pass under or through a wall
1872 // the groundentity check causes items to fall off of ledges
1873 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1874 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1876 // if it is still inside the pusher, block
1877 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);
1878 if (trace.startsolid)
1881 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1883 // hack to invoke all necessary movement triggers
1885 if(!SV_PushEntity(&trace2, check, move2, true, true))
1887 // entity "check" got teleported
1894 // still inside pusher, so it's really blocked
1897 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1899 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1902 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1903 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1907 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1908 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1909 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1910 SV_LinkEdict(pusher);
1912 // move back any entities we already moved
1913 for (i = 0;i < num_moved;i++)
1915 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1916 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1917 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1921 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1922 if (PRVM_serveredictfunction(pusher, blocked))
1924 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1925 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1926 PRVM_ExecuteProgram (PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1931 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1932 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1933 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1942 void SV_Physics_Pusher (prvm_edict_t *ent)
1944 float thinktime, oldltime, movetime;
1946 oldltime = PRVM_serveredictfloat(ent, ltime);
1948 thinktime = PRVM_serveredictfloat(ent, nextthink);
1949 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1951 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1956 movetime = sv.frametime;
1959 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
1960 SV_PushMove (ent, movetime);
1962 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
1964 PRVM_serveredictfloat(ent, nextthink) = 0;
1965 PRVM_serverglobalfloat(time) = sv.time;
1966 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1967 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1968 PRVM_ExecuteProgram (PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1974 ===============================================================================
1978 ===============================================================================
1981 static float unstickoffsets[] =
1983 // poutting -/+z changes first as they are least weird
1998 typedef enum unstickresult_e
2006 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2010 // if not stuck in a bmodel, just return
2011 if (!SV_TestEntityPosition(ent, vec3_origin))
2012 return UNSTICK_GOOD;
2014 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2016 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2018 VectorCopy(unstickoffsets + i, offset);
2020 //SV_LinkEdict_TouchAreaGrid(ent);
2021 return UNSTICK_UNSTUCK;
2025 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2026 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2028 for(i = 2; i <= maxunstick; ++i)
2030 VectorClear(offset);
2032 if (!SV_TestEntityPosition(ent, offset))
2035 //SV_LinkEdict_TouchAreaGrid(ent);
2036 return UNSTICK_UNSTUCK;
2039 if (!SV_TestEntityPosition(ent, offset))
2042 //SV_LinkEdict_TouchAreaGrid(ent);
2043 return UNSTICK_UNSTUCK;
2047 return UNSTICK_STUCK;
2050 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2053 switch(SV_UnstickEntityReturnOffset(ent, offset))
2057 case UNSTICK_UNSTUCK:
2058 Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2061 if (developer_extra.integer)
2062 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)));
2065 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2074 This is a big hack to try and fix the rare case of getting stuck in the world
2078 void SV_CheckStuck (prvm_edict_t *ent)
2082 switch(SV_UnstickEntityReturnOffset(ent, offset))
2085 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2087 case UNSTICK_UNSTUCK:
2088 Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2091 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2092 if (!SV_TestEntityPosition(ent, offset))
2094 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)));
2096 //SV_LinkEdict_TouchAreaGrid(ent);
2099 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)));
2102 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2112 qboolean SV_CheckWater (prvm_edict_t *ent)
2115 int nNativeContents;
2118 point[0] = PRVM_serveredictvector(ent, origin)[0];
2119 point[1] = PRVM_serveredictvector(ent, origin)[1];
2120 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2122 // DRESK - Support for Entity Contents Transition Event
2123 // NOTE: Some logic needed to be slightly re-ordered
2124 // to not affect performance and allow for the feature.
2126 // Acquire Super Contents Prior to Resets
2127 cont = SV_PointSuperContents(point);
2128 // Acquire Native Contents Here
2129 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2131 // DRESK - Support for Entity Contents Transition Event
2132 if(PRVM_serveredictfloat(ent, watertype))
2133 // Entity did NOT Spawn; Check
2134 SV_CheckContentsTransition(ent, nNativeContents);
2137 PRVM_serveredictfloat(ent, waterlevel) = 0;
2138 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2139 cont = SV_PointSuperContents(point);
2140 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2142 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2143 PRVM_serveredictfloat(ent, waterlevel) = 1;
2144 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2145 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2147 PRVM_serveredictfloat(ent, waterlevel) = 2;
2148 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2149 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2150 PRVM_serveredictfloat(ent, waterlevel) = 3;
2154 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2163 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2166 vec3_t forward, into, side;
2168 AngleVectors (PRVM_serveredictvector(ent, v_angle), forward, NULL, NULL);
2169 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2171 // cut the tangential velocity
2172 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2173 VectorScale (stepnormal, i, into);
2174 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2175 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2176 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2182 =====================
2185 Player has come to a dead stop, possibly due to the problem with limited
2186 float precision at some angle joins in the BSP hull.
2188 Try fixing by pushing one pixel in each direction.
2190 This is a hack, but in the interest of good gameplay...
2191 ======================
2193 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2198 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2201 for (i=0 ; i<8 ; i++)
2203 // try pushing a little in an axial direction
2206 case 0: dir[0] = 2; dir[1] = 0; break;
2207 case 1: dir[0] = 0; dir[1] = 2; break;
2208 case 2: dir[0] = -2; dir[1] = 0; break;
2209 case 3: dir[0] = 0; dir[1] = -2; break;
2210 case 4: dir[0] = 2; dir[1] = 2; break;
2211 case 5: dir[0] = -2; dir[1] = 2; break;
2212 case 6: dir[0] = 2; dir[1] = -2; break;
2213 case 7: dir[0] = -2; dir[1] = -2; break;
2216 SV_PushEntity (&trace, ent, dir, false, true);
2218 // retry the original move
2219 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2220 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2221 PRVM_serveredictvector(ent, velocity)[2] = 0;
2222 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2224 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2225 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2227 Con_DPrint("TryUnstick - success.\n");
2231 // go back to the original pos and try again
2232 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2236 VectorClear (PRVM_serveredictvector(ent, velocity));
2237 Con_DPrint("TryUnstick - failure.\n");
2243 =====================
2246 Only used by players
2247 ======================
2249 void SV_WalkMove (prvm_edict_t *ent)
2253 //int originalmove_clip;
2254 int originalmove_flags;
2255 int originalmove_groundentity;
2256 int hitsupercontentsmask;
2258 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2259 trace_t downtrace, trace;
2260 qboolean applygravity;
2262 // if frametime is 0 (due to client sending the same timestamp twice),
2264 if (sv.frametime <= 0)
2267 if (sv_gameplayfix_unstickplayers.integer)
2268 SV_CheckStuck (ent);
2270 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2272 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2274 SV_CheckVelocity(ent);
2276 // do a regular slide move unless it looks like you ran into a step
2277 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2279 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2280 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2282 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2284 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2287 // only try this if there was no floor in the way in the trace (no,
2288 // this check seems to be not REALLY necessary, because if clip & 1,
2289 // our trace will hit that thing too)
2290 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2291 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2292 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2293 type = MOVE_MISSILE;
2294 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2295 type = MOVE_WORLDONLY;
2296 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2297 type = MOVE_NOMONSTERS; // only clip against bmodels
2300 trace = SV_TraceBox(upmove, PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2301 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2302 clip |= 1; // but we HAVE found a floor
2305 // if the move did not hit the ground at any point, we're not on ground
2307 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2309 SV_CheckVelocity(ent);
2311 SV_LinkEdict_TouchAreaGrid(ent);
2313 if(clip & 8) // teleport
2316 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2319 if (sv_nostep.integer)
2322 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2323 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2324 //originalmove_clip = clip;
2325 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2326 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2328 // if move didn't block on a step, return
2331 // if move was not trying to move into the step, return
2332 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2335 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2337 // return if gibbed by a trigger
2338 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2341 // only step up while jumping if that is enabled
2342 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2343 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2347 // try moving up and forward to go up a step
2348 // back to start pos
2349 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2350 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2353 VectorClear (upmove);
2354 upmove[2] = sv_stepheight.value;
2355 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2357 // we got teleported when upstepping... must abort the move
2362 PRVM_serveredictvector(ent, velocity)[2] = 0;
2363 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2364 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2367 // we got teleported when upstepping... must abort the move
2368 // note that z velocity handling may not be what QC expects here, but we cannot help it
2372 SV_CheckVelocity(ent);
2374 SV_LinkEdict_TouchAreaGrid(ent);
2376 // check for stuckness, possibly due to the limited precision of floats
2377 // in the clipping hulls
2379 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2380 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2382 //Con_Printf("wall\n");
2383 // stepping up didn't make any progress, revert to original move
2384 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2385 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2386 //clip = originalmove_clip;
2387 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2388 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2389 // now try to unstick if needed
2390 //clip = SV_TryUnstick (ent, oldvel);
2394 //Con_Printf("step - ");
2396 // extra friction based on view angle
2397 if (clip & 2 && sv_wallfriction.integer)
2398 SV_WallFriction (ent, stepnormal);
2400 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2401 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))
2405 VectorClear (downmove);
2406 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2407 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2409 // we got teleported when downstepping... must abort the move
2413 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2415 // this has been disabled so that you can't jump when you are stepping
2416 // up while already jumping (also known as the Quake2 double jump bug)
2418 // LordHavoc: disabled this check so you can walk on monsters/players
2419 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2421 //Con_Printf("onground\n");
2422 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2423 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2429 //Con_Printf("slope\n");
2430 // if the push down didn't end up on good ground, use the move without
2431 // the step up. This happens near wall / slope combinations, and can
2432 // cause the player to hop up higher on a slope too steep to climb
2433 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2434 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2435 //clip = originalmove_clip;
2436 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2437 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2440 SV_CheckVelocity(ent);
2442 SV_LinkEdict_TouchAreaGrid(ent);
2445 //============================================================================
2451 Entities that are "stuck" to another entity
2454 void SV_Physics_Follow (prvm_edict_t *ent)
2456 vec3_t vf, vr, vu, angles, v;
2460 if (!SV_RunThink (ent))
2463 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2464 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2465 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])
2467 // quick case for no rotation
2468 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2472 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2473 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2474 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2475 AngleVectors (angles, vf, vr, vu);
2476 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];
2477 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];
2478 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];
2479 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2480 angles[1] = PRVM_serveredictvector(e, angles)[1];
2481 angles[2] = PRVM_serveredictvector(e, angles)[2];
2482 AngleVectors (angles, vf, vr, vu);
2483 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2484 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2485 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2487 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2489 //SV_LinkEdict_TouchAreaGrid(ent);
2493 ==============================================================================
2497 ==============================================================================
2502 SV_CheckWaterTransition
2506 void SV_CheckWaterTransition (prvm_edict_t *ent)
2508 // 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
2510 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(PRVM_serveredictvector(ent, origin)));
2511 if (!PRVM_serveredictfloat(ent, watertype))
2513 // just spawned here
2514 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2516 PRVM_serveredictfloat(ent, watertype) = cont;
2517 PRVM_serveredictfloat(ent, waterlevel) = 1;
2521 // DRESK - Support for Entity Contents Transition Event
2522 // NOTE: Call here BEFORE updating the watertype below,
2523 // and suppress watersplash sound if a valid function
2524 // call was made to allow for custom "splash" sounds.
2525 else if( !SV_CheckContentsTransition(ent, cont) )
2526 { // Contents Transition Function Invalid; Potentially Play Water Sound
2527 // check if the entity crossed into or out of water
2528 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2529 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2532 if (cont <= CONTENTS_WATER)
2534 PRVM_serveredictfloat(ent, watertype) = cont;
2535 PRVM_serveredictfloat(ent, waterlevel) = 1;
2539 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2540 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2548 Toss, bounce, and fly movement. When onground, do nothing.
2552 void SV_Physics_Toss (prvm_edict_t *ent)
2558 prvm_edict_t *groundentity;
2560 // if onground, return without moving
2561 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2563 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2564 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2566 // don't stick to ground if onground and moving upward
2567 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2569 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2571 // we can trust FL_ONGROUND if groundentity is world because it never moves
2574 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2576 // if ent was supported by a brush model on previous frame,
2577 // and groundentity is now freed, set groundentity to 0 (world)
2578 // which leaves it suspended in the air
2579 PRVM_serveredictedict(ent, groundentity) = 0;
2580 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2583 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2585 // don't slide if still touching the groundentity
2589 ent->priv.server->suspendedinairflag = false;
2591 SV_CheckVelocity (ent);
2594 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2595 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2598 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2600 movetime = sv.frametime;
2601 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2604 VectorScale (PRVM_serveredictvector(ent, velocity), movetime, move);
2605 if(!SV_PushEntity (&trace, ent, move, true, true))
2606 return; // teleported
2607 if (ent->priv.server->free)
2609 if (trace.bmodelstartsolid)
2611 // try to unstick the entity
2612 if (sv_gameplayfix_unstickentities.integer)
2613 SV_UnstickEntity(ent);
2614 if(!SV_PushEntity (&trace, ent, move, false, true))
2615 return; // teleported
2616 if (ent->priv.server->free)
2619 if (trace.fraction == 1)
2621 movetime *= 1 - min(1, trace.fraction);
2622 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCEMISSILE)
2625 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2627 bouncefactor = 1.0f;
2629 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2630 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2632 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2634 float d, ent_gravity;
2638 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2640 bouncefactor = 0.5f;
2642 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2644 bouncestop = 60.0f / 800.0f;
2646 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2647 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2650 // LordHavoc: fixed grenades not bouncing when fired down a slope
2651 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2653 d = DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity));
2654 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2656 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2657 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2658 VectorClear (PRVM_serveredictvector(ent, velocity));
2659 VectorClear (PRVM_serveredictvector(ent, avelocity));
2662 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2666 if (trace.plane.normal[2] > 0.7 && PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * bouncestop * ent_gravity)
2668 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2669 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2670 VectorClear (PRVM_serveredictvector(ent, velocity));
2671 VectorClear (PRVM_serveredictvector(ent, avelocity));
2674 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2679 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2680 if (trace.plane.normal[2] > 0.7)
2682 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2683 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2684 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2685 ent->priv.server->suspendedinairflag = true;
2686 VectorClear (PRVM_serveredictvector(ent, velocity));
2687 VectorClear (PRVM_serveredictvector(ent, avelocity));
2690 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2692 if (!sv_gameplayfix_slidemoveprojectiles.integer || (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_BOUNCE && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCEMISSILE) || ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
2696 // check for in water
2697 SV_CheckWaterTransition (ent);
2701 ===============================================================================
2705 ===============================================================================
2712 Monsters freefall when they don't have a ground entity, otherwise
2713 all movement is done with discrete steps.
2715 This is also used for objects that have become still on the ground, but
2716 will fall if the floor is pulled out from under them.
2719 void SV_Physics_Step (prvm_edict_t *ent)
2721 int flags = (int)PRVM_serveredictfloat(ent, flags);
2724 // Backup Velocity in the event that movetypesteplandevent is called,
2725 // to provide a parameter with the entity's velocity at impact.
2726 vec3_t backupVelocity;
2727 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2728 // don't fall at all if fly/swim
2729 if (!(flags & (FL_FLY | FL_SWIM)))
2731 if (flags & FL_ONGROUND)
2733 // freefall if onground and moving upward
2734 // freefall if not standing on a world surface (it may be a lift or trap door)
2735 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2737 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2738 SV_CheckVelocity(ent);
2739 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2741 SV_LinkEdict_TouchAreaGrid(ent);
2742 ent->priv.server->waterposition_forceupdate = true;
2747 // freefall if not onground
2748 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2750 SV_CheckVelocity(ent);
2751 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2753 SV_LinkEdict_TouchAreaGrid(ent);
2756 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2758 // DRESK - Check for Entity Land Event Function
2759 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2760 { // Valid Function; Execute
2761 // Prepare Parameters
2762 // Assign Velocity at Impact
2763 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2764 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2765 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2767 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2768 // Execute VM Function
2769 PRVM_ExecuteProgram(PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2772 // Check for Engine Landing Sound
2773 if(sv_sound_land.string)
2774 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2776 ent->priv.server->waterposition_forceupdate = true;
2781 if (!SV_RunThink(ent))
2784 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2786 ent->priv.server->waterposition_forceupdate = false;
2787 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2788 SV_CheckWaterTransition(ent);
2792 //============================================================================
2794 static void SV_Physics_Entity (prvm_edict_t *ent)
2796 // don't run think/move on newly spawned projectiles as it messes up
2797 // movement interpolation and rocket trails, and is inconsistent with
2798 // respect to entities spawned in the same frame
2799 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2800 // but if it spawns a lower numbered ent, it doesn't - this never moves
2801 // ents in the first frame regardless)
2802 qboolean runmove = ent->priv.server->move;
2803 ent->priv.server->move = true;
2804 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2806 switch ((int) PRVM_serveredictfloat(ent, movetype))
2809 case MOVETYPE_FAKEPUSH:
2810 SV_Physics_Pusher (ent);
2813 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2814 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2817 case MOVETYPE_FOLLOW:
2818 SV_Physics_Follow (ent);
2820 case MOVETYPE_NOCLIP:
2821 if (SV_RunThink(ent))
2824 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2825 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2830 SV_Physics_Step (ent);
2833 if (SV_RunThink (ent))
2837 case MOVETYPE_BOUNCE:
2838 case MOVETYPE_BOUNCEMISSILE:
2839 case MOVETYPE_FLYMISSILE:
2841 case MOVETYPE_FLY_WORLDONLY:
2843 if (SV_RunThink (ent))
2844 SV_Physics_Toss (ent);
2846 case MOVETYPE_PHYSICS:
2847 if (SV_RunThink(ent))
2850 SV_LinkEdict_TouchAreaGrid(ent);
2854 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2859 void SV_Physics_ClientMove(void)
2862 ent = host_client->edict;
2864 // call player physics, this needs the proper frametime
2865 PRVM_serverglobalfloat(frametime) = sv.frametime;
2868 // call standard client pre-think, with frametime = 0
2869 PRVM_serverglobalfloat(time) = sv.time;
2870 PRVM_serverglobalfloat(frametime) = 0;
2871 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2872 PRVM_ExecuteProgram (PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2873 PRVM_serverglobalfloat(frametime) = sv.frametime;
2875 // make sure the velocity is sane (not a NaN)
2876 SV_CheckVelocity(ent);
2878 // perform MOVETYPE_WALK behavior
2881 // call standard player post-think, with frametime = 0
2882 PRVM_serverglobalfloat(time) = sv.time;
2883 PRVM_serverglobalfloat(frametime) = 0;
2884 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2885 PRVM_ExecuteProgram (PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2886 PRVM_serverglobalfloat(frametime) = sv.frametime;
2888 if(PRVM_serveredictfloat(ent, fixangle))
2890 // angle fixing was requested by physics code...
2891 // so store the current angles for later use
2892 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2893 host_client->fixangle_angles_set = TRUE;
2895 // and clear fixangle for the next frame
2896 PRVM_serveredictfloat(ent, fixangle) = 0;
2900 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2902 // don't do physics on disconnected clients, FrikBot relies on this
2903 if (!host_client->spawned)
2906 // make sure the velocity is sane (not a NaN)
2907 SV_CheckVelocity(ent);
2909 // don't run physics here if running asynchronously
2910 if (host_client->clmovement_inputtimeout <= 0)
2913 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2916 // make sure the velocity is still sane (not a NaN)
2917 SV_CheckVelocity(ent);
2919 // call standard client pre-think
2920 PRVM_serverglobalfloat(time) = sv.time;
2921 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2922 PRVM_ExecuteProgram(PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2924 // make sure the velocity is still sane (not a NaN)
2925 SV_CheckVelocity(ent);
2928 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2930 // don't do physics on disconnected clients, FrikBot relies on this
2931 if (!host_client->spawned)
2934 // make sure the velocity is sane (not a NaN)
2935 SV_CheckVelocity(ent);
2937 // call standard player post-think
2938 PRVM_serverglobalfloat(time) = sv.time;
2939 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2940 PRVM_ExecuteProgram(PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2942 // make sure the velocity is still sane (not a NaN)
2943 SV_CheckVelocity(ent);
2945 if(PRVM_serveredictfloat(ent, fixangle))
2947 // angle fixing was requested by physics code...
2948 // so store the current angles for later use
2949 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2950 host_client->fixangle_angles_set = TRUE;
2952 // and clear fixangle for the next frame
2953 PRVM_serveredictfloat(ent, fixangle) = 0;
2956 // decrement the countdown variable used to decide when to go back to
2957 // synchronous physics
2958 if (host_client->clmovement_inputtimeout > sv.frametime)
2959 host_client->clmovement_inputtimeout -= sv.frametime;
2961 host_client->clmovement_inputtimeout = 0;
2964 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2966 // don't do physics on disconnected clients, FrikBot relies on this
2967 if (!host_client->spawned)
2969 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2973 // make sure the velocity is sane (not a NaN)
2974 SV_CheckVelocity(ent);
2976 switch ((int) PRVM_serveredictfloat(ent, movetype))
2979 case MOVETYPE_FAKEPUSH:
2980 SV_Physics_Pusher (ent);
2983 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2984 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2987 case MOVETYPE_FOLLOW:
2988 SV_Physics_Follow (ent);
2990 case MOVETYPE_NOCLIP:
2993 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2994 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2997 SV_Physics_Step (ent);
3001 // don't run physics here if running asynchronously
3002 if (host_client->clmovement_inputtimeout <= 0)
3006 case MOVETYPE_BOUNCE:
3007 case MOVETYPE_BOUNCEMISSILE:
3008 case MOVETYPE_FLYMISSILE:
3011 SV_Physics_Toss (ent);
3014 case MOVETYPE_FLY_WORLDONLY:
3018 case MOVETYPE_PHYSICS:
3022 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3026 SV_CheckVelocity (ent);
3029 SV_LinkEdict_TouchAreaGrid(ent);
3031 SV_CheckVelocity (ent);
3040 void SV_Physics (void)
3045 // let the progs know that a new frame has started
3046 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3047 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3048 PRVM_serverglobalfloat(time) = sv.time;
3049 PRVM_serverglobalfloat(frametime) = sv.frametime;
3050 PRVM_ExecuteProgram (PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3052 // run physics engine
3053 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3056 // treat each object in turn
3059 // if force_retouch, relink all the entities
3060 if (PRVM_serverglobalfloat(force_retouch) > 0)
3061 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3062 if (!ent->priv.server->free)
3063 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3065 if (sv_gameplayfix_consistentplayerprethink.integer)
3067 // run physics on the client entities in 3 stages
3068 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3069 if (!ent->priv.server->free)
3070 SV_Physics_ClientEntity_PreThink(ent);
3072 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3073 if (!ent->priv.server->free)
3074 SV_Physics_ClientEntity(ent);
3076 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3077 if (!ent->priv.server->free)
3078 SV_Physics_ClientEntity_PostThink(ent);
3082 // run physics on the client entities
3083 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3085 if (!ent->priv.server->free)
3087 SV_Physics_ClientEntity_PreThink(ent);
3088 SV_Physics_ClientEntity(ent);
3089 SV_Physics_ClientEntity_PostThink(ent);
3094 // run physics on all the non-client entities
3095 if (!sv_freezenonclients.integer)
3097 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3098 if (!ent->priv.server->free)
3099 SV_Physics_Entity(ent);
3100 // make a second pass to see if any ents spawned this frame and make
3101 // sure they run their move/think
3102 if (sv_gameplayfix_delayprojectiles.integer < 0)
3103 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3104 if (!ent->priv.server->move && !ent->priv.server->free)
3105 SV_Physics_Entity(ent);
3108 if (PRVM_serverglobalfloat(force_retouch) > 0)
3109 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3111 // LordHavoc: endframe support
3112 if (PRVM_serverfunction(EndFrame))
3114 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3115 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3116 PRVM_serverglobalfloat(time) = sv.time;
3117 PRVM_ExecuteProgram (PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3120 // decrement prog->num_edicts if the highest number entities died
3121 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3123 if (!sv_freezenonclients.integer)
3124 sv.time += sv.frametime;