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.worldstartsolid = 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.worldstartsolid = 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.worldstartsolid = 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)
641 prvm_prog_t *prog = SVVM_prog;
645 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
648 VectorCopy(trace.endpos, temp);
649 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
650 #if COLLISIONPARANOID < 3
651 if (trace.startsolid || endstuck)
653 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" : "");
659 int SV_PointSuperContents(const vec3_t point)
661 prvm_prog_t *prog = SVVM_prog;
662 int supercontents = 0;
666 // matrices to transform into/out of other entity's space
667 matrix4x4_t matrix, imatrix;
668 // model of other entity
671 // list of entities to test for collisions
673 static prvm_edict_t *touchedicts[MAX_EDICTS];
675 // get world supercontents at this point
676 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
677 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
679 // if sv_gameplayfix_swiminbmodels is off we're done
680 if (!sv_gameplayfix_swiminbmodels.integer)
681 return supercontents;
683 // get list of entities at this point
684 numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
685 if (numtouchedicts > MAX_EDICTS)
687 // this never happens
688 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
689 numtouchedicts = MAX_EDICTS;
691 for (i = 0;i < numtouchedicts;i++)
693 touch = touchedicts[i];
695 // we only care about SOLID_BSP for pointcontents
696 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
699 // might interact, so do an exact clip
700 model = SV_GetModelFromEdict(touch);
701 if (!model || !model->PointSuperContents)
703 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);
704 Matrix4x4_Invert_Simple(&imatrix, &matrix);
705 Matrix4x4_Transform(&imatrix, point, transformed);
706 frame = (int)PRVM_serveredictfloat(touch, frame);
707 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
710 return supercontents;
714 ===============================================================================
716 Linking entities into the world culling system
718 ===============================================================================
721 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
723 prvm_prog_t *prog = SVVM_prog;
724 vec3_t paddedmins, paddedmaxs;
725 if (maxedicts < 1 || resultedicts == NULL)
727 // LordHavoc: discovered this actually causes its own bugs (dm6 teleporters being too close to info_teleport_destination)
728 //VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
729 //VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
730 VectorCopy(mins, paddedmins);
731 VectorCopy(maxs, paddedmaxs);
732 if (sv_areadebug.integer)
734 int numresultedicts = 0;
737 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
739 ed = PRVM_EDICT_NUM(edictindex);
740 if (!ed->priv.required->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
742 resultedicts[numresultedicts++] = ed;
743 if (numresultedicts == maxedicts)
747 return numresultedicts;
750 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
753 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
755 prvm_prog_t *prog = SVVM_prog;
756 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
757 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
758 PRVM_serverglobalfloat(time) = sv.time;
759 PRVM_serverglobalfloat(trace_allsolid) = false;
760 PRVM_serverglobalfloat(trace_startsolid) = false;
761 PRVM_serverglobalfloat(trace_fraction) = 1;
762 PRVM_serverglobalfloat(trace_inwater) = false;
763 PRVM_serverglobalfloat(trace_inopen) = true;
764 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
765 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
766 PRVM_serverglobalfloat(trace_plane_dist) = 0;
767 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
768 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
769 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
770 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
771 PRVM_serverglobalstring(trace_dphittexturename) = 0;
772 prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
775 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
777 prvm_prog_t *prog = SVVM_prog;
778 int i, numtouchedicts, old_self, old_other;
780 static prvm_edict_t *touchedicts[MAX_EDICTS];
782 if (ent == prog->edicts)
783 return; // don't add the world
785 if (ent->priv.server->free)
788 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
791 // build a list of edicts to touch, because the link loop can be corrupted
792 // by IncreaseEdicts called during touch functions
793 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
794 if (numtouchedicts > MAX_EDICTS)
796 // this never happens
797 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
798 numtouchedicts = MAX_EDICTS;
801 old_self = PRVM_serverglobaledict(self);
802 old_other = PRVM_serverglobaledict(other);
803 for (i = 0;i < numtouchedicts;i++)
805 touch = touchedicts[i];
806 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
808 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
811 PRVM_serverglobaledict(self) = old_self;
812 PRVM_serverglobaledict(other) = old_other;
815 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
819 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
821 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
822 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
823 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
824 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];
825 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];
826 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
827 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];
828 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];
829 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
830 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];
831 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];
832 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
833 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];
834 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];
835 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
836 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];
837 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];
838 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
839 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];
840 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];
841 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
842 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];
843 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];
852 void SV_LinkEdict (prvm_edict_t *ent)
854 prvm_prog_t *prog = SVVM_prog;
856 vec3_t mins, maxs, entmins, entmaxs, entangles;
859 if (ent == prog->edicts)
860 return; // don't add the world
862 if (ent->priv.server->free)
865 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
866 if (modelindex < 0 || modelindex >= MAX_MODELS)
868 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
871 model = SV_GetModelByIndex(modelindex);
873 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
874 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
875 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
879 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
881 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
882 // TODO special handling for spheres?
883 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
884 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
885 VectorCopy(PRVM_serveredictvector(ent, angles), entangles);
886 RotateBBox(entmins, entmaxs, entangles, mins, maxs);
887 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
888 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
890 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
894 if (!model->TraceBox)
895 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
897 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
899 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
900 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
902 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
904 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
905 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
909 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
910 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
915 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
916 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
917 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
922 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
923 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
927 // to make items easier to pick up and allow them to be grabbed off
928 // of shelves, the abs sizes are expanded
930 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
941 // because movement is clipped an epsilon away from an actual edge,
942 // we must fully check even when bounding boxes don't quite touch
951 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
952 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
954 World_LinkEdict(&sv.world, ent, mins, maxs);
958 ===============================================================================
962 ===============================================================================
967 SV_TestEntityPosition
969 returns true if the entity is in solid currently
972 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
974 prvm_prog_t *prog = SVVM_prog;
976 vec3_t org, entorigin, entmins, entmaxs;
978 contents = SV_GenericHitSuperContentsMask(ent);
979 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
980 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
981 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
982 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
983 trace = SV_TraceBox(org, entmins, entmaxs, entorigin, ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, contents);
984 if (trace.startsupercontents & contents)
988 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
990 // q1bsp/hlbsp use hulls and if the entity does not exactly match
991 // a hull size it is incorrectly tested, so this code tries to
992 // 'fix' it slightly...
993 // FIXME: this breaks entities larger than the hull size
996 VectorAdd(org, entmins, m1);
997 VectorAdd(org, entmaxs, m2);
998 VectorSubtract(m2, m1, s);
999 #define EPSILON (1.0f / 32.0f)
1000 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
1001 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
1002 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
1003 for (i = 0;i < 8;i++)
1005 v[0] = (i & 1) ? m2[0] : m1[0];
1006 v[1] = (i & 2) ? m2[1] : m1[1];
1007 v[2] = (i & 4) ? m2[2] : m1[2];
1008 if (SV_PointSuperContents(v) & contents)
1013 // if the trace found a better position for the entity, move it there
1014 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
1017 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
1018 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
1020 // verify if the endpos is REALLY outside solid
1021 VectorCopy(trace.endpos, org);
1022 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, contents);
1023 if(trace.startsolid)
1024 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
1026 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1032 // DRESK - Support for Entity Contents Transition Event
1035 SV_CheckContentsTransition
1037 returns true if entity had a valid contentstransition function call
1040 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
1042 prvm_prog_t *prog = SVVM_prog;
1043 int bValidFunctionCall;
1045 // Default Valid Function Call to False
1046 bValidFunctionCall = false;
1048 if(PRVM_serveredictfloat(ent, watertype) != nContents)
1049 { // Changed Contents
1050 // Acquire Contents Transition Function from QC
1051 if(PRVM_serveredictfunction(ent, contentstransition))
1052 { // Valid Function; Execute
1053 // Assign Valid Function
1054 bValidFunctionCall = true;
1055 // Prepare Parameters (Original Contents, New Contents)
1056 // Original Contents
1057 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
1059 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1061 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1063 PRVM_serverglobalfloat(time) = sv.time;
1064 // Execute VM Function
1065 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1069 // Return if Function Call was Valid
1070 return bValidFunctionCall;
1079 void SV_CheckVelocity (prvm_edict_t *ent)
1081 prvm_prog_t *prog = SVVM_prog;
1088 for (i=0 ; i<3 ; i++)
1090 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1092 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1093 PRVM_serveredictvector(ent, velocity)[i] = 0;
1095 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1097 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1098 PRVM_serveredictvector(ent, origin)[i] = 0;
1102 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1103 // player_run/player_stand1 does not horribly malfunction if the
1104 // velocity becomes a denormalized float
1105 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0001)
1106 VectorClear(PRVM_serveredictvector(ent, velocity));
1108 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1109 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1110 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1112 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1113 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1114 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1115 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1123 Runs thinking code if time. There is some play in the exact time the think
1124 function will be called, because it is called before any movement is done
1125 in a frame. Not used for pushmove objects, because they must be exact.
1126 Returns false if the entity removed itself.
1129 static qboolean SV_RunThink (prvm_edict_t *ent)
1131 prvm_prog_t *prog = SVVM_prog;
1134 // don't let things stay in the past.
1135 // it is possible to start that way by a trigger with a local time.
1136 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1139 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1141 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1142 PRVM_serveredictfloat(ent, nextthink) = 0;
1143 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1144 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1145 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1146 // mods often set nextthink to time to cause a think every frame,
1147 // we don't want to loop in that case, so exit if the new nextthink is
1148 // <= the time the qc was told, also exit if it is past the end of the
1150 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1153 return !ent->priv.server->free;
1160 Two entities have touched, so run their touch functions
1163 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1165 prvm_prog_t *prog = SVVM_prog;
1166 int restorevm_tempstringsbuf_cursize;
1167 int old_self, old_other;
1168 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1170 old_self = PRVM_serverglobaledict(self);
1171 old_other = PRVM_serverglobaledict(other);
1172 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1174 VM_SetTraceGlobals(prog, trace);
1176 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1178 PRVM_serverglobalfloat(time) = sv.time;
1179 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1180 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1181 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1184 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1186 PRVM_serverglobalfloat(time) = sv.time;
1187 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1188 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1189 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1190 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1191 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1192 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1193 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1194 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1195 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1196 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1197 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1200 PRVM_serverglobaledict(self) = old_self;
1201 PRVM_serverglobaledict(other) = old_other;
1202 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1210 Slide off of the impacting object
1211 returns the blocked flags (1 = floor, 2 = step / wall)
1214 #define STOP_EPSILON 0.1
1215 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1220 backoff = -DotProduct (in, normal) * overbounce;
1221 VectorMA(in, backoff, normal, out);
1223 for (i = 0;i < 3;i++)
1224 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1233 The basic solid body movement clip that slides along multiple planes
1234 Returns the clipflags if the velocity was modified (hit something solid)
1238 8 = teleported by touch method
1239 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1242 static float SV_Gravity (prvm_edict_t *ent);
1243 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink);
1244 #define MAX_CLIP_PLANES 5
1245 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1247 prvm_prog_t *prog = SVVM_prog;
1248 int blocked, bumpcount;
1249 int i, j, numplanes;
1250 float d, time_left, gravity;
1251 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1252 prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
1261 VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1265 gravity = SV_Gravity(ent);
1267 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1269 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1270 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1272 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1277 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1278 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1281 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1283 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1286 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1287 if(!SV_PushEntity(&trace, ent, push, false))
1289 // we got teleported by a touch function
1290 // let's abort the move
1295 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
1296 // abort move if we're stuck in the world (and didn't make it out)
1297 if (trace.worldstartsolid && trace.allsolid)
1299 VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
1303 if (trace.fraction == 1)
1305 if (trace.plane.normal[2])
1307 if (trace.plane.normal[2] > 0.7)
1314 Con_Printf ("SV_FlyMove: !trace.ent");
1315 trace.ent = prog->edicts;
1318 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1319 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1322 else if (stepheight)
1324 // step - handle it immediately
1330 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1331 VectorSet(steppush, 0, 0, stepheight);
1332 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1333 if(!SV_PushEntity(&steptrace, ent, steppush, false))
1338 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1339 if(!SV_PushEntity(&steptrace2, ent, push, false))
1344 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1345 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1346 if(!SV_PushEntity(&steptrace3, ent, steppush, false))
1351 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1352 // accept the new position if it made some progress...
1353 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1355 //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]);
1357 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1358 time_left *= 1 - trace.fraction;
1364 //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]);
1365 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1370 // step - return it to caller
1372 // save the trace for player extrafriction
1374 VectorCopy(trace.plane.normal, stepnormal);
1376 if (trace.fraction >= 0.001)
1378 // actually covered some distance
1379 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1383 time_left *= 1 - trace.fraction;
1385 // clipped to another plane
1386 if (numplanes >= MAX_CLIP_PLANES)
1388 // this shouldn't really happen
1389 VectorClear(PRVM_serveredictvector(ent, velocity));
1395 for (i = 0;i < numplanes;i++)
1396 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1400 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1405 VectorCopy(trace.plane.normal, planes[numplanes]);
1408 // modify original_velocity so it parallels all of the clip planes
1409 for (i = 0;i < numplanes;i++)
1411 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1412 for (j = 0;j < numplanes;j++)
1417 if (DotProduct(new_velocity, planes[j]) < 0)
1427 // go along this plane
1428 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1432 // go along the crease
1435 VectorClear(PRVM_serveredictvector(ent, velocity));
1439 CrossProduct(planes[0], planes[1], dir);
1440 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1441 VectorNormalize(dir);
1442 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1443 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1446 // if current velocity is against the original velocity,
1447 // stop dead to avoid tiny occilations in sloping corners
1448 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1450 VectorClear(PRVM_serveredictvector(ent, velocity));
1455 //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]);
1458 if ((blocked & 1) == 0 && bumpcount > 1)
1460 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1461 // flag ONGROUND if there's ground under it
1462 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask);
1466 // LordHavoc: this came from QW and allows you to get out of water more easily
1467 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1468 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1472 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1474 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1475 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1488 static float SV_Gravity (prvm_edict_t *ent)
1490 prvm_prog_t *prog = SVVM_prog;
1493 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1496 return ent_gravity * sv_gravity.value * sv.frametime;
1501 ===============================================================================
1505 ===============================================================================
1508 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1510 prvm_prog_t *prog = SVVM_prog;
1514 vec3_t stuckmins, stuckmaxs;
1515 vec3_t goodmins, goodmaxs;
1519 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1520 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1521 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1522 VectorCopy(pivot, goodmins);
1523 VectorCopy(pivot, goodmaxs);
1524 for (bump = 0;bump < 6;bump++)
1526 int coord = 2-(bump >> 1);
1527 //int coord = (bump >> 1);
1528 int dir = (bump & 1);
1531 for(subbump = 0; ; ++subbump)
1533 VectorCopy(stuckorigin, testorigin);
1537 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1542 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1545 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1546 if (stucktrace.bmodelstartsolid)
1548 // BAD BAD, can't fix that
1552 if (stucktrace.fraction >= 1)
1557 // BAD BAD, can't fix that
1561 // we hit something... let's move out of it
1562 VectorSubtract(stucktrace.endpos, testorigin, move);
1563 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1564 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1568 Con_Printf("subbump: %d\n", subbump);
1574 goodmaxs[coord] = stuckmaxs[coord];
1579 goodmins[coord] = stuckmins[coord];
1584 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1589 qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1591 prvm_prog_t *prog = SVVM_prog;
1595 vec3_t stuckmins, stuckmaxs;
1597 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1598 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1599 separation = 0.0f; // when using hulls, it can not be enlarged
1600 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1601 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1602 stuckmins[0] -= separation;
1603 stuckmins[1] -= separation;
1604 stuckmins[2] -= separation;
1605 stuckmaxs[0] += separation;
1606 stuckmaxs[1] += separation;
1607 stuckmaxs[2] += separation;
1608 // first pass we try to get it out of brush entities
1609 // second pass we try to get it out of world only (can't win them all)
1610 for (pass = 0;pass < 2;pass++)
1612 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1613 for (bump = 0;bump < 10;bump++)
1615 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1616 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1618 // found a good location, use it
1619 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1622 nudge = -stucktrace.startdepth;
1623 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1633 Does not change the entities velocity at all
1634 The trace struct is filled with the trace that has been done.
1635 Returns true if the push did not result in the entity being teleported by QC code.
1638 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink)
1640 prvm_prog_t *prog = SVVM_prog;
1648 solid = (int)PRVM_serveredictfloat(ent, solid);
1649 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1650 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1651 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1653 // move start position out of solids
1654 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1656 SV_NudgeOutOfSolid(ent);
1659 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1660 VectorAdd(start, push, end);
1662 if (movetype == MOVETYPE_FLYMISSILE)
1663 type = MOVE_MISSILE;
1664 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1665 type = MOVE_WORLDONLY;
1666 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1667 type = MOVE_NOMONSTERS; // only clip against bmodels
1671 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1672 // fail the move if stuck in world
1673 if (trace->worldstartsolid)
1676 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1678 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1683 if(!trace->startsolid)
1684 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)
1686 Con_Printf("something eeeeevil happened\n");
1691 SV_LinkEdict_TouchAreaGrid(ent);
1693 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))))
1694 SV_Impact (ent, trace);
1696 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1698 ent->priv.required->mark = 0;
1701 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1703 ent->priv.required->mark = 0;
1708 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1720 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1722 prvm_prog_t *prog = SVVM_prog;
1724 int pusherowner, pusherprog;
1727 float savesolid, movetime2, pushltime;
1728 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1730 int numcheckentities;
1731 static prvm_edict_t *checkentities[MAX_EDICTS];
1732 dp_model_t *pushermodel;
1733 trace_t trace, trace2;
1734 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1735 static unsigned short moved_edicts[MAX_EDICTS];
1738 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])
1740 PRVM_serveredictfloat(pusher, ltime) += movetime;
1744 switch ((int) PRVM_serveredictfloat(pusher, solid))
1746 // LordHavoc: valid pusher types
1749 case SOLID_SLIDEBOX:
1750 case SOLID_CORPSE: // LordHavoc: this would be weird...
1752 // LordHavoc: no collisions
1755 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1756 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1757 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1758 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1759 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1760 PRVM_serveredictfloat(pusher, ltime) += movetime;
1761 SV_LinkEdict(pusher);
1764 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1767 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1768 if (index < 1 || index >= MAX_MODELS)
1770 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1773 pushermodel = SV_GetModelByIndex(index);
1774 pusherowner = PRVM_serveredictedict(pusher, owner);
1775 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1777 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1779 movetime2 = movetime;
1780 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1781 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1782 if (moveangle[0] || moveangle[2])
1784 for (i = 0;i < 3;i++)
1788 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1789 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1793 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1794 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1798 else if (moveangle[1])
1800 for (i = 0;i < 3;i++)
1804 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1805 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1809 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1810 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1816 for (i = 0;i < 3;i++)
1820 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1821 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1825 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1826 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1831 VectorNegate (moveangle, a);
1832 AngleVectorsFLU (a, forward, left, up);
1834 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1835 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1836 pushltime = PRVM_serveredictfloat(pusher, ltime);
1838 // move the pusher to its final position
1840 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1841 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1842 PRVM_serveredictfloat(pusher, ltime) += movetime;
1843 SV_LinkEdict(pusher);
1845 pushermodel = SV_GetModelFromEdict(pusher);
1846 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);
1847 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1849 savesolid = PRVM_serveredictfloat(pusher, solid);
1851 // see if any solid entities are inside the final position
1854 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1855 numcheckentities = 0;
1856 else // MOVETYPE_PUSH
1857 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1858 for (e = 0;e < numcheckentities;e++)
1860 prvm_edict_t *check = checkentities[e];
1861 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1866 case MOVETYPE_FOLLOW:
1867 case MOVETYPE_NOCLIP:
1868 case MOVETYPE_FLY_WORLDONLY:
1874 if (PRVM_serveredictedict(check, owner) == pusherprog)
1877 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1880 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1882 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1883 check->priv.server->waterposition_forceupdate = true;
1885 checkcontents = SV_GenericHitSuperContentsMask(check);
1887 // if the entity is standing on the pusher, it will definitely be moved
1888 // if the entity is not standing on the pusher, but is in the pusher's
1889 // final position, move it
1890 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1892 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1893 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1894 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1895 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1896 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1897 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents);
1898 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1899 if (!trace.startsolid)
1901 //Con_Printf("- not in solid\n");
1906 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1907 //VectorClear(pivot);
1912 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1913 VectorAdd (org, pivot, org);
1914 org2[0] = DotProduct (org, forward);
1915 org2[1] = DotProduct (org, left);
1916 org2[2] = DotProduct (org, up);
1917 VectorSubtract (org2, org, move);
1918 VectorAdd (move, move1, move);
1921 VectorCopy (move1, move);
1923 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1925 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1926 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1927 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1929 // physics objects need better collisions than this code can do
1930 if (movetype == MOVETYPE_PHYSICS)
1932 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1933 SV_LinkEdict(check);
1934 SV_LinkEdict_TouchAreaGrid(check);
1938 // try moving the contacted entity
1939 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1940 if(!SV_PushEntity (&trace, check, move, true))
1942 // entity "check" got teleported
1943 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1944 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1945 continue; // pushed enough
1947 // FIXME: turn players specially
1948 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1949 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1950 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1952 // this trace.fraction < 1 check causes items to fall off of pushers
1953 // if they pass under or through a wall
1954 // the groundentity check causes items to fall off of ledges
1955 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1956 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1958 // if it is still inside the pusher, block
1959 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1960 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1961 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1962 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1963 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1964 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents);
1965 if (trace.startsolid)
1968 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1970 // hack to invoke all necessary movement triggers
1972 if(!SV_PushEntity(&trace2, check, move2, true))
1974 // entity "check" got teleported
1981 // still inside pusher, so it's really blocked
1984 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1986 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1989 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1990 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1994 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1995 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1996 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1997 SV_LinkEdict(pusher);
1999 // move back any entities we already moved
2000 for (i = 0;i < num_moved;i++)
2002 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
2003 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
2004 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
2008 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
2009 if (PRVM_serveredictfunction(pusher, blocked))
2011 PRVM_serverglobalfloat(time) = sv.time;
2012 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
2013 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
2014 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
2019 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
2020 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
2021 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
2030 static void SV_Physics_Pusher (prvm_edict_t *ent)
2032 prvm_prog_t *prog = SVVM_prog;
2033 float thinktime, oldltime, movetime;
2035 oldltime = PRVM_serveredictfloat(ent, ltime);
2037 thinktime = PRVM_serveredictfloat(ent, nextthink);
2038 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
2040 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
2045 movetime = sv.frametime;
2048 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
2049 SV_PushMove (ent, movetime);
2051 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
2053 PRVM_serveredictfloat(ent, nextthink) = 0;
2054 PRVM_serverglobalfloat(time) = sv.time;
2055 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2056 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
2057 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2063 ===============================================================================
2067 ===============================================================================
2070 static float unstickoffsets[] =
2072 // poutting -/+z changes first as they are least weird
2087 typedef enum unstickresult_e
2095 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2097 prvm_prog_t *prog = SVVM_prog;
2100 // if not stuck in a bmodel, just return
2101 if (!SV_TestEntityPosition(ent, vec3_origin))
2102 return UNSTICK_GOOD;
2104 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2106 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2108 VectorCopy(unstickoffsets + i, offset);
2110 //SV_LinkEdict_TouchAreaGrid(ent);
2111 return UNSTICK_UNSTUCK;
2115 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2116 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2118 for(i = 2; i <= maxunstick; ++i)
2120 VectorClear(offset);
2122 if (!SV_TestEntityPosition(ent, offset))
2125 //SV_LinkEdict_TouchAreaGrid(ent);
2126 return UNSTICK_UNSTUCK;
2129 if (!SV_TestEntityPosition(ent, offset))
2132 //SV_LinkEdict_TouchAreaGrid(ent);
2133 return UNSTICK_UNSTUCK;
2137 return UNSTICK_STUCK;
2140 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2142 prvm_prog_t *prog = SVVM_prog;
2144 switch(SV_UnstickEntityReturnOffset(ent, offset))
2148 case UNSTICK_UNSTUCK:
2149 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]);
2152 if (developer_extra.integer)
2153 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2156 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2165 This is a big hack to try and fix the rare case of getting stuck in the world
2169 static void SV_CheckStuck (prvm_edict_t *ent)
2171 prvm_prog_t *prog = SVVM_prog;
2174 switch(SV_UnstickEntityReturnOffset(ent, offset))
2177 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2179 case UNSTICK_UNSTUCK:
2180 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]);
2183 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2184 if (!SV_TestEntityPosition(ent, offset))
2186 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)));
2188 //SV_LinkEdict_TouchAreaGrid(ent);
2191 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2194 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2204 static qboolean SV_CheckWater (prvm_edict_t *ent)
2206 prvm_prog_t *prog = SVVM_prog;
2208 int nNativeContents;
2211 point[0] = PRVM_serveredictvector(ent, origin)[0];
2212 point[1] = PRVM_serveredictvector(ent, origin)[1];
2213 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2215 // DRESK - Support for Entity Contents Transition Event
2216 // NOTE: Some logic needed to be slightly re-ordered
2217 // to not affect performance and allow for the feature.
2219 // Acquire Super Contents Prior to Resets
2220 cont = SV_PointSuperContents(point);
2221 // Acquire Native Contents Here
2222 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2224 // DRESK - Support for Entity Contents Transition Event
2225 if(PRVM_serveredictfloat(ent, watertype))
2226 // Entity did NOT Spawn; Check
2227 SV_CheckContentsTransition(ent, nNativeContents);
2230 PRVM_serveredictfloat(ent, waterlevel) = 0;
2231 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2232 cont = SV_PointSuperContents(point);
2233 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2235 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2236 PRVM_serveredictfloat(ent, waterlevel) = 1;
2237 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2238 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2240 PRVM_serveredictfloat(ent, waterlevel) = 2;
2241 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2242 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2243 PRVM_serveredictfloat(ent, waterlevel) = 3;
2247 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2256 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2258 prvm_prog_t *prog = SVVM_prog;
2260 vec3_t forward, into, side, v_angle;
2262 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2263 AngleVectors (v_angle, forward, NULL, NULL);
2264 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2266 // cut the tangential velocity
2267 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2268 VectorScale (stepnormal, i, into);
2269 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2270 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2271 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2277 =====================
2280 Player has come to a dead stop, possibly due to the problem with limited
2281 float precision at some angle joins in the BSP hull.
2283 Try fixing by pushing one pixel in each direction.
2285 This is a hack, but in the interest of good gameplay...
2286 ======================
2288 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2293 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2296 for (i=0 ; i<8 ; i++)
2298 // try pushing a little in an axial direction
2301 case 0: dir[0] = 2; dir[1] = 0; break;
2302 case 1: dir[0] = 0; dir[1] = 2; break;
2303 case 2: dir[0] = -2; dir[1] = 0; break;
2304 case 3: dir[0] = 0; dir[1] = -2; break;
2305 case 4: dir[0] = 2; dir[1] = 2; break;
2306 case 5: dir[0] = -2; dir[1] = 2; break;
2307 case 6: dir[0] = 2; dir[1] = -2; break;
2308 case 7: dir[0] = -2; dir[1] = -2; break;
2311 SV_PushEntity (&trace, ent, dir, false, true);
2313 // retry the original move
2314 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2315 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2316 PRVM_serveredictvector(ent, velocity)[2] = 0;
2317 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2319 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2320 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2322 Con_DPrint("TryUnstick - success.\n");
2326 // go back to the original pos and try again
2327 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2331 VectorClear (PRVM_serveredictvector(ent, velocity));
2332 Con_DPrint("TryUnstick - failure.\n");
2338 =====================
2341 Only used by players
2342 ======================
2344 static void SV_WalkMove (prvm_edict_t *ent)
2346 prvm_prog_t *prog = SVVM_prog;
2349 //int originalmove_clip;
2350 int originalmove_flags;
2351 int originalmove_groundentity;
2352 int hitsupercontentsmask;
2354 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2355 trace_t downtrace, trace;
2356 qboolean applygravity;
2358 // if frametime is 0 (due to client sending the same timestamp twice),
2360 if (sv.frametime <= 0)
2363 if (sv_gameplayfix_unstickplayers.integer)
2364 SV_CheckStuck (ent);
2366 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2368 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2370 SV_CheckVelocity(ent);
2372 // do a regular slide move unless it looks like you ran into a step
2373 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2375 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2376 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2378 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2380 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2383 // only try this if there was no floor in the way in the trace (no,
2384 // this check seems to be not REALLY necessary, because if clip & 1,
2385 // our trace will hit that thing too)
2386 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2387 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2388 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2389 type = MOVE_MISSILE;
2390 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2391 type = MOVE_WORLDONLY;
2392 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2393 type = MOVE_NOMONSTERS; // only clip against bmodels
2396 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2397 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2398 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2399 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2400 clip |= 1; // but we HAVE found a floor
2403 // if the move did not hit the ground at any point, we're not on ground
2405 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2407 SV_CheckVelocity(ent);
2409 SV_LinkEdict_TouchAreaGrid(ent);
2411 if(clip & 8) // teleport
2414 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2417 if (sv_nostep.integer)
2420 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2421 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2422 //originalmove_clip = clip;
2423 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2424 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2426 // if move didn't block on a step, return
2429 // if move was not trying to move into the step, return
2430 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2433 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2435 // return if gibbed by a trigger
2436 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2439 // return if attempting to jump while airborn (unless sv_jumpstep)
2440 if (!sv_jumpstep.integer)
2441 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2445 // try moving up and forward to go up a step
2446 // back to start pos
2447 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2448 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2451 VectorClear (upmove);
2452 upmove[2] = sv_stepheight.value;
2453 if(!SV_PushEntity(&trace, ent, upmove, true))
2455 // we got teleported when upstepping... must abort the move
2460 PRVM_serveredictvector(ent, velocity)[2] = 0;
2461 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2462 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2465 // we got teleported when upstepping... must abort the move
2466 // note that z velocity handling may not be what QC expects here, but we cannot help it
2470 SV_CheckVelocity(ent);
2472 SV_LinkEdict_TouchAreaGrid(ent);
2474 // check for stuckness, possibly due to the limited precision of floats
2475 // in the clipping hulls
2477 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2478 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2480 //Con_Printf("wall\n");
2481 // stepping up didn't make any progress, revert to original move
2482 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2483 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2484 //clip = originalmove_clip;
2485 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2486 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2487 // now try to unstick if needed
2488 //clip = SV_TryUnstick (ent, oldvel);
2492 //Con_Printf("step - ");
2494 // extra friction based on view angle
2495 if (clip & 2 && sv_wallfriction.integer)
2496 SV_WallFriction (ent, stepnormal);
2498 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2499 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))
2503 VectorClear (downmove);
2504 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2505 if(!SV_PushEntity (&downtrace, ent, downmove, true))
2507 // we got teleported when downstepping... must abort the move
2511 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2513 // this has been disabled so that you can't jump when you are stepping
2514 // up while already jumping (also known as the Quake2 double jump bug)
2516 // LordHavoc: disabled this check so you can walk on monsters/players
2517 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2519 //Con_Printf("onground\n");
2520 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2521 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2527 //Con_Printf("slope\n");
2528 // if the push down didn't end up on good ground, use the move without
2529 // the step up. This happens near wall / slope combinations, and can
2530 // cause the player to hop up higher on a slope too steep to climb
2531 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2532 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2533 //clip = originalmove_clip;
2534 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2535 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2538 SV_CheckVelocity(ent);
2540 SV_LinkEdict_TouchAreaGrid(ent);
2543 //============================================================================
2549 Entities that are "stuck" to another entity
2552 static void SV_Physics_Follow (prvm_edict_t *ent)
2554 prvm_prog_t *prog = SVVM_prog;
2555 vec3_t vf, vr, vu, angles, v;
2559 if (!SV_RunThink (ent))
2562 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2563 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2564 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])
2566 // quick case for no rotation
2567 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2571 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2572 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2573 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2574 AngleVectors (angles, vf, vr, vu);
2575 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];
2576 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];
2577 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];
2578 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2579 angles[1] = PRVM_serveredictvector(e, angles)[1];
2580 angles[2] = PRVM_serveredictvector(e, angles)[2];
2581 AngleVectors (angles, vf, vr, vu);
2582 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2583 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2584 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2586 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2588 //SV_LinkEdict_TouchAreaGrid(ent);
2592 ==============================================================================
2596 ==============================================================================
2601 SV_CheckWaterTransition
2605 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2608 prvm_prog_t *prog = SVVM_prog;
2609 // 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
2611 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2612 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(entorigin));
2613 if (!PRVM_serveredictfloat(ent, watertype))
2615 // just spawned here
2616 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2618 PRVM_serveredictfloat(ent, watertype) = cont;
2619 PRVM_serveredictfloat(ent, waterlevel) = 1;
2623 // DRESK - Support for Entity Contents Transition Event
2624 // NOTE: Call here BEFORE updating the watertype below,
2625 // and suppress watersplash sound if a valid function
2626 // call was made to allow for custom "splash" sounds.
2627 else if( !SV_CheckContentsTransition(ent, cont) )
2628 { // Contents Transition Function Invalid; Potentially Play Water Sound
2629 // check if the entity crossed into or out of water
2630 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2631 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2634 if (cont <= CONTENTS_WATER)
2636 PRVM_serveredictfloat(ent, watertype) = cont;
2637 PRVM_serveredictfloat(ent, waterlevel) = 1;
2641 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2642 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2650 Toss, bounce, and fly movement. When onground, do nothing.
2654 void SV_Physics_Toss (prvm_edict_t *ent)
2656 prvm_prog_t *prog = SVVM_prog;
2661 prvm_edict_t *groundentity;
2662 float d, ent_gravity;
2666 // if onground, return without moving
2667 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2669 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2670 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2672 // don't stick to ground if onground and moving upward
2673 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2675 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2677 // we can trust FL_ONGROUND if groundentity is world because it never moves
2680 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2682 // if ent was supported by a brush model on previous frame,
2683 // and groundentity is now freed, set groundentity to 0 (world)
2684 // which leaves it suspended in the air
2685 PRVM_serveredictedict(ent, groundentity) = 0;
2686 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2689 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2691 // don't slide if still touching the groundentity
2695 ent->priv.server->suspendedinairflag = false;
2697 SV_CheckVelocity (ent);
2700 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2701 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2704 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2706 movetime = sv.frametime;
2707 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2710 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2711 if(!SV_PushEntity(&trace, ent, move, true))
2712 return; // teleported
2713 if (ent->priv.server->free)
2715 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2717 // try to unstick the entity
2718 SV_UnstickEntity(ent);
2719 if(!SV_PushEntity(&trace, ent, move, true))
2720 return; // teleported
2721 if (ent->priv.server->free)
2724 if (trace.fraction == 1)
2726 movetime *= 1 - min(1, trace.fraction);
2727 switch((int)PRVM_serveredictfloat(ent, movetype))
2729 case MOVETYPE_BOUNCEMISSILE:
2730 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2732 bouncefactor = 1.0f;
2734 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2735 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2736 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2739 case MOVETYPE_BOUNCE:
2740 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2742 bouncefactor = 0.5f;
2744 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2746 bouncestop = 60.0f / 800.0f;
2748 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2749 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2752 // LordHavoc: fixed grenades not bouncing when fired down a slope
2753 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2754 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2756 d = PRVM_serveredictvector(ent, velocity)[2];
2757 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2759 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2760 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2761 VectorClear(PRVM_serveredictvector(ent, velocity));
2762 VectorClear(PRVM_serveredictvector(ent, avelocity));
2767 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2768 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2773 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2774 if (trace.plane.normal[2] > 0.7)
2776 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2777 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2778 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2779 ent->priv.server->suspendedinairflag = true;
2780 VectorClear (PRVM_serveredictvector(ent, velocity));
2781 VectorClear (PRVM_serveredictvector(ent, avelocity));
2784 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2790 // check for in water
2791 SV_CheckWaterTransition (ent);
2795 ===============================================================================
2799 ===============================================================================
2806 Monsters freefall when they don't have a ground entity, otherwise
2807 all movement is done with discrete steps.
2809 This is also used for objects that have become still on the ground, but
2810 will fall if the floor is pulled out from under them.
2813 static void SV_Physics_Step (prvm_edict_t *ent)
2815 prvm_prog_t *prog = SVVM_prog;
2816 int flags = (int)PRVM_serveredictfloat(ent, flags);
2819 // Backup Velocity in the event that movetypesteplandevent is called,
2820 // to provide a parameter with the entity's velocity at impact.
2821 vec3_t backupVelocity;
2822 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2823 // don't fall at all if fly/swim
2824 if (!(flags & (FL_FLY | FL_SWIM)))
2826 if (flags & FL_ONGROUND)
2828 // freefall if onground and moving upward
2829 // freefall if not standing on a world surface (it may be a lift or trap door)
2830 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2832 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2833 SV_CheckVelocity(ent);
2834 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2836 SV_LinkEdict_TouchAreaGrid(ent);
2837 ent->priv.server->waterposition_forceupdate = true;
2842 // freefall if not onground
2843 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2845 SV_CheckVelocity(ent);
2846 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2848 SV_LinkEdict_TouchAreaGrid(ent);
2851 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2853 // DRESK - Check for Entity Land Event Function
2854 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2855 { // Valid Function; Execute
2856 // Prepare Parameters
2857 // Assign Velocity at Impact
2858 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2859 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2860 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2862 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2864 PRVM_serverglobalfloat(time) = sv.time;
2865 // Execute VM Function
2866 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2869 // Check for Engine Landing Sound
2870 if(sv_sound_land.string)
2871 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2873 ent->priv.server->waterposition_forceupdate = true;
2878 if (!SV_RunThink(ent))
2881 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2883 ent->priv.server->waterposition_forceupdate = false;
2884 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2885 SV_CheckWaterTransition(ent);
2889 //============================================================================
2891 static void SV_Physics_Entity (prvm_edict_t *ent)
2893 prvm_prog_t *prog = SVVM_prog;
2894 // don't run think/move on newly spawned projectiles as it messes up
2895 // movement interpolation and rocket trails, and is inconsistent with
2896 // respect to entities spawned in the same frame
2897 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2898 // but if it spawns a lower numbered ent, it doesn't - this never moves
2899 // ents in the first frame regardless)
2900 qboolean runmove = ent->priv.server->move;
2901 ent->priv.server->move = true;
2902 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2904 switch ((int) PRVM_serveredictfloat(ent, movetype))
2907 case MOVETYPE_FAKEPUSH:
2908 SV_Physics_Pusher (ent);
2911 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2912 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2915 case MOVETYPE_FOLLOW:
2916 SV_Physics_Follow (ent);
2918 case MOVETYPE_NOCLIP:
2919 if (SV_RunThink(ent))
2922 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2923 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2928 SV_Physics_Step (ent);
2931 if (SV_RunThink (ent))
2935 case MOVETYPE_BOUNCE:
2936 case MOVETYPE_BOUNCEMISSILE:
2937 case MOVETYPE_FLYMISSILE:
2939 case MOVETYPE_FLY_WORLDONLY:
2941 if (SV_RunThink (ent))
2942 SV_Physics_Toss (ent);
2944 case MOVETYPE_PHYSICS:
2945 if (SV_RunThink(ent))
2948 SV_LinkEdict_TouchAreaGrid(ent);
2952 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2957 void SV_Physics_ClientMove(void)
2959 prvm_prog_t *prog = SVVM_prog;
2961 ent = host_client->edict;
2963 // call player physics, this needs the proper frametime
2964 PRVM_serverglobalfloat(frametime) = sv.frametime;
2967 // call standard client pre-think, with frametime = 0
2968 PRVM_serverglobalfloat(time) = sv.time;
2969 PRVM_serverglobalfloat(frametime) = 0;
2970 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2971 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2972 PRVM_serverglobalfloat(frametime) = sv.frametime;
2974 // make sure the velocity is sane (not a NaN)
2975 SV_CheckVelocity(ent);
2977 // perform MOVETYPE_WALK behavior
2980 // call standard player post-think, with frametime = 0
2981 PRVM_serverglobalfloat(time) = sv.time;
2982 PRVM_serverglobalfloat(frametime) = 0;
2983 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2984 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2985 PRVM_serverglobalfloat(frametime) = sv.frametime;
2987 if(PRVM_serveredictfloat(ent, fixangle))
2989 // angle fixing was requested by physics code...
2990 // so store the current angles for later use
2991 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2992 host_client->fixangle_angles_set = TRUE;
2994 // and clear fixangle for the next frame
2995 PRVM_serveredictfloat(ent, fixangle) = 0;
2999 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
3001 prvm_prog_t *prog = SVVM_prog;
3002 // don't do physics on disconnected clients, FrikBot relies on this
3003 if (!host_client->begun)
3006 // make sure the velocity is sane (not a NaN)
3007 SV_CheckVelocity(ent);
3009 // don't run physics here if running asynchronously
3010 if (host_client->clmovement_inputtimeout <= 0)
3013 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
3016 // make sure the velocity is still sane (not a NaN)
3017 SV_CheckVelocity(ent);
3019 // call standard client pre-think
3020 PRVM_serverglobalfloat(time) = sv.time;
3021 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3022 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3024 // make sure the velocity is still sane (not a NaN)
3025 SV_CheckVelocity(ent);
3028 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3030 prvm_prog_t *prog = SVVM_prog;
3031 // don't do physics on disconnected clients, FrikBot relies on this
3032 if (!host_client->begun)
3035 // make sure the velocity is sane (not a NaN)
3036 SV_CheckVelocity(ent);
3038 // call standard player post-think
3039 PRVM_serverglobalfloat(time) = sv.time;
3040 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3041 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3043 // make sure the velocity is still sane (not a NaN)
3044 SV_CheckVelocity(ent);
3046 if(PRVM_serveredictfloat(ent, fixangle))
3048 // angle fixing was requested by physics code...
3049 // so store the current angles for later use
3050 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3051 host_client->fixangle_angles_set = TRUE;
3053 // and clear fixangle for the next frame
3054 PRVM_serveredictfloat(ent, fixangle) = 0;
3057 // decrement the countdown variable used to decide when to go back to
3058 // synchronous physics
3059 if (host_client->clmovement_inputtimeout > sv.frametime)
3060 host_client->clmovement_inputtimeout -= sv.frametime;
3062 host_client->clmovement_inputtimeout = 0;
3065 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3067 prvm_prog_t *prog = SVVM_prog;
3068 // don't do physics on disconnected clients, FrikBot relies on this
3069 if (!host_client->begun)
3071 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3075 // make sure the velocity is sane (not a NaN)
3076 SV_CheckVelocity(ent);
3078 switch ((int) PRVM_serveredictfloat(ent, movetype))
3081 case MOVETYPE_FAKEPUSH:
3082 SV_Physics_Pusher (ent);
3085 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3086 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3089 case MOVETYPE_FOLLOW:
3090 SV_Physics_Follow (ent);
3092 case MOVETYPE_NOCLIP:
3095 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3096 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3099 SV_Physics_Step (ent);
3103 // don't run physics here if running asynchronously
3104 if (host_client->clmovement_inputtimeout <= 0)
3108 case MOVETYPE_BOUNCE:
3109 case MOVETYPE_BOUNCEMISSILE:
3110 case MOVETYPE_FLYMISSILE:
3113 SV_Physics_Toss (ent);
3116 case MOVETYPE_FLY_WORLDONLY:
3120 case MOVETYPE_PHYSICS:
3124 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3128 SV_CheckVelocity (ent);
3131 SV_LinkEdict_TouchAreaGrid(ent);
3133 SV_CheckVelocity (ent);
3142 void SV_Physics (void)
3144 prvm_prog_t *prog = SVVM_prog;
3148 // let the progs know that a new frame has started
3149 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3150 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3151 PRVM_serverglobalfloat(time) = sv.time;
3152 PRVM_serverglobalfloat(frametime) = sv.frametime;
3153 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3155 // run physics engine
3156 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3159 // treat each object in turn
3162 // if force_retouch, relink all the entities
3163 if (PRVM_serverglobalfloat(force_retouch) > 0)
3164 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3165 if (!ent->priv.server->free)
3166 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3168 if (sv_gameplayfix_consistentplayerprethink.integer)
3170 // run physics on the client entities in 3 stages
3171 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)
3173 SV_Physics_ClientEntity_PreThink(ent);
3175 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3176 if (!ent->priv.server->free)
3177 SV_Physics_ClientEntity(ent);
3179 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3180 if (!ent->priv.server->free)
3181 SV_Physics_ClientEntity_PostThink(ent);
3185 // run physics on the client entities
3186 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3188 if (!ent->priv.server->free)
3190 SV_Physics_ClientEntity_PreThink(ent);
3191 SV_Physics_ClientEntity(ent);
3192 SV_Physics_ClientEntity_PostThink(ent);
3197 // run physics on all the non-client entities
3198 if (!sv_freezenonclients.integer)
3200 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3201 if (!ent->priv.server->free)
3202 SV_Physics_Entity(ent);
3203 // make a second pass to see if any ents spawned this frame and make
3204 // sure they run their move/think
3205 if (sv_gameplayfix_delayprojectiles.integer < 0)
3206 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3207 if (!ent->priv.server->move && !ent->priv.server->free)
3208 SV_Physics_Entity(ent);
3211 if (PRVM_serverglobalfloat(force_retouch) > 0)
3212 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3214 // LordHavoc: endframe support
3215 if (PRVM_serverfunction(EndFrame))
3217 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3218 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3219 PRVM_serverglobalfloat(time) = sv.time;
3220 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3223 // decrement prog->num_edicts if the highest number entities died
3224 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3226 if (!sv_freezenonclients.integer)
3227 sv.time += sv.frametime;