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 = SV_EntitiesInBox(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 = SV_EntitiesInBox(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 = SV_EntitiesInBox(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 = SV_EntitiesInBox(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 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
704 vec3_t paddedmins, paddedmaxs;
705 if (maxedicts < 1 || resultedicts == NULL)
707 VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
708 VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
709 if (sv_areadebug.integer)
711 int numresultedicts = 0;
714 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
716 ed = PRVM_EDICT_NUM(edictindex);
717 if (!ed->priv.required->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
719 resultedicts[numresultedicts++] = ed;
720 if (numresultedicts == maxedicts)
724 return numresultedicts;
727 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
730 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
732 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
733 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
734 PRVM_serverglobalfloat(time) = sv.time;
735 PRVM_serverglobalfloat(trace_allsolid) = false;
736 PRVM_serverglobalfloat(trace_startsolid) = false;
737 PRVM_serverglobalfloat(trace_fraction) = 1;
738 PRVM_serverglobalfloat(trace_inwater) = false;
739 PRVM_serverglobalfloat(trace_inopen) = true;
740 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
741 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
742 PRVM_serverglobalfloat(trace_plane_dist) = 0;
743 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
744 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
745 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
746 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
747 PRVM_serverglobalstring(trace_dphittexturename) = 0;
748 PRVM_ExecuteProgram (PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
751 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
753 int i, numtouchedicts, old_self, old_other;
755 static prvm_edict_t *touchedicts[MAX_EDICTS];
757 if (ent == prog->edicts)
758 return; // don't add the world
760 if (ent->priv.server->free)
763 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
766 // build a list of edicts to touch, because the link loop can be corrupted
767 // by IncreaseEdicts called during touch functions
768 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
769 if (numtouchedicts > MAX_EDICTS)
771 // this never happens
772 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
773 numtouchedicts = MAX_EDICTS;
776 old_self = PRVM_serverglobaledict(self);
777 old_other = PRVM_serverglobaledict(other);
778 for (i = 0;i < numtouchedicts;i++)
780 touch = touchedicts[i];
781 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
783 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
786 PRVM_serverglobaledict(self) = old_self;
787 PRVM_serverglobaledict(other) = old_other;
790 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
794 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
796 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
797 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
798 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
799 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];
800 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];
801 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
802 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];
803 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];
804 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
805 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];
806 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];
807 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
808 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
809 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
810 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
811 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
812 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
813 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
814 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
815 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
816 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
817 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
818 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
827 void SV_LinkEdict (prvm_edict_t *ent)
833 if (ent == prog->edicts)
834 return; // don't add the world
836 if (ent->priv.server->free)
839 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
840 if (modelindex < 0 || modelindex >= MAX_MODELS)
842 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
845 model = SV_GetModelByIndex(modelindex);
847 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
848 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
849 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
853 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
855 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
856 // TODO special handling for spheres?
857 RotateBBox(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), PRVM_serveredictvector(ent, angles), mins, maxs);
858 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
859 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
861 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
865 if (!model->TraceBox)
866 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
868 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
870 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
871 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
873 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
875 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
876 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
880 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
881 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
886 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
887 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
888 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
893 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
894 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
898 // to make items easier to pick up and allow them to be grabbed off
899 // of shelves, the abs sizes are expanded
901 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
912 // because movement is clipped an epsilon away from an actual edge,
913 // we must fully check even when bounding boxes don't quite touch
922 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
923 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
925 World_LinkEdict(&sv.world, ent, mins, maxs);
929 ===============================================================================
933 ===============================================================================
938 SV_TestEntityPosition
940 returns true if the entity is in solid currently
943 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
948 contents = SV_GenericHitSuperContentsMask(ent);
949 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
950 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);
951 if (trace.startsupercontents & contents)
955 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
957 // q1bsp/hlbsp use hulls and if the entity does not exactly match
958 // a hull size it is incorrectly tested, so this code tries to
959 // 'fix' it slightly...
960 // FIXME: this breaks entities larger than the hull size
963 VectorAdd(org, PRVM_serveredictvector(ent, mins), m1);
964 VectorAdd(org, PRVM_serveredictvector(ent, maxs), m2);
965 VectorSubtract(m2, m1, s);
966 #define EPSILON (1.0f / 32.0f)
967 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
968 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
969 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
970 for (i = 0;i < 8;i++)
972 v[0] = (i & 1) ? m2[0] : m1[0];
973 v[1] = (i & 2) ? m2[1] : m1[1];
974 v[2] = (i & 4) ? m2[2] : m1[2];
975 if (SV_PointSuperContents(v) & contents)
980 // if the trace found a better position for the entity, move it there
981 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
984 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
985 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
987 // verify if the endpos is REALLY outside solid
988 VectorCopy(trace.endpos, org);
989 trace = SV_TraceBox(org, PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), org, MOVE_NOMONSTERS, ent, contents);
991 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
993 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1004 void SV_CheckAllEnts (void)
1007 prvm_edict_t *check;
1009 // see if any solid entities are inside the final position
1010 check = PRVM_NEXT_EDICT(prog->edicts);
1011 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
1013 if (check->priv.server->free)
1015 if (PRVM_serveredictfloat(check, movetype) == MOVETYPE_PUSH
1016 || PRVM_serveredictfloat(check, movetype) == MOVETYPE_NONE
1017 || PRVM_serveredictfloat(check, movetype) == MOVETYPE_FOLLOW
1018 || PRVM_serveredictfloat(check, movetype) == MOVETYPE_NOCLIP
1019 || PRVM_serveredictfloat(check, movetype) == MOVETYPE_FLY_WORLDONLY)
1022 if (SV_TestEntityPosition (check, vec3_origin))
1023 Con_Print("entity in invalid position\n");
1027 // DRESK - Support for Entity Contents Transition Event
1030 SV_CheckContentsTransition
1032 returns true if entity had a valid contentstransition function call
1035 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
1037 int bValidFunctionCall;
1039 // Default Valid Function Call to False
1040 bValidFunctionCall = false;
1042 if(PRVM_serveredictfloat(ent, watertype) != nContents)
1043 { // Changed Contents
1044 // Acquire Contents Transition Function from QC
1045 if(PRVM_serveredictfunction(ent, contentstransition))
1046 { // Valid Function; Execute
1047 // Assign Valid Function
1048 bValidFunctionCall = true;
1049 // Prepare Parameters (Original Contents, New Contents)
1050 // Original Contents
1051 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
1053 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1055 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1056 // Execute VM Function
1057 PRVM_ExecuteProgram(PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1061 // Return if Function Call was Valid
1062 return bValidFunctionCall;
1071 void SV_CheckVelocity (prvm_edict_t *ent)
1079 for (i=0 ; i<3 ; i++)
1081 if (IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1083 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)));
1084 PRVM_serveredictvector(ent, velocity)[i] = 0;
1086 if (IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1088 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)));
1089 PRVM_serveredictvector(ent, origin)[i] = 0;
1093 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1094 // player_run/player_stand1 does not horribly malfunction if the
1095 // velocity becomes a denormalized float
1096 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0001)
1097 VectorClear(PRVM_serveredictvector(ent, velocity));
1099 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1100 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1101 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1103 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1104 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1105 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1106 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1114 Runs thinking code if time. There is some play in the exact time the think
1115 function will be called, because it is called before any movement is done
1116 in a frame. Not used for pushmove objects, because they must be exact.
1117 Returns false if the entity removed itself.
1120 qboolean SV_RunThink (prvm_edict_t *ent)
1124 // don't let things stay in the past.
1125 // it is possible to start that way by a trigger with a local time.
1126 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1129 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1131 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1132 PRVM_serveredictfloat(ent, nextthink) = 0;
1133 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1134 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1135 PRVM_ExecuteProgram (PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1136 // mods often set nextthink to time to cause a think every frame,
1137 // we don't want to loop in that case, so exit if the new nextthink is
1138 // <= the time the qc was told, also exit if it is past the end of the
1140 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1143 return !ent->priv.server->free;
1150 Two entities have touched, so run their touch functions
1153 extern void VM_SetTraceGlobals(const trace_t *trace);
1154 extern sizebuf_t vm_tempstringsbuf;
1155 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1157 int restorevm_tempstringsbuf_cursize;
1158 int old_self, old_other;
1159 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1161 old_self = PRVM_serverglobaledict(self);
1162 old_other = PRVM_serverglobaledict(other);
1163 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1165 VM_SetTraceGlobals(trace);
1167 PRVM_serverglobalfloat(time) = sv.time;
1168 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1170 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1171 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1172 PRVM_ExecuteProgram (PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1175 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1177 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1178 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1179 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1180 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1181 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1182 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1183 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1184 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1185 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1186 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1187 PRVM_ExecuteProgram (PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1190 PRVM_serverglobaledict(self) = old_self;
1191 PRVM_serverglobaledict(other) = old_other;
1192 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1200 Slide off of the impacting object
1201 returns the blocked flags (1 = floor, 2 = step / wall)
1204 #define STOP_EPSILON 0.1
1205 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1210 backoff = -DotProduct (in, normal) * overbounce;
1211 VectorMA(in, backoff, normal, out);
1213 for (i = 0;i < 3;i++)
1214 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1223 The basic solid body movement clip that slides along multiple planes
1224 Returns the clipflags if the velocity was modified (hit something solid)
1228 8 = teleported by touch method
1229 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1232 static float SV_Gravity (prvm_edict_t *ent);
1233 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1234 #define MAX_CLIP_PLANES 5
1235 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1237 int blocked, bumpcount;
1238 int i, j, numplanes;
1239 float d, time_left, gravity;
1240 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1249 if(sv_gameplayfix_nogravityonground.integer)
1250 if((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
1251 applygravity = false;
1255 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1257 gravity = SV_Gravity(ent) * 0.5f;
1258 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1262 applygravity = false;
1263 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
1267 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1268 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1271 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1273 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1276 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1277 if(!SV_PushEntity(&trace, ent, push, false, false))
1279 // we got teleported by a touch function
1280 // let's abort the move
1285 if (trace.fraction == 1)
1287 if (trace.plane.normal[2])
1289 if (trace.plane.normal[2] > 0.7)
1296 Con_Printf ("SV_FlyMove: !trace.ent");
1297 trace.ent = prog->edicts;
1300 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1301 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1304 else if (stepheight)
1306 // step - handle it immediately
1312 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1313 VectorSet(steppush, 0, 0, stepheight);
1314 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1315 if(!SV_PushEntity(&steptrace, ent, steppush, false, false))
1320 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1321 if(!SV_PushEntity(&steptrace2, ent, push, false, false))
1326 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1327 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1328 if(!SV_PushEntity(&steptrace3, ent, steppush, false, false))
1333 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1334 // accept the new position if it made some progress...
1335 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1337 //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]);
1339 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1340 time_left *= 1 - trace.fraction;
1346 //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]);
1347 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1352 // step - return it to caller
1354 // save the trace for player extrafriction
1356 VectorCopy(trace.plane.normal, stepnormal);
1358 if (trace.fraction >= 0.001)
1360 // actually covered some distance
1361 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1365 time_left *= 1 - trace.fraction;
1367 // clipped to another plane
1368 if (numplanes >= MAX_CLIP_PLANES)
1370 // this shouldn't really happen
1371 VectorClear(PRVM_serveredictvector(ent, velocity));
1377 for (i = 0;i < numplanes;i++)
1378 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1382 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1387 VectorCopy(trace.plane.normal, planes[numplanes]);
1390 // modify original_velocity so it parallels all of the clip planes
1391 for (i = 0;i < numplanes;i++)
1393 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1394 for (j = 0;j < numplanes;j++)
1399 if (DotProduct(new_velocity, planes[j]) < 0)
1409 // go along this plane
1410 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1414 // go along the crease
1417 VectorClear(PRVM_serveredictvector(ent, velocity));
1421 CrossProduct(planes[0], planes[1], dir);
1422 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1423 VectorNormalize(dir);
1424 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1425 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1428 // if current velocity is against the original velocity,
1429 // stop dead to avoid tiny occilations in sloping corners
1430 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1432 VectorClear(PRVM_serveredictvector(ent, velocity));
1437 //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]);
1440 if ((blocked & 1) == 0 && bumpcount > 1)
1442 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1443 // flag ONGROUND if there's ground under it
1444 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask);
1448 // LordHavoc: this came from QW and allows you to get out of water more easily
1449 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1450 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1451 if (applygravity && !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1452 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1462 static float SV_Gravity (prvm_edict_t *ent)
1466 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1469 return ent_gravity * sv_gravity.value * sv.frametime;
1474 ===============================================================================
1478 ===============================================================================
1481 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1486 vec3_t stuckmins, stuckmaxs;
1487 vec3_t goodmins, goodmaxs;
1491 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1492 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1493 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1494 VectorCopy(pivot, goodmins);
1495 VectorCopy(pivot, goodmaxs);
1496 for (bump = 0;bump < 6;bump++)
1498 int coord = 2-(bump >> 1);
1499 //int coord = (bump >> 1);
1500 int dir = (bump & 1);
1503 for(subbump = 0; ; ++subbump)
1505 VectorCopy(stuckorigin, testorigin);
1509 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1514 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1517 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1518 if (stucktrace.bmodelstartsolid)
1520 // BAD BAD, can't fix that
1524 if (stucktrace.fraction >= 1)
1529 // BAD BAD, can't fix that
1533 // we hit something... let's move out of it
1534 VectorSubtract(stucktrace.endpos, testorigin, move);
1535 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1536 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1540 Con_Printf("subbump: %d\n", subbump);
1546 goodmaxs[coord] = stuckmaxs[coord];
1551 goodmins[coord] = stuckmins[coord];
1556 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1561 static qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1566 vec3_t stuckmins, stuckmaxs;
1568 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1569 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1570 separation = 0.0f; // when using hulls, it can not be enlarged
1571 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1572 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1573 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1574 stuckmins[0] -= separation;
1575 stuckmins[1] -= separation;
1576 stuckmins[2] -= separation;
1577 stuckmaxs[0] += separation;
1578 stuckmaxs[1] += separation;
1579 stuckmaxs[2] += separation;
1580 for (bump = 0;bump < 10;bump++)
1582 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1583 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1585 // found a good location, use it
1586 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1589 nudge = -stucktrace.startdepth;
1590 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1599 Does not change the entities velocity at all
1600 The trace struct is filled with the trace that has been done.
1601 Returns true if the push did not result in the entity being teleported by QC code.
1604 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1610 vec3_t original, original_velocity;
1614 solid = (int)PRVM_serveredictfloat(ent, solid);
1615 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1616 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1617 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1619 // move start position out of solids
1620 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1622 SV_NudgeOutOfSolid(ent);
1625 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1626 VectorAdd(start, push, end);
1628 if (movetype == MOVETYPE_FLYMISSILE)
1629 type = MOVE_MISSILE;
1630 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1631 type = MOVE_WORLDONLY;
1632 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1633 type = MOVE_NOMONSTERS; // only clip against bmodels
1637 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1638 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1641 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1643 VectorCopy(PRVM_serveredictvector(ent, origin), original);
1644 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1649 if(!trace->startsolid)
1650 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)
1652 Con_Printf("something eeeeevil happened\n");
1657 SV_LinkEdict_TouchAreaGrid(ent);
1659 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))))
1660 SV_Impact (ent, trace);
1662 return VectorCompare(PRVM_serveredictvector(ent, origin), original) && VectorCompare(PRVM_serveredictvector(ent, velocity), original_velocity);
1672 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1675 int pusherowner, pusherprog;
1678 float savesolid, movetime2, pushltime;
1679 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1681 int numcheckentities;
1682 static prvm_edict_t *checkentities[MAX_EDICTS];
1683 dp_model_t *pushermodel;
1684 trace_t trace, trace2;
1685 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1686 static unsigned short moved_edicts[MAX_EDICTS];
1689 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])
1691 PRVM_serveredictfloat(pusher, ltime) += movetime;
1695 switch ((int) PRVM_serveredictfloat(pusher, solid))
1697 // LordHavoc: valid pusher types
1700 case SOLID_SLIDEBOX:
1701 case SOLID_CORPSE: // LordHavoc: this would be weird...
1703 // LordHavoc: no collisions
1706 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1707 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1708 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1709 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1710 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1711 PRVM_serveredictfloat(pusher, ltime) += movetime;
1712 SV_LinkEdict(pusher);
1715 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1718 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1719 if (index < 1 || index >= MAX_MODELS)
1721 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1724 pushermodel = SV_GetModelByIndex(index);
1725 pusherowner = PRVM_serveredictedict(pusher, owner);
1726 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1728 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1730 movetime2 = movetime;
1731 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1732 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1733 if (moveangle[0] || moveangle[2])
1735 for (i = 0;i < 3;i++)
1739 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1740 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1744 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1745 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1749 else if (moveangle[1])
1751 for (i = 0;i < 3;i++)
1755 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1756 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1760 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1761 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1767 for (i = 0;i < 3;i++)
1771 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1772 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1776 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1777 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1782 VectorNegate (moveangle, a);
1783 AngleVectorsFLU (a, forward, left, up);
1785 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1786 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1787 pushltime = PRVM_serveredictfloat(pusher, ltime);
1789 // move the pusher to its final position
1791 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1792 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1793 PRVM_serveredictfloat(pusher, ltime) += movetime;
1794 SV_LinkEdict(pusher);
1796 pushermodel = SV_GetModelFromEdict(pusher);
1797 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);
1798 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1800 savesolid = PRVM_serveredictfloat(pusher, solid);
1802 // see if any solid entities are inside the final position
1805 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1806 numcheckentities = 0;
1807 else // MOVETYPE_PUSH
1808 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1809 for (e = 0;e < numcheckentities;e++)
1811 prvm_edict_t *check = checkentities[e];
1812 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1817 case MOVETYPE_FOLLOW:
1818 case MOVETYPE_NOCLIP:
1819 case MOVETYPE_FLY_WORLDONLY:
1825 if (PRVM_serveredictedict(check, owner) == pusherprog)
1828 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1831 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1833 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1834 check->priv.server->waterposition_forceupdate = true;
1836 checkcontents = SV_GenericHitSuperContentsMask(check);
1838 // if the entity is standing on the pusher, it will definitely be moved
1839 // if the entity is not standing on the pusher, but is in the pusher's
1840 // final position, move it
1841 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1843 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);
1844 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1845 if (!trace.startsolid)
1847 //Con_Printf("- not in solid\n");
1852 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1853 //VectorClear(pivot);
1858 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1859 VectorAdd (org, pivot, org);
1860 org2[0] = DotProduct (org, forward);
1861 org2[1] = DotProduct (org, left);
1862 org2[2] = DotProduct (org, up);
1863 VectorSubtract (org2, org, move);
1864 VectorAdd (move, move1, move);
1867 VectorCopy (move1, move);
1869 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1871 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1872 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1873 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1875 // physics objects need better collisions than this code can do
1876 if (movetype == MOVETYPE_PHYSICS)
1878 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1879 SV_LinkEdict(check);
1880 SV_LinkEdict_TouchAreaGrid(check);
1884 // try moving the contacted entity
1885 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1886 if(!SV_PushEntity (&trace, check, move, true, true))
1888 // entity "check" got teleported
1889 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1890 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1891 continue; // pushed enough
1893 // FIXME: turn players specially
1894 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1895 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1896 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1898 // this trace.fraction < 1 check causes items to fall off of pushers
1899 // if they pass under or through a wall
1900 // the groundentity check causes items to fall off of ledges
1901 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1902 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1904 // if it is still inside the pusher, block
1905 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);
1906 if (trace.startsolid)
1909 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1911 // hack to invoke all necessary movement triggers
1913 if(!SV_PushEntity(&trace2, check, move2, true, true))
1915 // entity "check" got teleported
1922 // still inside pusher, so it's really blocked
1925 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1927 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1930 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1931 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1935 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1936 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1937 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1938 SV_LinkEdict(pusher);
1940 // move back any entities we already moved
1941 for (i = 0;i < num_moved;i++)
1943 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1944 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1945 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1949 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1950 if (PRVM_serveredictfunction(pusher, blocked))
1952 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1953 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1954 PRVM_ExecuteProgram (PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1959 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1960 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1961 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1970 void SV_Physics_Pusher (prvm_edict_t *ent)
1972 float thinktime, oldltime, movetime;
1974 oldltime = PRVM_serveredictfloat(ent, ltime);
1976 thinktime = PRVM_serveredictfloat(ent, nextthink);
1977 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1979 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1984 movetime = sv.frametime;
1987 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
1988 SV_PushMove (ent, movetime);
1990 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
1992 PRVM_serveredictfloat(ent, nextthink) = 0;
1993 PRVM_serverglobalfloat(time) = sv.time;
1994 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1995 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1996 PRVM_ExecuteProgram (PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2002 ===============================================================================
2006 ===============================================================================
2009 static float unstickoffsets[] =
2011 // poutting -/+z changes first as they are least weird
2026 typedef enum unstickresult_e
2034 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2038 // if not stuck in a bmodel, just return
2039 if (!SV_TestEntityPosition(ent, vec3_origin))
2040 return UNSTICK_GOOD;
2042 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2044 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2046 VectorCopy(unstickoffsets + i, offset);
2048 //SV_LinkEdict_TouchAreaGrid(ent);
2049 return UNSTICK_UNSTUCK;
2053 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2054 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2056 for(i = 2; i <= maxunstick; ++i)
2058 VectorClear(offset);
2060 if (!SV_TestEntityPosition(ent, offset))
2063 //SV_LinkEdict_TouchAreaGrid(ent);
2064 return UNSTICK_UNSTUCK;
2067 if (!SV_TestEntityPosition(ent, offset))
2070 //SV_LinkEdict_TouchAreaGrid(ent);
2071 return UNSTICK_UNSTUCK;
2075 return UNSTICK_STUCK;
2078 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2081 switch(SV_UnstickEntityReturnOffset(ent, offset))
2085 case UNSTICK_UNSTUCK:
2086 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]);
2089 if (developer_extra.integer)
2090 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)));
2093 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2102 This is a big hack to try and fix the rare case of getting stuck in the world
2106 void SV_CheckStuck (prvm_edict_t *ent)
2110 switch(SV_UnstickEntityReturnOffset(ent, offset))
2113 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2115 case UNSTICK_UNSTUCK:
2116 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]);
2119 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2120 if (!SV_TestEntityPosition(ent, offset))
2122 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)));
2124 //SV_LinkEdict_TouchAreaGrid(ent);
2127 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(PRVM_serveredictstring(ent, classname)));
2130 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2140 qboolean SV_CheckWater (prvm_edict_t *ent)
2143 int nNativeContents;
2146 point[0] = PRVM_serveredictvector(ent, origin)[0];
2147 point[1] = PRVM_serveredictvector(ent, origin)[1];
2148 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2150 // DRESK - Support for Entity Contents Transition Event
2151 // NOTE: Some logic needed to be slightly re-ordered
2152 // to not affect performance and allow for the feature.
2154 // Acquire Super Contents Prior to Resets
2155 cont = SV_PointSuperContents(point);
2156 // Acquire Native Contents Here
2157 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2159 // DRESK - Support for Entity Contents Transition Event
2160 if(PRVM_serveredictfloat(ent, watertype))
2161 // Entity did NOT Spawn; Check
2162 SV_CheckContentsTransition(ent, nNativeContents);
2165 PRVM_serveredictfloat(ent, waterlevel) = 0;
2166 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2167 cont = SV_PointSuperContents(point);
2168 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2170 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2171 PRVM_serveredictfloat(ent, waterlevel) = 1;
2172 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2173 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2175 PRVM_serveredictfloat(ent, waterlevel) = 2;
2176 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2177 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2178 PRVM_serveredictfloat(ent, waterlevel) = 3;
2182 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2191 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2194 vec3_t forward, into, side;
2196 AngleVectors (PRVM_serveredictvector(ent, v_angle), forward, NULL, NULL);
2197 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2199 // cut the tangential velocity
2200 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2201 VectorScale (stepnormal, i, into);
2202 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2203 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2204 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2210 =====================
2213 Player has come to a dead stop, possibly due to the problem with limited
2214 float precision at some angle joins in the BSP hull.
2216 Try fixing by pushing one pixel in each direction.
2218 This is a hack, but in the interest of good gameplay...
2219 ======================
2221 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2226 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2229 for (i=0 ; i<8 ; i++)
2231 // try pushing a little in an axial direction
2234 case 0: dir[0] = 2; dir[1] = 0; break;
2235 case 1: dir[0] = 0; dir[1] = 2; break;
2236 case 2: dir[0] = -2; dir[1] = 0; break;
2237 case 3: dir[0] = 0; dir[1] = -2; break;
2238 case 4: dir[0] = 2; dir[1] = 2; break;
2239 case 5: dir[0] = -2; dir[1] = 2; break;
2240 case 6: dir[0] = 2; dir[1] = -2; break;
2241 case 7: dir[0] = -2; dir[1] = -2; break;
2244 SV_PushEntity (&trace, ent, dir, false, true);
2246 // retry the original move
2247 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2248 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2249 PRVM_serveredictvector(ent, velocity)[2] = 0;
2250 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2252 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2253 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2255 Con_DPrint("TryUnstick - success.\n");
2259 // go back to the original pos and try again
2260 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2264 VectorClear (PRVM_serveredictvector(ent, velocity));
2265 Con_DPrint("TryUnstick - failure.\n");
2271 =====================
2274 Only used by players
2275 ======================
2277 void SV_WalkMove (prvm_edict_t *ent)
2281 //int originalmove_clip;
2282 int originalmove_flags;
2283 int originalmove_groundentity;
2284 int hitsupercontentsmask;
2286 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2287 trace_t downtrace, trace;
2288 qboolean applygravity;
2290 // if frametime is 0 (due to client sending the same timestamp twice),
2292 if (sv.frametime <= 0)
2295 if (sv_gameplayfix_unstickplayers.integer)
2296 SV_CheckStuck (ent);
2298 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2300 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2302 SV_CheckVelocity(ent);
2304 // do a regular slide move unless it looks like you ran into a step
2305 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2307 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2308 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2310 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2312 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2315 // only try this if there was no floor in the way in the trace (no,
2316 // this check seems to be not REALLY necessary, because if clip & 1,
2317 // our trace will hit that thing too)
2318 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2319 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2320 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2321 type = MOVE_MISSILE;
2322 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2323 type = MOVE_WORLDONLY;
2324 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2325 type = MOVE_NOMONSTERS; // only clip against bmodels
2328 trace = SV_TraceBox(upmove, PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2329 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2330 clip |= 1; // but we HAVE found a floor
2333 // if the move did not hit the ground at any point, we're not on ground
2335 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2337 SV_CheckVelocity(ent);
2339 SV_LinkEdict_TouchAreaGrid(ent);
2341 if(clip & 8) // teleport
2344 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2347 if (sv_nostep.integer)
2350 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2351 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2352 //originalmove_clip = clip;
2353 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2354 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2356 // if move didn't block on a step, return
2359 // if move was not trying to move into the step, return
2360 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2363 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2365 // return if gibbed by a trigger
2366 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2369 // only step up while jumping if that is enabled
2370 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2371 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2375 // try moving up and forward to go up a step
2376 // back to start pos
2377 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2378 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2381 VectorClear (upmove);
2382 upmove[2] = sv_stepheight.value;
2383 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2385 // we got teleported when upstepping... must abort the move
2390 PRVM_serveredictvector(ent, velocity)[2] = 0;
2391 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2392 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2395 // we got teleported when upstepping... must abort the move
2396 // note that z velocity handling may not be what QC expects here, but we cannot help it
2400 SV_CheckVelocity(ent);
2402 SV_LinkEdict_TouchAreaGrid(ent);
2404 // check for stuckness, possibly due to the limited precision of floats
2405 // in the clipping hulls
2407 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2408 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2410 //Con_Printf("wall\n");
2411 // stepping up didn't make any progress, revert to original move
2412 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2413 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2414 //clip = originalmove_clip;
2415 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2416 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2417 // now try to unstick if needed
2418 //clip = SV_TryUnstick (ent, oldvel);
2422 //Con_Printf("step - ");
2424 // extra friction based on view angle
2425 if (clip & 2 && sv_wallfriction.integer)
2426 SV_WallFriction (ent, stepnormal);
2428 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2429 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))
2433 VectorClear (downmove);
2434 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2435 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2437 // we got teleported when downstepping... must abort the move
2441 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2443 // this has been disabled so that you can't jump when you are stepping
2444 // up while already jumping (also known as the Quake2 double jump bug)
2446 // LordHavoc: disabled this check so you can walk on monsters/players
2447 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2449 //Con_Printf("onground\n");
2450 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2451 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2457 //Con_Printf("slope\n");
2458 // if the push down didn't end up on good ground, use the move without
2459 // the step up. This happens near wall / slope combinations, and can
2460 // cause the player to hop up higher on a slope too steep to climb
2461 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2462 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2463 //clip = originalmove_clip;
2464 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2465 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2468 SV_CheckVelocity(ent);
2470 SV_LinkEdict_TouchAreaGrid(ent);
2473 //============================================================================
2479 Entities that are "stuck" to another entity
2482 void SV_Physics_Follow (prvm_edict_t *ent)
2484 vec3_t vf, vr, vu, angles, v;
2488 if (!SV_RunThink (ent))
2491 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2492 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2493 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])
2495 // quick case for no rotation
2496 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2500 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2501 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2502 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2503 AngleVectors (angles, vf, vr, vu);
2504 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];
2505 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];
2506 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];
2507 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2508 angles[1] = PRVM_serveredictvector(e, angles)[1];
2509 angles[2] = PRVM_serveredictvector(e, angles)[2];
2510 AngleVectors (angles, vf, vr, vu);
2511 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2512 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2513 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2515 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2517 //SV_LinkEdict_TouchAreaGrid(ent);
2521 ==============================================================================
2525 ==============================================================================
2530 SV_CheckWaterTransition
2534 void SV_CheckWaterTransition (prvm_edict_t *ent)
2536 // 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
2538 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(PRVM_serveredictvector(ent, origin)));
2539 if (!PRVM_serveredictfloat(ent, watertype))
2541 // just spawned here
2542 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2544 PRVM_serveredictfloat(ent, watertype) = cont;
2545 PRVM_serveredictfloat(ent, waterlevel) = 1;
2549 // DRESK - Support for Entity Contents Transition Event
2550 // NOTE: Call here BEFORE updating the watertype below,
2551 // and suppress watersplash sound if a valid function
2552 // call was made to allow for custom "splash" sounds.
2553 else if( !SV_CheckContentsTransition(ent, cont) )
2554 { // Contents Transition Function Invalid; Potentially Play Water Sound
2555 // check if the entity crossed into or out of water
2556 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2557 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2560 if (cont <= CONTENTS_WATER)
2562 PRVM_serveredictfloat(ent, watertype) = cont;
2563 PRVM_serveredictfloat(ent, waterlevel) = 1;
2567 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2568 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2576 Toss, bounce, and fly movement. When onground, do nothing.
2580 void SV_Physics_Toss (prvm_edict_t *ent)
2586 prvm_edict_t *groundentity;
2588 // if onground, return without moving
2589 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2591 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2592 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2594 // don't stick to ground if onground and moving upward
2595 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2597 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2599 // we can trust FL_ONGROUND if groundentity is world because it never moves
2602 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2604 // if ent was supported by a brush model on previous frame,
2605 // and groundentity is now freed, set groundentity to 0 (world)
2606 // which leaves it suspended in the air
2607 PRVM_serveredictedict(ent, groundentity) = 0;
2608 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2611 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2613 // don't slide if still touching the groundentity
2617 ent->priv.server->suspendedinairflag = false;
2619 SV_CheckVelocity (ent);
2622 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2623 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2626 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2628 movetime = sv.frametime;
2629 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2632 VectorScale (PRVM_serveredictvector(ent, velocity), movetime, move);
2633 if(!SV_PushEntity (&trace, ent, move, true, true))
2634 return; // teleported
2635 if (ent->priv.server->free)
2637 if (trace.bmodelstartsolid)
2639 // try to unstick the entity
2640 if (sv_gameplayfix_unstickentities.integer)
2641 SV_UnstickEntity(ent);
2642 if(!SV_PushEntity (&trace, ent, move, false, true))
2643 return; // teleported
2644 if (ent->priv.server->free)
2647 if (trace.fraction == 1)
2649 movetime *= 1 - min(1, trace.fraction);
2650 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCEMISSILE)
2653 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2655 bouncefactor = 1.0f;
2657 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2658 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2660 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2662 float d, ent_gravity;
2666 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2668 bouncefactor = 0.5f;
2670 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2672 bouncestop = 60.0f / 800.0f;
2674 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2675 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2678 // LordHavoc: fixed grenades not bouncing when fired down a slope
2679 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2681 d = DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity));
2682 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2684 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2685 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2686 VectorClear (PRVM_serveredictvector(ent, velocity));
2687 VectorClear (PRVM_serveredictvector(ent, avelocity));
2690 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2694 if (trace.plane.normal[2] > 0.7 && PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * bouncestop * ent_gravity)
2696 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2697 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2698 VectorClear (PRVM_serveredictvector(ent, velocity));
2699 VectorClear (PRVM_serveredictvector(ent, avelocity));
2702 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2707 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2708 if (trace.plane.normal[2] > 0.7)
2710 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2711 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2712 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2713 ent->priv.server->suspendedinairflag = true;
2714 VectorClear (PRVM_serveredictvector(ent, velocity));
2715 VectorClear (PRVM_serveredictvector(ent, avelocity));
2718 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2720 if (!sv_gameplayfix_slidemoveprojectiles.integer || (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_BOUNCE && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCEMISSILE) || ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
2724 // check for in water
2725 SV_CheckWaterTransition (ent);
2729 ===============================================================================
2733 ===============================================================================
2740 Monsters freefall when they don't have a ground entity, otherwise
2741 all movement is done with discrete steps.
2743 This is also used for objects that have become still on the ground, but
2744 will fall if the floor is pulled out from under them.
2747 void SV_Physics_Step (prvm_edict_t *ent)
2749 int flags = (int)PRVM_serveredictfloat(ent, flags);
2752 // Backup Velocity in the event that movetypesteplandevent is called,
2753 // to provide a parameter with the entity's velocity at impact.
2754 vec3_t backupVelocity;
2755 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2756 // don't fall at all if fly/swim
2757 if (!(flags & (FL_FLY | FL_SWIM)))
2759 if (flags & FL_ONGROUND)
2761 // freefall if onground and moving upward
2762 // freefall if not standing on a world surface (it may be a lift or trap door)
2763 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2765 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2766 SV_CheckVelocity(ent);
2767 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2769 SV_LinkEdict_TouchAreaGrid(ent);
2770 ent->priv.server->waterposition_forceupdate = true;
2775 // freefall if not onground
2776 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2778 SV_CheckVelocity(ent);
2779 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2781 SV_LinkEdict_TouchAreaGrid(ent);
2784 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2786 // DRESK - Check for Entity Land Event Function
2787 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2788 { // Valid Function; Execute
2789 // Prepare Parameters
2790 // Assign Velocity at Impact
2791 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2792 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2793 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2795 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2796 // Execute VM Function
2797 PRVM_ExecuteProgram(PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2800 // Check for Engine Landing Sound
2801 if(sv_sound_land.string)
2802 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2804 ent->priv.server->waterposition_forceupdate = true;
2809 if (!SV_RunThink(ent))
2812 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2814 ent->priv.server->waterposition_forceupdate = false;
2815 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2816 SV_CheckWaterTransition(ent);
2820 //============================================================================
2822 static void SV_Physics_Entity (prvm_edict_t *ent)
2824 // don't run think/move on newly spawned projectiles as it messes up
2825 // movement interpolation and rocket trails, and is inconsistent with
2826 // respect to entities spawned in the same frame
2827 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2828 // but if it spawns a lower numbered ent, it doesn't - this never moves
2829 // ents in the first frame regardless)
2830 qboolean runmove = ent->priv.server->move;
2831 ent->priv.server->move = true;
2832 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2834 switch ((int) PRVM_serveredictfloat(ent, movetype))
2837 case MOVETYPE_FAKEPUSH:
2838 SV_Physics_Pusher (ent);
2841 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2842 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2845 case MOVETYPE_FOLLOW:
2846 SV_Physics_Follow (ent);
2848 case MOVETYPE_NOCLIP:
2849 if (SV_RunThink(ent))
2852 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2853 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2858 SV_Physics_Step (ent);
2861 if (SV_RunThink (ent))
2865 case MOVETYPE_BOUNCE:
2866 case MOVETYPE_BOUNCEMISSILE:
2867 case MOVETYPE_FLYMISSILE:
2869 case MOVETYPE_FLY_WORLDONLY:
2871 if (SV_RunThink (ent))
2872 SV_Physics_Toss (ent);
2874 case MOVETYPE_PHYSICS:
2875 if (SV_RunThink(ent))
2878 SV_LinkEdict_TouchAreaGrid(ent);
2882 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2887 void SV_Physics_ClientMove(void)
2890 ent = host_client->edict;
2892 // call player physics, this needs the proper frametime
2893 PRVM_serverglobalfloat(frametime) = sv.frametime;
2896 // call standard client pre-think, with frametime = 0
2897 PRVM_serverglobalfloat(time) = sv.time;
2898 PRVM_serverglobalfloat(frametime) = 0;
2899 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2900 PRVM_ExecuteProgram (PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2901 PRVM_serverglobalfloat(frametime) = sv.frametime;
2903 // make sure the velocity is sane (not a NaN)
2904 SV_CheckVelocity(ent);
2906 // perform MOVETYPE_WALK behavior
2909 // call standard player post-think, with frametime = 0
2910 PRVM_serverglobalfloat(time) = sv.time;
2911 PRVM_serverglobalfloat(frametime) = 0;
2912 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2913 PRVM_ExecuteProgram (PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2914 PRVM_serverglobalfloat(frametime) = sv.frametime;
2916 if(PRVM_serveredictfloat(ent, fixangle))
2918 // angle fixing was requested by physics code...
2919 // so store the current angles for later use
2920 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2921 host_client->fixangle_angles_set = TRUE;
2923 // and clear fixangle for the next frame
2924 PRVM_serveredictfloat(ent, fixangle) = 0;
2928 static void SV_Physics_ClientEntity_PreThink(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 // don't run physics here if running asynchronously
2938 if (host_client->clmovement_inputtimeout <= 0)
2941 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2944 // make sure the velocity is still sane (not a NaN)
2945 SV_CheckVelocity(ent);
2947 // call standard client pre-think
2948 PRVM_serverglobalfloat(time) = sv.time;
2949 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2950 PRVM_ExecuteProgram(PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2952 // make sure the velocity is still sane (not a NaN)
2953 SV_CheckVelocity(ent);
2956 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2958 // don't do physics on disconnected clients, FrikBot relies on this
2959 if (!host_client->spawned)
2962 // make sure the velocity is sane (not a NaN)
2963 SV_CheckVelocity(ent);
2965 // call standard player post-think
2966 PRVM_serverglobalfloat(time) = sv.time;
2967 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2968 PRVM_ExecuteProgram(PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2970 // make sure the velocity is still sane (not a NaN)
2971 SV_CheckVelocity(ent);
2973 if(PRVM_serveredictfloat(ent, fixangle))
2975 // angle fixing was requested by physics code...
2976 // so store the current angles for later use
2977 memcpy(host_client->fixangle_angles, PRVM_serveredictvector(ent, angles), sizeof(host_client->fixangle_angles));
2978 host_client->fixangle_angles_set = TRUE;
2980 // and clear fixangle for the next frame
2981 PRVM_serveredictfloat(ent, fixangle) = 0;
2984 // decrement the countdown variable used to decide when to go back to
2985 // synchronous physics
2986 if (host_client->clmovement_inputtimeout > sv.frametime)
2987 host_client->clmovement_inputtimeout -= sv.frametime;
2989 host_client->clmovement_inputtimeout = 0;
2992 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2994 // don't do physics on disconnected clients, FrikBot relies on this
2995 if (!host_client->spawned)
2997 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3001 // make sure the velocity is sane (not a NaN)
3002 SV_CheckVelocity(ent);
3004 switch ((int) PRVM_serveredictfloat(ent, movetype))
3007 case MOVETYPE_FAKEPUSH:
3008 SV_Physics_Pusher (ent);
3011 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3012 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3015 case MOVETYPE_FOLLOW:
3016 SV_Physics_Follow (ent);
3018 case MOVETYPE_NOCLIP:
3021 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3022 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3025 SV_Physics_Step (ent);
3029 // don't run physics here if running asynchronously
3030 if (host_client->clmovement_inputtimeout <= 0)
3034 case MOVETYPE_BOUNCE:
3035 case MOVETYPE_BOUNCEMISSILE:
3036 case MOVETYPE_FLYMISSILE:
3039 SV_Physics_Toss (ent);
3042 case MOVETYPE_FLY_WORLDONLY:
3046 case MOVETYPE_PHYSICS:
3050 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3054 SV_CheckVelocity (ent);
3057 SV_LinkEdict_TouchAreaGrid(ent);
3059 SV_CheckVelocity (ent);
3068 void SV_Physics (void)
3073 // let the progs know that a new frame has started
3074 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3075 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3076 PRVM_serverglobalfloat(time) = sv.time;
3077 PRVM_serverglobalfloat(frametime) = sv.frametime;
3078 PRVM_ExecuteProgram (PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3080 // run physics engine
3081 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3084 // treat each object in turn
3087 // if force_retouch, relink all the entities
3088 if (PRVM_serverglobalfloat(force_retouch) > 0)
3089 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3090 if (!ent->priv.server->free)
3091 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3093 if (sv_gameplayfix_consistentplayerprethink.integer)
3095 // run physics on the client entities in 3 stages
3096 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3097 if (!ent->priv.server->free)
3098 SV_Physics_ClientEntity_PreThink(ent);
3100 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3101 if (!ent->priv.server->free)
3102 SV_Physics_ClientEntity(ent);
3104 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3105 if (!ent->priv.server->free)
3106 SV_Physics_ClientEntity_PostThink(ent);
3110 // run physics on the client entities
3111 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3113 if (!ent->priv.server->free)
3115 SV_Physics_ClientEntity_PreThink(ent);
3116 SV_Physics_ClientEntity(ent);
3117 SV_Physics_ClientEntity_PostThink(ent);
3122 // run physics on all the non-client entities
3123 if (!sv_freezenonclients.integer)
3125 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3126 if (!ent->priv.server->free)
3127 SV_Physics_Entity(ent);
3128 // make a second pass to see if any ents spawned this frame and make
3129 // sure they run their move/think
3130 if (sv_gameplayfix_delayprojectiles.integer < 0)
3131 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3132 if (!ent->priv.server->move && !ent->priv.server->free)
3133 SV_Physics_Entity(ent);
3136 if (PRVM_serverglobalfloat(force_retouch) > 0)
3137 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3139 // LordHavoc: endframe support
3140 if (PRVM_serverfunction(EndFrame))
3142 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3143 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3144 PRVM_serverglobalfloat(time) = sv.time;
3145 PRVM_ExecuteProgram (PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3148 // decrement prog->num_edicts if the highest number entities died
3149 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3151 if (!sv_freezenonclients.integer)
3152 sv.time += sv.frametime;