2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "prvm_cmds.h"
28 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
30 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
32 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
33 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
34 corpses are SOLID_NOT and MOVETYPE_TOSS
35 crates are SOLID_BBOX and MOVETYPE_TOSS
36 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
37 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
39 solid_edge items only clip against bsp models.
43 #define MOVE_EPSILON 0.01
45 void SV_Physics_Toss (prvm_edict_t *ent);
47 int SV_GetPitchSign(prvm_prog_t *prog, prvm_edict_t *ent)
51 (model = SV_GetModelFromEdict(ent))
53 model->type == mod_alias
56 (((unsigned char)PRVM_serveredictfloat(ent, pflags)) & PFLAGS_FULLDYNAMIC)
58 ((gamemode == GAME_TENEBRAE) && ((unsigned int)PRVM_serveredictfloat(ent, effects) & (16 | 32)))
66 ===============================================================================
70 ===============================================================================
73 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
75 prvm_prog_t *prog = SVVM_prog;
78 int dphitcontentsmask = (int)PRVM_serveredictfloat(passedict, dphitcontentsmask);
79 if (dphitcontentsmask)
80 return dphitcontentsmask;
81 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_SLIDEBOX)
83 if ((int)PRVM_serveredictfloat(passedict, flags) & FL_MONSTER)
84 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
86 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
88 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_CORPSE)
89 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
90 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_TRIGGER)
91 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
93 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
96 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
104 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
106 prvm_prog_t *prog = SVVM_prog;
107 int i, bodysupercontents;
110 prvm_edict_t *traceowner, *touch;
112 // temporary storage because prvm_vec_t may differ from vec_t
113 vec3_t touchmins, touchmaxs;
114 // bounding box of entire move area
115 vec3_t clipboxmins, clipboxmaxs;
116 // size when clipping against monsters
117 vec3_t clipmins2, clipmaxs2;
118 // start and end origin of move
122 // matrices to transform into/out of other entity's space
123 matrix4x4_t matrix, imatrix;
124 // model of other entity
126 // list of entities to test for collisions
128 static prvm_edict_t *touchedicts[MAX_EDICTS];
130 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
132 VectorCopy(start, clipstart);
133 VectorClear(clipmins2);
134 VectorClear(clipmaxs2);
135 #if COLLISIONPARANOID >= 3
136 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
140 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
141 cliptrace.bmodelstartsolid = cliptrace.startsolid;
142 if (cliptrace.startsolid || cliptrace.fraction < 1)
143 cliptrace.ent = prog->edicts;
144 if (type == MOVE_WORLDONLY)
147 if (type == MOVE_MISSILE)
149 // LordHavoc: modified this, was = -15, now -= 15
150 for (i = 0;i < 3;i++)
157 // create the bounding box of the entire move
158 for (i = 0;i < 3;i++)
160 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
161 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
164 // debug override to test against everything
165 if (sv_debugmove.integer)
167 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
168 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
171 // if the passedict is world, make it NULL (to avoid two checks each time)
172 if (passedict == prog->edicts)
174 // precalculate prog value for passedict for comparisons
175 passedictprog = PRVM_EDICT_TO_PROG(passedict);
176 // precalculate passedict's owner edict pointer for comparisons
177 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
180 // because this uses World_EntitiestoBox, we know all entity boxes overlap
181 // the clip region, so we can skip culling checks in the loop below
182 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
183 if (numtouchedicts > MAX_EDICTS)
185 // this never happens
186 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
187 numtouchedicts = MAX_EDICTS;
189 for (i = 0;i < numtouchedicts;i++)
191 touch = touchedicts[i];
193 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
195 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
200 // don't clip against self
201 if (passedict == touch)
203 // don't clip owned entities against owner
204 if (traceowner == touch)
206 // don't clip owner against owned entities
207 if (passedictprog == PRVM_serveredictedict(touch, owner))
209 // don't clip points against points (they can't collide)
210 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
214 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
216 // might interact, so do an exact clip
218 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
220 model = SV_GetModelFromEdict(touch);
221 pitchsign = SV_GetPitchSign(prog, touch);
224 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);
226 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
227 Matrix4x4_Invert_Simple(&imatrix, &matrix);
228 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
229 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
230 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
231 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
232 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
233 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
234 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
236 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
238 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
250 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
251 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
253 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
256 prvm_prog_t *prog = SVVM_prog;
257 int i, bodysupercontents;
260 prvm_edict_t *traceowner, *touch;
262 // temporary storage because prvm_vec_t may differ from vec_t
263 vec3_t touchmins, touchmaxs;
264 // bounding box of entire move area
265 vec3_t clipboxmins, clipboxmaxs;
266 // size when clipping against monsters
267 vec3_t clipmins2, clipmaxs2;
268 // start and end origin of move
269 vec3_t clipstart, clipend;
272 // matrices to transform into/out of other entity's space
273 matrix4x4_t matrix, imatrix;
274 // model of other entity
276 // list of entities to test for collisions
278 static prvm_edict_t *touchedicts[MAX_EDICTS];
279 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
283 if (VectorCompare(start, pEnd))
284 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
286 if(collision_endposnudge.value > 0)
288 // TRICK: make the trace 1 qu longer!
289 VectorSubtract(pEnd, start, end);
290 len = VectorNormalizeLength(end);
291 VectorMA(pEnd, collision_endposnudge.value, end, end);
294 VectorCopy(pEnd, end);
296 if (VectorCompare(start, end))
297 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
300 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
302 VectorCopy(start, clipstart);
303 VectorCopy(end, clipend);
304 VectorClear(clipmins2);
305 VectorClear(clipmaxs2);
306 #if COLLISIONPARANOID >= 3
307 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
311 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, false);
312 cliptrace.bmodelstartsolid = cliptrace.startsolid;
313 if (cliptrace.startsolid || cliptrace.fraction < 1)
314 cliptrace.ent = prog->edicts;
315 if (type == MOVE_WORLDONLY)
318 if (type == MOVE_MISSILE)
320 // LordHavoc: modified this, was = -15, now -= 15
321 for (i = 0;i < 3;i++)
328 // create the bounding box of the entire move
329 for (i = 0;i < 3;i++)
331 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
332 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
335 // debug override to test against everything
336 if (sv_debugmove.integer)
338 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
339 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
342 // if the passedict is world, make it NULL (to avoid two checks each time)
343 if (passedict == prog->edicts)
345 // precalculate prog value for passedict for comparisons
346 passedictprog = PRVM_EDICT_TO_PROG(passedict);
347 // precalculate passedict's owner edict pointer for comparisons
348 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
351 // because this uses World_EntitiestoBox, we know all entity boxes overlap
352 // the clip region, so we can skip culling checks in the loop below
353 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
354 if (numtouchedicts > MAX_EDICTS)
356 // this never happens
357 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
358 numtouchedicts = MAX_EDICTS;
360 for (i = 0;i < numtouchedicts;i++)
362 touch = touchedicts[i];
364 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
366 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
371 // don't clip against self
372 if (passedict == touch)
374 // don't clip owned entities against owner
375 if (traceowner == touch)
377 // don't clip owner against owned entities
378 if (passedictprog == PRVM_serveredictedict(touch, owner))
380 // don't clip points against points (they can't collide)
381 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
385 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
387 // might interact, so do an exact clip
389 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
391 model = SV_GetModelFromEdict(touch);
392 pitchsign = SV_GetPitchSign(prog, touch);
395 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);
397 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
398 Matrix4x4_Invert_Simple(&imatrix, &matrix);
399 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
400 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
401 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
402 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
403 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
404 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
405 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
407 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, false);
409 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
413 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
414 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
415 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
425 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
426 #if COLLISIONPARANOID >= 1
427 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)
429 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)
432 #if COLLISIONPARANOID >= 1
433 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)
435 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)
439 prvm_prog_t *prog = SVVM_prog;
440 vec3_t hullmins, hullmaxs;
441 int i, bodysupercontents;
445 prvm_edict_t *traceowner, *touch;
447 // temporary storage because prvm_vec_t may differ from vec_t
448 vec3_t touchmins, touchmaxs;
449 // bounding box of entire move area
450 vec3_t clipboxmins, clipboxmaxs;
451 // size of the moving object
452 vec3_t clipmins, clipmaxs;
453 // size when clipping against monsters
454 vec3_t clipmins2, clipmaxs2;
455 // start and end origin of move
456 vec3_t clipstart, clipend;
459 // matrices to transform into/out of other entity's space
460 matrix4x4_t matrix, imatrix;
461 // model of other entity
463 // list of entities to test for collisions
465 static prvm_edict_t *touchedicts[MAX_EDICTS];
466 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
470 if (VectorCompare(mins, maxs))
472 vec3_t shiftstart, shiftend;
473 VectorAdd(start, mins, shiftstart);
474 VectorAdd(pEnd, mins, shiftend);
475 if (VectorCompare(start, pEnd))
476 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
478 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
479 VectorSubtract(trace.endpos, mins, trace.endpos);
483 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
485 // TRICK: make the trace 1 qu longer!
486 VectorSubtract(pEnd, start, end);
487 len = VectorNormalizeLength(end);
488 VectorMA(pEnd, collision_endposnudge.value, end, end);
491 VectorCopy(pEnd, end);
493 if (VectorCompare(mins, maxs))
495 vec3_t shiftstart, shiftend;
496 VectorAdd(start, mins, shiftstart);
497 VectorAdd(end, mins, shiftend);
498 if (VectorCompare(start, end))
499 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
501 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
502 VectorSubtract(trace.endpos, mins, trace.endpos);
507 VectorCopy(start, clipstart);
508 VectorCopy(end, clipend);
509 VectorCopy(mins, clipmins);
510 VectorCopy(maxs, clipmaxs);
511 VectorCopy(mins, clipmins2);
512 VectorCopy(maxs, clipmaxs2);
513 #if COLLISIONPARANOID >= 3
514 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
518 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
519 cliptrace.bmodelstartsolid = cliptrace.startsolid;
520 if (cliptrace.startsolid || cliptrace.fraction < 1)
521 cliptrace.ent = prog->edicts;
522 if (type == MOVE_WORLDONLY)
525 if (type == MOVE_MISSILE)
527 // LordHavoc: modified this, was = -15, now -= 15
528 for (i = 0;i < 3;i++)
535 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
536 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
537 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
540 VectorCopy(clipmins, hullmins);
541 VectorCopy(clipmaxs, hullmaxs);
544 // create the bounding box of the entire move
545 for (i = 0;i < 3;i++)
547 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
548 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
551 // debug override to test against everything
552 if (sv_debugmove.integer)
554 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
555 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
558 // if the passedict is world, make it NULL (to avoid two checks each time)
559 if (passedict == prog->edicts)
561 // precalculate prog value for passedict for comparisons
562 passedictprog = PRVM_EDICT_TO_PROG(passedict);
563 // figure out whether this is a point trace for comparisons
564 pointtrace = VectorCompare(clipmins, clipmaxs);
565 // precalculate passedict's owner edict pointer for comparisons
566 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
569 // because this uses World_EntitiestoBox, we know all entity boxes overlap
570 // the clip region, so we can skip culling checks in the loop below
571 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
572 if (numtouchedicts > MAX_EDICTS)
574 // this never happens
575 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
576 numtouchedicts = MAX_EDICTS;
578 for (i = 0;i < numtouchedicts;i++)
580 touch = touchedicts[i];
582 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
584 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
589 // don't clip against self
590 if (passedict == touch)
592 // don't clip owned entities against owner
593 if (traceowner == touch)
595 // don't clip owner against owned entities
596 if (passedictprog == PRVM_serveredictedict(touch, owner))
598 // don't clip points against points (they can't collide)
599 if (pointtrace && VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
603 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
605 // might interact, so do an exact clip
607 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
609 model = SV_GetModelFromEdict(touch);
610 pitchsign = SV_GetPitchSign(prog, touch);
613 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);
615 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
616 Matrix4x4_Invert_Simple(&imatrix, &matrix);
617 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
618 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
619 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
620 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
621 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
622 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
623 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
625 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
627 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
631 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
632 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
633 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
638 #if COLLISIONPARANOID >= 1
639 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)
644 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
647 VectorCopy(trace.endpos, temp);
648 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
649 #if COLLISIONPARANOID < 3
650 if (trace.startsolid || endstuck)
652 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" : "");
658 int SV_PointSuperContents(const vec3_t point)
660 prvm_prog_t *prog = SVVM_prog;
661 int supercontents = 0;
665 // matrices to transform into/out of other entity's space
666 matrix4x4_t matrix, imatrix;
667 // model of other entity
670 // list of entities to test for collisions
672 static prvm_edict_t *touchedicts[MAX_EDICTS];
674 // get world supercontents at this point
675 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
676 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
678 // if sv_gameplayfix_swiminbmodels is off we're done
679 if (!sv_gameplayfix_swiminbmodels.integer)
680 return supercontents;
682 // get list of entities at this point
683 numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
684 if (numtouchedicts > MAX_EDICTS)
686 // this never happens
687 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
688 numtouchedicts = MAX_EDICTS;
690 for (i = 0;i < numtouchedicts;i++)
692 touch = touchedicts[i];
694 // we only care about SOLID_BSP for pointcontents
695 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
698 // might interact, so do an exact clip
699 model = SV_GetModelFromEdict(touch);
700 if (!model || !model->PointSuperContents)
702 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);
703 Matrix4x4_Invert_Simple(&imatrix, &matrix);
704 Matrix4x4_Transform(&imatrix, point, transformed);
705 frame = (int)PRVM_serveredictfloat(touch, frame);
706 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
709 return supercontents;
713 ===============================================================================
715 Linking entities into the world culling system
717 ===============================================================================
720 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
722 prvm_prog_t *prog = SVVM_prog;
723 vec3_t paddedmins, paddedmaxs;
724 if (maxedicts < 1 || resultedicts == NULL)
726 // LordHavoc: discovered this actually causes its own bugs (dm6 teleporters being too close to info_teleport_destination)
727 //VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
728 //VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
729 VectorCopy(mins, paddedmins);
730 VectorCopy(maxs, paddedmaxs);
731 if (sv_areadebug.integer)
733 int numresultedicts = 0;
736 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
738 ed = PRVM_EDICT_NUM(edictindex);
739 if (!ed->priv.required->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
741 resultedicts[numresultedicts++] = ed;
742 if (numresultedicts == maxedicts)
746 return numresultedicts;
749 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
752 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
754 prvm_prog_t *prog = SVVM_prog;
755 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
756 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
757 PRVM_serverglobalfloat(time) = sv.time;
758 PRVM_serverglobalfloat(trace_allsolid) = false;
759 PRVM_serverglobalfloat(trace_startsolid) = false;
760 PRVM_serverglobalfloat(trace_fraction) = 1;
761 PRVM_serverglobalfloat(trace_inwater) = false;
762 PRVM_serverglobalfloat(trace_inopen) = true;
763 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
764 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
765 PRVM_serverglobalfloat(trace_plane_dist) = 0;
766 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
767 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
768 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
769 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
770 PRVM_serverglobalstring(trace_dphittexturename) = 0;
771 prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
774 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
776 prvm_prog_t *prog = SVVM_prog;
777 int i, numtouchedicts, old_self, old_other;
779 static prvm_edict_t *touchedicts[MAX_EDICTS];
781 if (ent == prog->edicts)
782 return; // don't add the world
784 if (ent->priv.server->free)
787 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
790 // build a list of edicts to touch, because the link loop can be corrupted
791 // by IncreaseEdicts called during touch functions
792 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
793 if (numtouchedicts > MAX_EDICTS)
795 // this never happens
796 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
797 numtouchedicts = MAX_EDICTS;
800 old_self = PRVM_serverglobaledict(self);
801 old_other = PRVM_serverglobaledict(other);
802 for (i = 0;i < numtouchedicts;i++)
804 touch = touchedicts[i];
805 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
807 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
810 PRVM_serverglobaledict(self) = old_self;
811 PRVM_serverglobaledict(other) = old_other;
814 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
818 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
820 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
821 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
822 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
823 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
824 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
825 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
826 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
827 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
828 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
829 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];
830 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];
831 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
832 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];
833 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];
834 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
835 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];
836 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];
837 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
838 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];
839 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];
840 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
841 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];
842 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];
851 void SV_LinkEdict (prvm_edict_t *ent)
853 prvm_prog_t *prog = SVVM_prog;
855 vec3_t mins, maxs, entmins, entmaxs, entangles;
858 if (ent == prog->edicts)
859 return; // don't add the world
861 if (ent->priv.server->free)
864 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
865 if (modelindex < 0 || modelindex >= MAX_MODELS)
867 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
870 model = SV_GetModelByIndex(modelindex);
872 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
873 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
874 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
878 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
880 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
881 // TODO special handling for spheres?
882 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
883 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
884 VectorCopy(PRVM_serveredictvector(ent, angles), entangles);
885 RotateBBox(entmins, entmaxs, entangles, mins, maxs);
886 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
887 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
889 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
893 if (!model->TraceBox)
894 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
896 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
898 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
899 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
901 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
903 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
904 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
908 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
909 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
914 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
915 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
916 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
921 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
922 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
926 // to make items easier to pick up and allow them to be grabbed off
927 // of shelves, the abs sizes are expanded
929 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
940 // because movement is clipped an epsilon away from an actual edge,
941 // we must fully check even when bounding boxes don't quite touch
950 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
951 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
953 World_LinkEdict(&sv.world, ent, mins, maxs);
957 ===============================================================================
961 ===============================================================================
966 SV_TestEntityPosition
968 returns true if the entity is in solid currently
971 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
973 prvm_prog_t *prog = SVVM_prog;
975 vec3_t org, entorigin, entmins, entmaxs;
977 contents = SV_GenericHitSuperContentsMask(ent);
978 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
979 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
980 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
981 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
982 trace = SV_TraceBox(org, entmins, entmaxs, entorigin, ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, contents);
983 if (trace.startsupercontents & contents)
987 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
989 // q1bsp/hlbsp use hulls and if the entity does not exactly match
990 // a hull size it is incorrectly tested, so this code tries to
991 // 'fix' it slightly...
992 // FIXME: this breaks entities larger than the hull size
995 VectorAdd(org, entmins, m1);
996 VectorAdd(org, entmaxs, m2);
997 VectorSubtract(m2, m1, s);
998 #define EPSILON (1.0f / 32.0f)
999 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
1000 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
1001 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
1002 for (i = 0;i < 8;i++)
1004 v[0] = (i & 1) ? m2[0] : m1[0];
1005 v[1] = (i & 2) ? m2[1] : m1[1];
1006 v[2] = (i & 4) ? m2[2] : m1[2];
1007 if (SV_PointSuperContents(v) & contents)
1012 // if the trace found a better position for the entity, move it there
1013 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
1016 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
1017 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
1019 // verify if the endpos is REALLY outside solid
1020 VectorCopy(trace.endpos, org);
1021 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, contents);
1022 if(trace.startsolid)
1023 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
1025 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1031 // DRESK - Support for Entity Contents Transition Event
1034 SV_CheckContentsTransition
1036 returns true if entity had a valid contentstransition function call
1039 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
1041 prvm_prog_t *prog = SVVM_prog;
1042 int bValidFunctionCall;
1044 // Default Valid Function Call to False
1045 bValidFunctionCall = false;
1047 if(PRVM_serveredictfloat(ent, watertype) != nContents)
1048 { // Changed Contents
1049 // Acquire Contents Transition Function from QC
1050 if(PRVM_serveredictfunction(ent, contentstransition))
1051 { // Valid Function; Execute
1052 // Assign Valid Function
1053 bValidFunctionCall = true;
1054 // Prepare Parameters (Original Contents, New Contents)
1055 // Original Contents
1056 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
1058 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1060 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1062 PRVM_serverglobalfloat(time) = sv.time;
1063 // Execute VM Function
1064 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1068 // Return if Function Call was Valid
1069 return bValidFunctionCall;
1078 void SV_CheckVelocity (prvm_edict_t *ent)
1080 prvm_prog_t *prog = SVVM_prog;
1087 for (i=0 ; i<3 ; i++)
1089 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1091 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1092 PRVM_serveredictvector(ent, velocity)[i] = 0;
1094 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1096 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1097 PRVM_serveredictvector(ent, origin)[i] = 0;
1101 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1102 // player_run/player_stand1 does not horribly malfunction if the
1103 // velocity becomes a denormalized float
1104 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0001)
1105 VectorClear(PRVM_serveredictvector(ent, velocity));
1107 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1108 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1109 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1111 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1112 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1113 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1114 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1122 Runs thinking code if time. There is some play in the exact time the think
1123 function will be called, because it is called before any movement is done
1124 in a frame. Not used for pushmove objects, because they must be exact.
1125 Returns false if the entity removed itself.
1128 static qboolean SV_RunThink (prvm_edict_t *ent)
1130 prvm_prog_t *prog = SVVM_prog;
1133 // don't let things stay in the past.
1134 // it is possible to start that way by a trigger with a local time.
1135 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1138 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1140 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1141 PRVM_serveredictfloat(ent, nextthink) = 0;
1142 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1143 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1144 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1145 // mods often set nextthink to time to cause a think every frame,
1146 // we don't want to loop in that case, so exit if the new nextthink is
1147 // <= the time the qc was told, also exit if it is past the end of the
1149 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1152 return !ent->priv.server->free;
1159 Two entities have touched, so run their touch functions
1162 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1164 prvm_prog_t *prog = SVVM_prog;
1165 int restorevm_tempstringsbuf_cursize;
1166 int old_self, old_other;
1167 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1169 old_self = PRVM_serverglobaledict(self);
1170 old_other = PRVM_serverglobaledict(other);
1171 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1173 VM_SetTraceGlobals(prog, trace);
1175 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1177 PRVM_serverglobalfloat(time) = sv.time;
1178 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1179 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1180 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1183 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1185 PRVM_serverglobalfloat(time) = sv.time;
1186 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1187 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1188 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1189 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1190 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1191 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1192 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1193 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1194 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1195 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1196 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1199 PRVM_serverglobaledict(self) = old_self;
1200 PRVM_serverglobaledict(other) = old_other;
1201 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1209 Slide off of the impacting object
1210 returns the blocked flags (1 = floor, 2 = step / wall)
1213 #define STOP_EPSILON 0.1
1214 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1219 backoff = -DotProduct (in, normal) * overbounce;
1220 VectorMA(in, backoff, normal, out);
1222 for (i = 0;i < 3;i++)
1223 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1232 The basic solid body movement clip that slides along multiple planes
1233 Returns the clipflags if the velocity was modified (hit something solid)
1237 8 = teleported by touch method
1238 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1241 static float SV_Gravity (prvm_edict_t *ent);
1242 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1243 #define MAX_CLIP_PLANES 5
1244 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1246 prvm_prog_t *prog = SVVM_prog;
1247 int blocked, bumpcount;
1248 int i, j, numplanes;
1249 float d, time_left, gravity;
1250 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1251 prvm_vec3_t primal_velocity, original_velocity, new_velocity;
1262 gravity = SV_Gravity(ent);
1264 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1266 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1267 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1269 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1274 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1275 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1278 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1280 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1283 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1284 if(!SV_PushEntity(&trace, ent, push, false, false))
1286 // we got teleported by a touch function
1287 // let's abort the move
1292 if (trace.fraction == 1)
1294 if (trace.plane.normal[2])
1296 if (trace.plane.normal[2] > 0.7)
1303 Con_Printf ("SV_FlyMove: !trace.ent");
1304 trace.ent = prog->edicts;
1307 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1308 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1311 else if (stepheight)
1313 // step - handle it immediately
1319 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1320 VectorSet(steppush, 0, 0, stepheight);
1321 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1322 if(!SV_PushEntity(&steptrace, ent, steppush, false, false))
1327 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1328 if(!SV_PushEntity(&steptrace2, ent, push, false, false))
1333 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1334 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1335 if(!SV_PushEntity(&steptrace3, ent, steppush, false, false))
1340 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1341 // accept the new position if it made some progress...
1342 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1344 //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]);
1346 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1347 time_left *= 1 - trace.fraction;
1353 //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]);
1354 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1359 // step - return it to caller
1361 // save the trace for player extrafriction
1363 VectorCopy(trace.plane.normal, stepnormal);
1365 if (trace.fraction >= 0.001)
1367 // actually covered some distance
1368 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1372 time_left *= 1 - trace.fraction;
1374 // clipped to another plane
1375 if (numplanes >= MAX_CLIP_PLANES)
1377 // this shouldn't really happen
1378 VectorClear(PRVM_serveredictvector(ent, velocity));
1384 for (i = 0;i < numplanes;i++)
1385 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1389 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1394 VectorCopy(trace.plane.normal, planes[numplanes]);
1397 // modify original_velocity so it parallels all of the clip planes
1398 for (i = 0;i < numplanes;i++)
1400 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1401 for (j = 0;j < numplanes;j++)
1406 if (DotProduct(new_velocity, planes[j]) < 0)
1416 // go along this plane
1417 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1421 // go along the crease
1424 VectorClear(PRVM_serveredictvector(ent, velocity));
1428 CrossProduct(planes[0], planes[1], dir);
1429 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1430 VectorNormalize(dir);
1431 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1432 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1435 // if current velocity is against the original velocity,
1436 // stop dead to avoid tiny occilations in sloping corners
1437 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1439 VectorClear(PRVM_serveredictvector(ent, velocity));
1444 //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]);
1447 if ((blocked & 1) == 0 && bumpcount > 1)
1449 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1450 // flag ONGROUND if there's ground under it
1451 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask);
1455 // LordHavoc: this came from QW and allows you to get out of water more easily
1456 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1457 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1461 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1463 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1464 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1477 static float SV_Gravity (prvm_edict_t *ent)
1479 prvm_prog_t *prog = SVVM_prog;
1482 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1485 return ent_gravity * sv_gravity.value * sv.frametime;
1490 ===============================================================================
1494 ===============================================================================
1497 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1499 prvm_prog_t *prog = SVVM_prog;
1503 vec3_t stuckmins, stuckmaxs;
1504 vec3_t goodmins, goodmaxs;
1508 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1509 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1510 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1511 VectorCopy(pivot, goodmins);
1512 VectorCopy(pivot, goodmaxs);
1513 for (bump = 0;bump < 6;bump++)
1515 int coord = 2-(bump >> 1);
1516 //int coord = (bump >> 1);
1517 int dir = (bump & 1);
1520 for(subbump = 0; ; ++subbump)
1522 VectorCopy(stuckorigin, testorigin);
1526 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1531 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1534 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1535 if (stucktrace.bmodelstartsolid)
1537 // BAD BAD, can't fix that
1541 if (stucktrace.fraction >= 1)
1546 // BAD BAD, can't fix that
1550 // we hit something... let's move out of it
1551 VectorSubtract(stucktrace.endpos, testorigin, move);
1552 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1553 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1557 Con_Printf("subbump: %d\n", subbump);
1563 goodmaxs[coord] = stuckmaxs[coord];
1568 goodmins[coord] = stuckmins[coord];
1573 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1578 static qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1580 prvm_prog_t *prog = SVVM_prog;
1584 vec3_t stuckmins, stuckmaxs;
1586 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1587 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1588 separation = 0.0f; // when using hulls, it can not be enlarged
1589 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1590 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1591 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1592 stuckmins[0] -= separation;
1593 stuckmins[1] -= separation;
1594 stuckmins[2] -= separation;
1595 stuckmaxs[0] += separation;
1596 stuckmaxs[1] += separation;
1597 stuckmaxs[2] += separation;
1598 for (bump = 0;bump < 10;bump++)
1600 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1601 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1603 // found a good location, use it
1604 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1607 nudge = -stucktrace.startdepth;
1608 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1617 Does not change the entities velocity at all
1618 The trace struct is filled with the trace that has been done.
1619 Returns true if the push did not result in the entity being teleported by QC code.
1622 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1624 prvm_prog_t *prog = SVVM_prog;
1632 solid = (int)PRVM_serveredictfloat(ent, solid);
1633 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1634 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1635 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1637 // move start position out of solids
1638 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1640 SV_NudgeOutOfSolid(ent);
1643 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1644 VectorAdd(start, push, end);
1646 if (movetype == MOVETYPE_FLYMISSILE)
1647 type = MOVE_MISSILE;
1648 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1649 type = MOVE_WORLDONLY;
1650 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1651 type = MOVE_NOMONSTERS; // only clip against bmodels
1655 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1656 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1659 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1661 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1666 if(!trace->startsolid)
1667 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)
1669 Con_Printf("something eeeeevil happened\n");
1674 SV_LinkEdict_TouchAreaGrid(ent);
1676 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))))
1677 SV_Impact (ent, trace);
1679 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1681 ent->priv.required->mark = 0;
1684 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1686 ent->priv.required->mark = 0;
1691 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1703 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1705 prvm_prog_t *prog = SVVM_prog;
1707 int pusherowner, pusherprog;
1710 float savesolid, movetime2, pushltime;
1711 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1713 int numcheckentities;
1714 static prvm_edict_t *checkentities[MAX_EDICTS];
1715 dp_model_t *pushermodel;
1716 trace_t trace, trace2;
1717 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1718 static unsigned short moved_edicts[MAX_EDICTS];
1721 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])
1723 PRVM_serveredictfloat(pusher, ltime) += movetime;
1727 switch ((int) PRVM_serveredictfloat(pusher, solid))
1729 // LordHavoc: valid pusher types
1732 case SOLID_SLIDEBOX:
1733 case SOLID_CORPSE: // LordHavoc: this would be weird...
1735 // LordHavoc: no collisions
1738 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1739 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1740 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1741 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1742 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1743 PRVM_serveredictfloat(pusher, ltime) += movetime;
1744 SV_LinkEdict(pusher);
1747 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1750 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1751 if (index < 1 || index >= MAX_MODELS)
1753 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1756 pushermodel = SV_GetModelByIndex(index);
1757 pusherowner = PRVM_serveredictedict(pusher, owner);
1758 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1760 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1762 movetime2 = movetime;
1763 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1764 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1765 if (moveangle[0] || moveangle[2])
1767 for (i = 0;i < 3;i++)
1771 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1772 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1776 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1777 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1781 else if (moveangle[1])
1783 for (i = 0;i < 3;i++)
1787 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1788 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1792 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1793 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1799 for (i = 0;i < 3;i++)
1803 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1804 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1808 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1809 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1814 VectorNegate (moveangle, a);
1815 AngleVectorsFLU (a, forward, left, up);
1817 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1818 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1819 pushltime = PRVM_serveredictfloat(pusher, ltime);
1821 // move the pusher to its final position
1823 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1824 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1825 PRVM_serveredictfloat(pusher, ltime) += movetime;
1826 SV_LinkEdict(pusher);
1828 pushermodel = SV_GetModelFromEdict(pusher);
1829 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);
1830 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1832 savesolid = PRVM_serveredictfloat(pusher, solid);
1834 // see if any solid entities are inside the final position
1837 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1838 numcheckentities = 0;
1839 else // MOVETYPE_PUSH
1840 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1841 for (e = 0;e < numcheckentities;e++)
1843 prvm_edict_t *check = checkentities[e];
1844 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1849 case MOVETYPE_FOLLOW:
1850 case MOVETYPE_NOCLIP:
1851 case MOVETYPE_FLY_WORLDONLY:
1857 if (PRVM_serveredictedict(check, owner) == pusherprog)
1860 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1863 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1865 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1866 check->priv.server->waterposition_forceupdate = true;
1868 checkcontents = SV_GenericHitSuperContentsMask(check);
1870 // if the entity is standing on the pusher, it will definitely be moved
1871 // if the entity is not standing on the pusher, but is in the pusher's
1872 // final position, move it
1873 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1875 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1876 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1877 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1878 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1879 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1880 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents);
1881 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1882 if (!trace.startsolid)
1884 //Con_Printf("- not in solid\n");
1889 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1890 //VectorClear(pivot);
1895 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1896 VectorAdd (org, pivot, org);
1897 org2[0] = DotProduct (org, forward);
1898 org2[1] = DotProduct (org, left);
1899 org2[2] = DotProduct (org, up);
1900 VectorSubtract (org2, org, move);
1901 VectorAdd (move, move1, move);
1904 VectorCopy (move1, move);
1906 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1908 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1909 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1910 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1912 // physics objects need better collisions than this code can do
1913 if (movetype == MOVETYPE_PHYSICS)
1915 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1916 SV_LinkEdict(check);
1917 SV_LinkEdict_TouchAreaGrid(check);
1921 // try moving the contacted entity
1922 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1923 if(!SV_PushEntity (&trace, check, move, true, true))
1925 // entity "check" got teleported
1926 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1927 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1928 continue; // pushed enough
1930 // FIXME: turn players specially
1931 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1932 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1933 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1935 // this trace.fraction < 1 check causes items to fall off of pushers
1936 // if they pass under or through a wall
1937 // the groundentity check causes items to fall off of ledges
1938 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1939 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1941 // if it is still inside the pusher, block
1942 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1943 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1944 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1945 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1946 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1947 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents);
1948 if (trace.startsolid)
1951 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1953 // hack to invoke all necessary movement triggers
1955 if(!SV_PushEntity(&trace2, check, move2, true, true))
1957 // entity "check" got teleported
1964 // still inside pusher, so it's really blocked
1967 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1969 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1972 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1973 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1977 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1978 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1979 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1980 SV_LinkEdict(pusher);
1982 // move back any entities we already moved
1983 for (i = 0;i < num_moved;i++)
1985 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1986 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1987 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1991 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1992 if (PRVM_serveredictfunction(pusher, blocked))
1994 PRVM_serverglobalfloat(time) = sv.time;
1995 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1996 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1997 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
2002 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
2003 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
2004 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
2013 static void SV_Physics_Pusher (prvm_edict_t *ent)
2015 prvm_prog_t *prog = SVVM_prog;
2016 float thinktime, oldltime, movetime;
2018 oldltime = PRVM_serveredictfloat(ent, ltime);
2020 thinktime = PRVM_serveredictfloat(ent, nextthink);
2021 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
2023 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
2028 movetime = sv.frametime;
2031 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
2032 SV_PushMove (ent, movetime);
2034 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
2036 PRVM_serveredictfloat(ent, nextthink) = 0;
2037 PRVM_serverglobalfloat(time) = sv.time;
2038 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2039 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
2040 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2046 ===============================================================================
2050 ===============================================================================
2053 static float unstickoffsets[] =
2055 // poutting -/+z changes first as they are least weird
2070 typedef enum unstickresult_e
2078 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2080 prvm_prog_t *prog = SVVM_prog;
2083 // if not stuck in a bmodel, just return
2084 if (!SV_TestEntityPosition(ent, vec3_origin))
2085 return UNSTICK_GOOD;
2087 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2089 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2091 VectorCopy(unstickoffsets + i, offset);
2093 //SV_LinkEdict_TouchAreaGrid(ent);
2094 return UNSTICK_UNSTUCK;
2098 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2099 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2101 for(i = 2; i <= maxunstick; ++i)
2103 VectorClear(offset);
2105 if (!SV_TestEntityPosition(ent, offset))
2108 //SV_LinkEdict_TouchAreaGrid(ent);
2109 return UNSTICK_UNSTUCK;
2112 if (!SV_TestEntityPosition(ent, offset))
2115 //SV_LinkEdict_TouchAreaGrid(ent);
2116 return UNSTICK_UNSTUCK;
2120 return UNSTICK_STUCK;
2123 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2125 prvm_prog_t *prog = SVVM_prog;
2127 switch(SV_UnstickEntityReturnOffset(ent, offset))
2131 case UNSTICK_UNSTUCK:
2132 Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2135 if (developer_extra.integer)
2136 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2139 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2148 This is a big hack to try and fix the rare case of getting stuck in the world
2152 static void SV_CheckStuck (prvm_edict_t *ent)
2154 prvm_prog_t *prog = SVVM_prog;
2157 switch(SV_UnstickEntityReturnOffset(ent, offset))
2160 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2162 case UNSTICK_UNSTUCK:
2163 Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2166 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2167 if (!SV_TestEntityPosition(ent, offset))
2169 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2171 //SV_LinkEdict_TouchAreaGrid(ent);
2174 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2177 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2187 static qboolean SV_CheckWater (prvm_edict_t *ent)
2189 prvm_prog_t *prog = SVVM_prog;
2191 int nNativeContents;
2194 point[0] = PRVM_serveredictvector(ent, origin)[0];
2195 point[1] = PRVM_serveredictvector(ent, origin)[1];
2196 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2198 // DRESK - Support for Entity Contents Transition Event
2199 // NOTE: Some logic needed to be slightly re-ordered
2200 // to not affect performance and allow for the feature.
2202 // Acquire Super Contents Prior to Resets
2203 cont = SV_PointSuperContents(point);
2204 // Acquire Native Contents Here
2205 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2207 // DRESK - Support for Entity Contents Transition Event
2208 if(PRVM_serveredictfloat(ent, watertype))
2209 // Entity did NOT Spawn; Check
2210 SV_CheckContentsTransition(ent, nNativeContents);
2213 PRVM_serveredictfloat(ent, waterlevel) = 0;
2214 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2215 cont = SV_PointSuperContents(point);
2216 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2218 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2219 PRVM_serveredictfloat(ent, waterlevel) = 1;
2220 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2221 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2223 PRVM_serveredictfloat(ent, waterlevel) = 2;
2224 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2225 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2226 PRVM_serveredictfloat(ent, waterlevel) = 3;
2230 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2239 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2241 prvm_prog_t *prog = SVVM_prog;
2243 vec3_t forward, into, side, v_angle;
2245 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2246 AngleVectors (v_angle, forward, NULL, NULL);
2247 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2249 // cut the tangential velocity
2250 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2251 VectorScale (stepnormal, i, into);
2252 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2253 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2254 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2260 =====================
2263 Player has come to a dead stop, possibly due to the problem with limited
2264 float precision at some angle joins in the BSP hull.
2266 Try fixing by pushing one pixel in each direction.
2268 This is a hack, but in the interest of good gameplay...
2269 ======================
2271 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2276 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2279 for (i=0 ; i<8 ; i++)
2281 // try pushing a little in an axial direction
2284 case 0: dir[0] = 2; dir[1] = 0; break;
2285 case 1: dir[0] = 0; dir[1] = 2; break;
2286 case 2: dir[0] = -2; dir[1] = 0; break;
2287 case 3: dir[0] = 0; dir[1] = -2; break;
2288 case 4: dir[0] = 2; dir[1] = 2; break;
2289 case 5: dir[0] = -2; dir[1] = 2; break;
2290 case 6: dir[0] = 2; dir[1] = -2; break;
2291 case 7: dir[0] = -2; dir[1] = -2; break;
2294 SV_PushEntity (&trace, ent, dir, false, true);
2296 // retry the original move
2297 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2298 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2299 PRVM_serveredictvector(ent, velocity)[2] = 0;
2300 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2302 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2303 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2305 Con_DPrint("TryUnstick - success.\n");
2309 // go back to the original pos and try again
2310 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2314 VectorClear (PRVM_serveredictvector(ent, velocity));
2315 Con_DPrint("TryUnstick - failure.\n");
2321 =====================
2324 Only used by players
2325 ======================
2327 static void SV_WalkMove (prvm_edict_t *ent)
2329 prvm_prog_t *prog = SVVM_prog;
2332 //int originalmove_clip;
2333 int originalmove_flags;
2334 int originalmove_groundentity;
2335 int hitsupercontentsmask;
2337 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2338 trace_t downtrace, trace;
2339 qboolean applygravity;
2341 // if frametime is 0 (due to client sending the same timestamp twice),
2343 if (sv.frametime <= 0)
2346 if (sv_gameplayfix_unstickplayers.integer)
2347 SV_CheckStuck (ent);
2349 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2351 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2353 SV_CheckVelocity(ent);
2355 // do a regular slide move unless it looks like you ran into a step
2356 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2358 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2359 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2361 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2363 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2366 // only try this if there was no floor in the way in the trace (no,
2367 // this check seems to be not REALLY necessary, because if clip & 1,
2368 // our trace will hit that thing too)
2369 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2370 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2371 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2372 type = MOVE_MISSILE;
2373 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2374 type = MOVE_WORLDONLY;
2375 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2376 type = MOVE_NOMONSTERS; // only clip against bmodels
2379 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2380 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2381 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2382 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2383 clip |= 1; // but we HAVE found a floor
2386 // if the move did not hit the ground at any point, we're not on ground
2388 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2390 SV_CheckVelocity(ent);
2392 SV_LinkEdict_TouchAreaGrid(ent);
2394 if(clip & 8) // teleport
2397 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2400 if (sv_nostep.integer)
2403 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2404 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2405 //originalmove_clip = clip;
2406 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2407 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2409 // if move didn't block on a step, return
2412 // if move was not trying to move into the step, return
2413 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2416 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2418 // return if gibbed by a trigger
2419 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2422 // only step up while jumping if that is enabled
2423 if (sv_jumpstep.integer)
2424 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2428 // try moving up and forward to go up a step
2429 // back to start pos
2430 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2431 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2434 VectorClear (upmove);
2435 upmove[2] = sv_stepheight.value;
2436 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2438 // we got teleported when upstepping... must abort the move
2443 PRVM_serveredictvector(ent, velocity)[2] = 0;
2444 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2445 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2448 // we got teleported when upstepping... must abort the move
2449 // note that z velocity handling may not be what QC expects here, but we cannot help it
2453 SV_CheckVelocity(ent);
2455 SV_LinkEdict_TouchAreaGrid(ent);
2457 // check for stuckness, possibly due to the limited precision of floats
2458 // in the clipping hulls
2460 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2461 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2463 //Con_Printf("wall\n");
2464 // stepping up didn't make any progress, revert to original move
2465 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2466 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2467 //clip = originalmove_clip;
2468 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2469 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2470 // now try to unstick if needed
2471 //clip = SV_TryUnstick (ent, oldvel);
2475 //Con_Printf("step - ");
2477 // extra friction based on view angle
2478 if (clip & 2 && sv_wallfriction.integer)
2479 SV_WallFriction (ent, stepnormal);
2481 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2482 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))
2486 VectorClear (downmove);
2487 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2488 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2490 // we got teleported when downstepping... must abort the move
2494 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2496 // this has been disabled so that you can't jump when you are stepping
2497 // up while already jumping (also known as the Quake2 double jump bug)
2499 // LordHavoc: disabled this check so you can walk on monsters/players
2500 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2502 //Con_Printf("onground\n");
2503 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2504 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2510 //Con_Printf("slope\n");
2511 // if the push down didn't end up on good ground, use the move without
2512 // the step up. This happens near wall / slope combinations, and can
2513 // cause the player to hop up higher on a slope too steep to climb
2514 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2515 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2516 //clip = originalmove_clip;
2517 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2518 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2521 SV_CheckVelocity(ent);
2523 SV_LinkEdict_TouchAreaGrid(ent);
2526 //============================================================================
2532 Entities that are "stuck" to another entity
2535 static void SV_Physics_Follow (prvm_edict_t *ent)
2537 prvm_prog_t *prog = SVVM_prog;
2538 vec3_t vf, vr, vu, angles, v;
2542 if (!SV_RunThink (ent))
2545 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2546 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2547 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])
2549 // quick case for no rotation
2550 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2554 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2555 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2556 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2557 AngleVectors (angles, vf, vr, vu);
2558 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];
2559 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];
2560 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];
2561 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2562 angles[1] = PRVM_serveredictvector(e, angles)[1];
2563 angles[2] = PRVM_serveredictvector(e, angles)[2];
2564 AngleVectors (angles, vf, vr, vu);
2565 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2566 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2567 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2569 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2571 //SV_LinkEdict_TouchAreaGrid(ent);
2575 ==============================================================================
2579 ==============================================================================
2584 SV_CheckWaterTransition
2588 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2591 prvm_prog_t *prog = SVVM_prog;
2592 // 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
2594 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2595 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(entorigin));
2596 if (!PRVM_serveredictfloat(ent, watertype))
2598 // just spawned here
2599 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2601 PRVM_serveredictfloat(ent, watertype) = cont;
2602 PRVM_serveredictfloat(ent, waterlevel) = 1;
2606 // DRESK - Support for Entity Contents Transition Event
2607 // NOTE: Call here BEFORE updating the watertype below,
2608 // and suppress watersplash sound if a valid function
2609 // call was made to allow for custom "splash" sounds.
2610 else if( !SV_CheckContentsTransition(ent, cont) )
2611 { // Contents Transition Function Invalid; Potentially Play Water Sound
2612 // check if the entity crossed into or out of water
2613 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2614 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2617 if (cont <= CONTENTS_WATER)
2619 PRVM_serveredictfloat(ent, watertype) = cont;
2620 PRVM_serveredictfloat(ent, waterlevel) = 1;
2624 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2625 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2633 Toss, bounce, and fly movement. When onground, do nothing.
2637 void SV_Physics_Toss (prvm_edict_t *ent)
2639 prvm_prog_t *prog = SVVM_prog;
2644 prvm_edict_t *groundentity;
2645 float d, ent_gravity;
2649 // if onground, return without moving
2650 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2652 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2653 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2655 // don't stick to ground if onground and moving upward
2656 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2658 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2660 // we can trust FL_ONGROUND if groundentity is world because it never moves
2663 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2665 // if ent was supported by a brush model on previous frame,
2666 // and groundentity is now freed, set groundentity to 0 (world)
2667 // which leaves it suspended in the air
2668 PRVM_serveredictedict(ent, groundentity) = 0;
2669 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2672 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2674 // don't slide if still touching the groundentity
2678 ent->priv.server->suspendedinairflag = false;
2680 SV_CheckVelocity (ent);
2683 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2684 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2687 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2689 movetime = sv.frametime;
2690 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2693 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2694 if(!SV_PushEntity(&trace, ent, move, true, true))
2695 return; // teleported
2696 if (ent->priv.server->free)
2698 if (trace.bmodelstartsolid)
2700 // try to unstick the entity
2701 if (sv_gameplayfix_unstickentities.integer)
2702 SV_UnstickEntity(ent);
2703 if(!SV_PushEntity(&trace, ent, move, false, true))
2704 return; // teleported
2705 if (ent->priv.server->free)
2708 if (trace.fraction == 1)
2710 movetime *= 1 - min(1, trace.fraction);
2711 switch((int)PRVM_serveredictfloat(ent, movetype))
2713 case MOVETYPE_BOUNCEMISSILE:
2714 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2716 bouncefactor = 1.0f;
2718 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2719 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2720 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2723 case MOVETYPE_BOUNCE:
2724 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2726 bouncefactor = 0.5f;
2728 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2730 bouncestop = 60.0f / 800.0f;
2732 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2733 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2736 // LordHavoc: fixed grenades not bouncing when fired down a slope
2737 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2738 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2740 d = PRVM_serveredictvector(ent, velocity)[2];
2741 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2743 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2744 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2745 VectorClear(PRVM_serveredictvector(ent, velocity));
2746 VectorClear(PRVM_serveredictvector(ent, avelocity));
2751 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2752 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2757 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2758 if (trace.plane.normal[2] > 0.7)
2760 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2761 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2762 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2763 ent->priv.server->suspendedinairflag = true;
2764 VectorClear (PRVM_serveredictvector(ent, velocity));
2765 VectorClear (PRVM_serveredictvector(ent, avelocity));
2768 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2774 // check for in water
2775 SV_CheckWaterTransition (ent);
2779 ===============================================================================
2783 ===============================================================================
2790 Monsters freefall when they don't have a ground entity, otherwise
2791 all movement is done with discrete steps.
2793 This is also used for objects that have become still on the ground, but
2794 will fall if the floor is pulled out from under them.
2797 static void SV_Physics_Step (prvm_edict_t *ent)
2799 prvm_prog_t *prog = SVVM_prog;
2800 int flags = (int)PRVM_serveredictfloat(ent, flags);
2803 // Backup Velocity in the event that movetypesteplandevent is called,
2804 // to provide a parameter with the entity's velocity at impact.
2805 vec3_t backupVelocity;
2806 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2807 // don't fall at all if fly/swim
2808 if (!(flags & (FL_FLY | FL_SWIM)))
2810 if (flags & FL_ONGROUND)
2812 // freefall if onground and moving upward
2813 // freefall if not standing on a world surface (it may be a lift or trap door)
2814 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2816 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2817 SV_CheckVelocity(ent);
2818 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2820 SV_LinkEdict_TouchAreaGrid(ent);
2821 ent->priv.server->waterposition_forceupdate = true;
2826 // freefall if not onground
2827 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2829 SV_CheckVelocity(ent);
2830 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2832 SV_LinkEdict_TouchAreaGrid(ent);
2835 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2837 // DRESK - Check for Entity Land Event Function
2838 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2839 { // Valid Function; Execute
2840 // Prepare Parameters
2841 // Assign Velocity at Impact
2842 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2843 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2844 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2846 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2848 PRVM_serverglobalfloat(time) = sv.time;
2849 // Execute VM Function
2850 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2853 // Check for Engine Landing Sound
2854 if(sv_sound_land.string)
2855 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2857 ent->priv.server->waterposition_forceupdate = true;
2862 if (!SV_RunThink(ent))
2865 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2867 ent->priv.server->waterposition_forceupdate = false;
2868 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2869 SV_CheckWaterTransition(ent);
2873 //============================================================================
2875 static void SV_Physics_Entity (prvm_edict_t *ent)
2877 prvm_prog_t *prog = SVVM_prog;
2878 // don't run think/move on newly spawned projectiles as it messes up
2879 // movement interpolation and rocket trails, and is inconsistent with
2880 // respect to entities spawned in the same frame
2881 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2882 // but if it spawns a lower numbered ent, it doesn't - this never moves
2883 // ents in the first frame regardless)
2884 qboolean runmove = ent->priv.server->move;
2885 ent->priv.server->move = true;
2886 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2888 switch ((int) PRVM_serveredictfloat(ent, movetype))
2891 case MOVETYPE_FAKEPUSH:
2892 SV_Physics_Pusher (ent);
2895 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2896 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2899 case MOVETYPE_FOLLOW:
2900 SV_Physics_Follow (ent);
2902 case MOVETYPE_NOCLIP:
2903 if (SV_RunThink(ent))
2906 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2907 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2912 SV_Physics_Step (ent);
2915 if (SV_RunThink (ent))
2919 case MOVETYPE_BOUNCE:
2920 case MOVETYPE_BOUNCEMISSILE:
2921 case MOVETYPE_FLYMISSILE:
2923 case MOVETYPE_FLY_WORLDONLY:
2925 if (SV_RunThink (ent))
2926 SV_Physics_Toss (ent);
2928 case MOVETYPE_PHYSICS:
2929 if (SV_RunThink(ent))
2932 SV_LinkEdict_TouchAreaGrid(ent);
2936 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2941 void SV_Physics_ClientMove(void)
2943 prvm_prog_t *prog = SVVM_prog;
2945 ent = host_client->edict;
2947 // call player physics, this needs the proper frametime
2948 PRVM_serverglobalfloat(frametime) = sv.frametime;
2951 // call standard client pre-think, with frametime = 0
2952 PRVM_serverglobalfloat(time) = sv.time;
2953 PRVM_serverglobalfloat(frametime) = 0;
2954 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2955 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2956 PRVM_serverglobalfloat(frametime) = sv.frametime;
2958 // make sure the velocity is sane (not a NaN)
2959 SV_CheckVelocity(ent);
2961 // perform MOVETYPE_WALK behavior
2964 // call standard player post-think, with frametime = 0
2965 PRVM_serverglobalfloat(time) = sv.time;
2966 PRVM_serverglobalfloat(frametime) = 0;
2967 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2968 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2969 PRVM_serverglobalfloat(frametime) = sv.frametime;
2971 if(PRVM_serveredictfloat(ent, fixangle))
2973 // angle fixing was requested by physics code...
2974 // so store the current angles for later use
2975 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2976 host_client->fixangle_angles_set = TRUE;
2978 // and clear fixangle for the next frame
2979 PRVM_serveredictfloat(ent, fixangle) = 0;
2983 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2985 prvm_prog_t *prog = SVVM_prog;
2986 // don't do physics on disconnected clients, FrikBot relies on this
2987 if (!host_client->begun)
2990 // make sure the velocity is sane (not a NaN)
2991 SV_CheckVelocity(ent);
2993 // don't run physics here if running asynchronously
2994 if (host_client->clmovement_inputtimeout <= 0)
2997 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
3000 // make sure the velocity is still sane (not a NaN)
3001 SV_CheckVelocity(ent);
3003 // call standard client pre-think
3004 PRVM_serverglobalfloat(time) = sv.time;
3005 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3006 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3008 // make sure the velocity is still sane (not a NaN)
3009 SV_CheckVelocity(ent);
3012 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3014 prvm_prog_t *prog = SVVM_prog;
3015 // don't do physics on disconnected clients, FrikBot relies on this
3016 if (!host_client->begun)
3019 // make sure the velocity is sane (not a NaN)
3020 SV_CheckVelocity(ent);
3022 // call standard player post-think
3023 PRVM_serverglobalfloat(time) = sv.time;
3024 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3025 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3027 // make sure the velocity is still sane (not a NaN)
3028 SV_CheckVelocity(ent);
3030 if(PRVM_serveredictfloat(ent, fixangle))
3032 // angle fixing was requested by physics code...
3033 // so store the current angles for later use
3034 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3035 host_client->fixangle_angles_set = TRUE;
3037 // and clear fixangle for the next frame
3038 PRVM_serveredictfloat(ent, fixangle) = 0;
3041 // decrement the countdown variable used to decide when to go back to
3042 // synchronous physics
3043 if (host_client->clmovement_inputtimeout > sv.frametime)
3044 host_client->clmovement_inputtimeout -= sv.frametime;
3046 host_client->clmovement_inputtimeout = 0;
3049 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3051 prvm_prog_t *prog = SVVM_prog;
3052 // don't do physics on disconnected clients, FrikBot relies on this
3053 if (!host_client->begun)
3055 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3059 // make sure the velocity is sane (not a NaN)
3060 SV_CheckVelocity(ent);
3062 switch ((int) PRVM_serveredictfloat(ent, movetype))
3065 case MOVETYPE_FAKEPUSH:
3066 SV_Physics_Pusher (ent);
3069 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3070 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3073 case MOVETYPE_FOLLOW:
3074 SV_Physics_Follow (ent);
3076 case MOVETYPE_NOCLIP:
3079 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3080 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3083 SV_Physics_Step (ent);
3087 // don't run physics here if running asynchronously
3088 if (host_client->clmovement_inputtimeout <= 0)
3092 case MOVETYPE_BOUNCE:
3093 case MOVETYPE_BOUNCEMISSILE:
3094 case MOVETYPE_FLYMISSILE:
3097 SV_Physics_Toss (ent);
3100 case MOVETYPE_FLY_WORLDONLY:
3104 case MOVETYPE_PHYSICS:
3108 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3112 SV_CheckVelocity (ent);
3115 SV_LinkEdict_TouchAreaGrid(ent);
3117 SV_CheckVelocity (ent);
3126 void SV_Physics (void)
3128 prvm_prog_t *prog = SVVM_prog;
3132 // let the progs know that a new frame has started
3133 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3134 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3135 PRVM_serverglobalfloat(time) = sv.time;
3136 PRVM_serverglobalfloat(frametime) = sv.frametime;
3137 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3139 // run physics engine
3140 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3143 // treat each object in turn
3146 // if force_retouch, relink all the entities
3147 if (PRVM_serverglobalfloat(force_retouch) > 0)
3148 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3149 if (!ent->priv.server->free)
3150 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3152 if (sv_gameplayfix_consistentplayerprethink.integer)
3154 // run physics on the client entities in 3 stages
3155 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3156 if (!ent->priv.server->free)
3157 SV_Physics_ClientEntity_PreThink(ent);
3159 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3160 if (!ent->priv.server->free)
3161 SV_Physics_ClientEntity(ent);
3163 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3164 if (!ent->priv.server->free)
3165 SV_Physics_ClientEntity_PostThink(ent);
3169 // run physics on the client entities
3170 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3172 if (!ent->priv.server->free)
3174 SV_Physics_ClientEntity_PreThink(ent);
3175 SV_Physics_ClientEntity(ent);
3176 SV_Physics_ClientEntity_PostThink(ent);
3181 // run physics on all the non-client entities
3182 if (!sv_freezenonclients.integer)
3184 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3185 if (!ent->priv.server->free)
3186 SV_Physics_Entity(ent);
3187 // make a second pass to see if any ents spawned this frame and make
3188 // sure they run their move/think
3189 if (sv_gameplayfix_delayprojectiles.integer < 0)
3190 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3191 if (!ent->priv.server->move && !ent->priv.server->free)
3192 SV_Physics_Entity(ent);
3195 if (PRVM_serverglobalfloat(force_retouch) > 0)
3196 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3198 // LordHavoc: endframe support
3199 if (PRVM_serverfunction(EndFrame))
3201 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3202 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3203 PRVM_serverglobalfloat(time) = sv.time;
3204 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3207 // decrement prog->num_edicts if the highest number entities died
3208 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3210 if (!sv_freezenonclients.integer)
3211 sv.time += sv.frametime;