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)
644 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
647 VectorCopy(trace.endpos, temp);
648 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
649 #if COLLISIONPARANOID < 3
650 if (trace.startsolid || endstuck)
652 Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "^3" : "", passedict ? (int)(passedict - prog->edicts) : -1, PRVM_serveredictvector(passedict, origin)[0], PRVM_serveredictvector(passedict, origin)[1], PRVM_serveredictvector(passedict, origin)[2], end[0] - PRVM_serveredictvector(passedict, origin)[0], end[1] - PRVM_serveredictvector(passedict, origin)[1], end[2] - PRVM_serveredictvector(passedict, origin)[2], trace.fraction, trace.endpos[0] - PRVM_serveredictvector(passedict, origin)[0], trace.endpos[1] - PRVM_serveredictvector(passedict, origin)[1], trace.endpos[2] - PRVM_serveredictvector(passedict, origin)[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
658 int SV_PointSuperContents(const vec3_t point)
660 prvm_prog_t *prog = SVVM_prog;
661 int supercontents = 0;
665 // matrices to transform into/out of other entity's space
666 matrix4x4_t matrix, imatrix;
667 // model of other entity
670 // list of entities to test for collisions
672 static prvm_edict_t *touchedicts[MAX_EDICTS];
674 // get world supercontents at this point
675 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
676 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
678 // if sv_gameplayfix_swiminbmodels is off we're done
679 if (!sv_gameplayfix_swiminbmodels.integer)
680 return supercontents;
682 // get list of entities at this point
683 numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
684 if (numtouchedicts > MAX_EDICTS)
686 // this never happens
687 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
688 numtouchedicts = MAX_EDICTS;
690 for (i = 0;i < numtouchedicts;i++)
692 touch = touchedicts[i];
694 // we only care about SOLID_BSP for pointcontents
695 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
698 // might interact, so do an exact clip
699 model = SV_GetModelFromEdict(touch);
700 if (!model || !model->PointSuperContents)
702 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
703 Matrix4x4_Invert_Simple(&imatrix, &matrix);
704 Matrix4x4_Transform(&imatrix, point, transformed);
705 frame = (int)PRVM_serveredictfloat(touch, frame);
706 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
709 return supercontents;
713 ===============================================================================
715 Linking entities into the world culling system
717 ===============================================================================
720 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
722 prvm_prog_t *prog = SVVM_prog;
723 vec3_t paddedmins, paddedmaxs;
724 if (maxedicts < 1 || resultedicts == NULL)
726 // LordHavoc: discovered this actually causes its own bugs (dm6 teleporters being too close to info_teleport_destination)
727 //VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
728 //VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
729 VectorCopy(mins, paddedmins);
730 VectorCopy(maxs, paddedmaxs);
731 if (sv_areadebug.integer)
733 int numresultedicts = 0;
736 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
738 ed = PRVM_EDICT_NUM(edictindex);
739 if (!ed->priv.required->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
741 resultedicts[numresultedicts++] = ed;
742 if (numresultedicts == maxedicts)
746 return numresultedicts;
749 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
752 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
754 prvm_prog_t *prog = SVVM_prog;
755 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
756 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
757 PRVM_serverglobalfloat(time) = sv.time;
758 PRVM_serverglobalfloat(trace_allsolid) = false;
759 PRVM_serverglobalfloat(trace_startsolid) = false;
760 PRVM_serverglobalfloat(trace_fraction) = 1;
761 PRVM_serverglobalfloat(trace_inwater) = false;
762 PRVM_serverglobalfloat(trace_inopen) = true;
763 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
764 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
765 PRVM_serverglobalfloat(trace_plane_dist) = 0;
766 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
767 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
768 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
769 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
770 PRVM_serverglobalstring(trace_dphittexturename) = 0;
771 prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
774 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
776 prvm_prog_t *prog = SVVM_prog;
777 int i, numtouchedicts, old_self, old_other;
779 static prvm_edict_t *touchedicts[MAX_EDICTS];
781 if (ent == prog->edicts)
782 return; // don't add the world
784 if (ent->priv.server->free)
787 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
790 // build a list of edicts to touch, because the link loop can be corrupted
791 // by IncreaseEdicts called during touch functions
792 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
793 if (numtouchedicts > MAX_EDICTS)
795 // this never happens
796 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
797 numtouchedicts = MAX_EDICTS;
800 old_self = PRVM_serverglobaledict(self);
801 old_other = PRVM_serverglobaledict(other);
802 for (i = 0;i < numtouchedicts;i++)
804 touch = touchedicts[i];
805 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
807 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
810 PRVM_serverglobaledict(self) = old_self;
811 PRVM_serverglobaledict(other) = old_other;
814 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
818 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
820 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
821 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
822 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
823 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
824 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
825 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
826 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
827 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
828 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
829 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
830 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
831 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
832 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
833 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
834 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
835 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
836 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
837 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
838 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
839 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
840 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
841 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
842 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
851 void SV_LinkEdict (prvm_edict_t *ent)
853 prvm_prog_t *prog = SVVM_prog;
855 vec3_t mins, maxs, entmins, entmaxs, entangles;
858 if (ent == prog->edicts)
859 return; // don't add the world
861 if (ent->priv.server->free)
864 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
865 if (modelindex < 0 || modelindex >= MAX_MODELS)
867 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
870 model = SV_GetModelByIndex(modelindex);
872 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
873 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
874 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
878 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
880 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
881 // TODO special handling for spheres?
882 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
883 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
884 VectorCopy(PRVM_serveredictvector(ent, angles), entangles);
885 RotateBBox(entmins, entmaxs, entangles, mins, maxs);
886 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
887 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
889 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
893 if (!model->TraceBox)
894 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
896 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
898 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
899 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
901 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
903 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
904 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
908 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
909 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
914 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
915 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
916 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
921 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
922 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
926 // to make items easier to pick up and allow them to be grabbed off
927 // of shelves, the abs sizes are expanded
929 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
940 // because movement is clipped an epsilon away from an actual edge,
941 // we must fully check even when bounding boxes don't quite touch
950 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
951 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
953 World_LinkEdict(&sv.world, ent, mins, maxs);
957 ===============================================================================
961 ===============================================================================
966 SV_TestEntityPosition
968 returns true if the entity is in solid currently
971 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
973 prvm_prog_t *prog = SVVM_prog;
975 vec3_t org, entorigin, entmins, entmaxs;
977 contents = SV_GenericHitSuperContentsMask(ent);
978 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
979 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
980 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
981 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
982 trace = SV_TraceBox(org, entmins, entmaxs, entorigin, ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, contents);
983 if (trace.startsupercontents & contents)
987 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
989 // q1bsp/hlbsp use hulls and if the entity does not exactly match
990 // a hull size it is incorrectly tested, so this code tries to
991 // 'fix' it slightly...
992 // FIXME: this breaks entities larger than the hull size
995 VectorAdd(org, entmins, m1);
996 VectorAdd(org, entmaxs, m2);
997 VectorSubtract(m2, m1, s);
998 #define EPSILON (1.0f / 32.0f)
999 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
1000 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
1001 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
1002 for (i = 0;i < 8;i++)
1004 v[0] = (i & 1) ? m2[0] : m1[0];
1005 v[1] = (i & 2) ? m2[1] : m1[1];
1006 v[2] = (i & 4) ? m2[2] : m1[2];
1007 if (SV_PointSuperContents(v) & contents)
1012 // if the trace found a better position for the entity, move it there
1013 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
1016 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
1017 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
1019 // verify if the endpos is REALLY outside solid
1020 VectorCopy(trace.endpos, org);
1021 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, contents);
1022 if(trace.startsolid)
1023 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
1025 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1031 // DRESK - Support for Entity Contents Transition Event
1034 SV_CheckContentsTransition
1036 returns true if entity had a valid contentstransition function call
1039 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
1041 prvm_prog_t *prog = SVVM_prog;
1042 int bValidFunctionCall;
1044 // Default Valid Function Call to False
1045 bValidFunctionCall = false;
1047 if(PRVM_serveredictfloat(ent, watertype) != nContents)
1048 { // Changed Contents
1049 // Acquire Contents Transition Function from QC
1050 if(PRVM_serveredictfunction(ent, contentstransition))
1051 { // Valid Function; Execute
1052 // Assign Valid Function
1053 bValidFunctionCall = true;
1054 // Prepare Parameters (Original Contents, New Contents)
1055 // Original Contents
1056 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
1058 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1060 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1062 PRVM_serverglobalfloat(time) = sv.time;
1063 // Execute VM Function
1064 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1068 // Return if Function Call was Valid
1069 return bValidFunctionCall;
1078 void SV_CheckVelocity (prvm_edict_t *ent)
1080 prvm_prog_t *prog = SVVM_prog;
1087 for (i=0 ; i<3 ; i++)
1089 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1091 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1092 PRVM_serveredictvector(ent, velocity)[i] = 0;
1094 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1096 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1097 PRVM_serveredictvector(ent, origin)[i] = 0;
1101 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1102 // player_run/player_stand1 does not horribly malfunction if the
1103 // velocity becomes a denormalized float
1104 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0001)
1105 VectorClear(PRVM_serveredictvector(ent, velocity));
1107 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1108 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1109 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1111 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1112 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1113 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1114 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1122 Runs thinking code if time. There is some play in the exact time the think
1123 function will be called, because it is called before any movement is done
1124 in a frame. Not used for pushmove objects, because they must be exact.
1125 Returns false if the entity removed itself.
1128 static qboolean SV_RunThink (prvm_edict_t *ent)
1130 prvm_prog_t *prog = SVVM_prog;
1133 // don't let things stay in the past.
1134 // it is possible to start that way by a trigger with a local time.
1135 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1138 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1140 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1141 PRVM_serveredictfloat(ent, nextthink) = 0;
1142 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1143 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1144 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1145 // mods often set nextthink to time to cause a think every frame,
1146 // we don't want to loop in that case, so exit if the new nextthink is
1147 // <= the time the qc was told, also exit if it is past the end of the
1149 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1152 return !ent->priv.server->free;
1159 Two entities have touched, so run their touch functions
1162 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1164 prvm_prog_t *prog = SVVM_prog;
1165 int restorevm_tempstringsbuf_cursize;
1166 int old_self, old_other;
1167 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1169 old_self = PRVM_serverglobaledict(self);
1170 old_other = PRVM_serverglobaledict(other);
1171 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1173 VM_SetTraceGlobals(prog, trace);
1175 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1177 PRVM_serverglobalfloat(time) = sv.time;
1178 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1179 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1180 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1183 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1185 PRVM_serverglobalfloat(time) = sv.time;
1186 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1187 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1188 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1189 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1190 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1191 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1192 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1193 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1194 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1195 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1196 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1199 PRVM_serverglobaledict(self) = old_self;
1200 PRVM_serverglobaledict(other) = old_other;
1201 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1209 Slide off of the impacting object
1210 returns the blocked flags (1 = floor, 2 = step / wall)
1213 #define STOP_EPSILON 0.1
1214 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1219 backoff = -DotProduct (in, normal) * overbounce;
1220 VectorMA(in, backoff, normal, out);
1222 for (i = 0;i < 3;i++)
1223 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1232 The basic solid body movement clip that slides along multiple planes
1233 Returns the clipflags if the velocity was modified (hit something solid)
1237 8 = teleported by touch method
1238 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1241 static float SV_Gravity (prvm_edict_t *ent);
1242 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink);
1243 #define MAX_CLIP_PLANES 5
1244 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1246 prvm_prog_t *prog = SVVM_prog;
1247 int blocked, bumpcount;
1248 int i, j, numplanes;
1249 float d, time_left, gravity;
1250 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1251 prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
1260 VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1264 gravity = SV_Gravity(ent);
1266 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1268 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1269 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1271 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1276 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1277 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1280 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1282 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1285 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1286 if(!SV_PushEntity(&trace, ent, push, false))
1288 // we got teleported by a touch function
1289 // let's abort the move
1294 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
1295 // abort move if we're stuck in the world (and didn't make it out)
1296 if (trace.worldstartsolid && trace.allsolid)
1298 VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
1302 if (trace.fraction == 1)
1304 if (trace.plane.normal[2])
1306 if (trace.plane.normal[2] > 0.7)
1313 Con_Printf ("SV_FlyMove: !trace.ent");
1314 trace.ent = prog->edicts;
1317 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1318 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1321 else if (stepheight)
1323 // step - handle it immediately
1329 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1330 VectorSet(steppush, 0, 0, stepheight);
1331 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1332 if(!SV_PushEntity(&steptrace, ent, steppush, false))
1337 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1338 if(!SV_PushEntity(&steptrace2, ent, push, false))
1343 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1344 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1345 if(!SV_PushEntity(&steptrace3, ent, steppush, false))
1350 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1351 // accept the new position if it made some progress...
1352 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1354 //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]);
1356 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1357 time_left *= 1 - trace.fraction;
1363 //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]);
1364 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1369 // step - return it to caller
1371 // save the trace for player extrafriction
1373 VectorCopy(trace.plane.normal, stepnormal);
1375 if (trace.fraction >= 0.001)
1377 // actually covered some distance
1378 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1382 time_left *= 1 - trace.fraction;
1384 // clipped to another plane
1385 if (numplanes >= MAX_CLIP_PLANES)
1387 // this shouldn't really happen
1388 VectorClear(PRVM_serveredictvector(ent, velocity));
1394 for (i = 0;i < numplanes;i++)
1395 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1399 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1404 VectorCopy(trace.plane.normal, planes[numplanes]);
1407 // modify original_velocity so it parallels all of the clip planes
1408 for (i = 0;i < numplanes;i++)
1410 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1411 for (j = 0;j < numplanes;j++)
1416 if (DotProduct(new_velocity, planes[j]) < 0)
1426 // go along this plane
1427 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1431 // go along the crease
1434 VectorClear(PRVM_serveredictvector(ent, velocity));
1438 CrossProduct(planes[0], planes[1], dir);
1439 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1440 VectorNormalize(dir);
1441 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1442 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1445 // if current velocity is against the original velocity,
1446 // stop dead to avoid tiny occilations in sloping corners
1447 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1449 VectorClear(PRVM_serveredictvector(ent, velocity));
1454 //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]);
1457 if ((blocked & 1) == 0 && bumpcount > 1)
1459 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1460 // flag ONGROUND if there's ground under it
1461 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask);
1465 // LordHavoc: this came from QW and allows you to get out of water more easily
1466 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1467 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1471 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1473 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1474 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1487 static float SV_Gravity (prvm_edict_t *ent)
1489 prvm_prog_t *prog = SVVM_prog;
1492 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1495 return ent_gravity * sv_gravity.value * sv.frametime;
1500 ===============================================================================
1504 ===============================================================================
1507 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1509 prvm_prog_t *prog = SVVM_prog;
1513 vec3_t stuckmins, stuckmaxs;
1514 vec3_t goodmins, goodmaxs;
1518 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1519 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1520 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1521 VectorCopy(pivot, goodmins);
1522 VectorCopy(pivot, goodmaxs);
1523 for (bump = 0;bump < 6;bump++)
1525 int coord = 2-(bump >> 1);
1526 //int coord = (bump >> 1);
1527 int dir = (bump & 1);
1530 for(subbump = 0; ; ++subbump)
1532 VectorCopy(stuckorigin, testorigin);
1536 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1541 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1544 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1545 if (stucktrace.bmodelstartsolid)
1547 // BAD BAD, can't fix that
1551 if (stucktrace.fraction >= 1)
1556 // BAD BAD, can't fix that
1560 // we hit something... let's move out of it
1561 VectorSubtract(stucktrace.endpos, testorigin, move);
1562 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1563 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1567 Con_Printf("subbump: %d\n", subbump);
1573 goodmaxs[coord] = stuckmaxs[coord];
1578 goodmins[coord] = stuckmins[coord];
1583 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1588 qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1590 prvm_prog_t *prog = SVVM_prog;
1594 vec3_t stuckmins, stuckmaxs;
1596 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1597 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1598 separation = 0.0f; // when using hulls, it can not be enlarged
1599 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1600 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1601 stuckmins[0] -= separation;
1602 stuckmins[1] -= separation;
1603 stuckmins[2] -= separation;
1604 stuckmaxs[0] += separation;
1605 stuckmaxs[1] += separation;
1606 stuckmaxs[2] += separation;
1607 // first pass we try to get it out of brush entities
1608 // second pass we try to get it out of world only (can't win them all)
1609 for (pass = 0;pass < 2;pass++)
1611 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1612 for (bump = 0;bump < 10;bump++)
1614 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1615 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1617 // found a good location, use it
1618 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1621 nudge = -stucktrace.startdepth;
1622 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1632 Does not change the entities velocity at all
1633 The trace struct is filled with the trace that has been done.
1634 Returns true if the push did not result in the entity being teleported by QC code.
1637 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink)
1639 prvm_prog_t *prog = SVVM_prog;
1647 solid = (int)PRVM_serveredictfloat(ent, solid);
1648 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1649 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1650 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1652 // move start position out of solids
1653 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1655 SV_NudgeOutOfSolid(ent);
1658 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1659 VectorAdd(start, push, end);
1661 if (movetype == MOVETYPE_FLYMISSILE)
1662 type = MOVE_MISSILE;
1663 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1664 type = MOVE_WORLDONLY;
1665 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1666 type = MOVE_NOMONSTERS; // only clip against bmodels
1670 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1671 // fail the move if stuck in world
1672 if (trace->worldstartsolid)
1675 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1677 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1682 if(!trace->startsolid)
1683 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)
1685 Con_Printf("something eeeeevil happened\n");
1690 SV_LinkEdict_TouchAreaGrid(ent);
1692 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))))
1693 SV_Impact (ent, trace);
1695 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1697 ent->priv.required->mark = 0;
1700 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1702 ent->priv.required->mark = 0;
1707 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1719 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1721 prvm_prog_t *prog = SVVM_prog;
1723 int pusherowner, pusherprog;
1726 float savesolid, movetime2, pushltime;
1727 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1729 int numcheckentities;
1730 static prvm_edict_t *checkentities[MAX_EDICTS];
1731 dp_model_t *pushermodel;
1732 trace_t trace, trace2;
1733 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1734 static unsigned short moved_edicts[MAX_EDICTS];
1737 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])
1739 PRVM_serveredictfloat(pusher, ltime) += movetime;
1743 switch ((int) PRVM_serveredictfloat(pusher, solid))
1745 // LordHavoc: valid pusher types
1748 case SOLID_SLIDEBOX:
1749 case SOLID_CORPSE: // LordHavoc: this would be weird...
1751 // LordHavoc: no collisions
1754 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1755 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1756 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1757 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1758 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1759 PRVM_serveredictfloat(pusher, ltime) += movetime;
1760 SV_LinkEdict(pusher);
1763 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1766 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1767 if (index < 1 || index >= MAX_MODELS)
1769 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1772 pushermodel = SV_GetModelByIndex(index);
1773 pusherowner = PRVM_serveredictedict(pusher, owner);
1774 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1776 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1778 movetime2 = movetime;
1779 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1780 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1781 if (moveangle[0] || moveangle[2])
1783 for (i = 0;i < 3;i++)
1787 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1788 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1792 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1793 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1797 else if (moveangle[1])
1799 for (i = 0;i < 3;i++)
1803 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1804 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1808 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1809 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1815 for (i = 0;i < 3;i++)
1819 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1820 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1824 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1825 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1830 VectorNegate (moveangle, a);
1831 AngleVectorsFLU (a, forward, left, up);
1833 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1834 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1835 pushltime = PRVM_serveredictfloat(pusher, ltime);
1837 // move the pusher to its final position
1839 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1840 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1841 PRVM_serveredictfloat(pusher, ltime) += movetime;
1842 SV_LinkEdict(pusher);
1844 pushermodel = SV_GetModelFromEdict(pusher);
1845 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);
1846 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1848 savesolid = PRVM_serveredictfloat(pusher, solid);
1850 // see if any solid entities are inside the final position
1853 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1854 numcheckentities = 0;
1855 else // MOVETYPE_PUSH
1856 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1857 for (e = 0;e < numcheckentities;e++)
1859 prvm_edict_t *check = checkentities[e];
1860 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1865 case MOVETYPE_FOLLOW:
1866 case MOVETYPE_NOCLIP:
1867 case MOVETYPE_FLY_WORLDONLY:
1873 if (PRVM_serveredictedict(check, owner) == pusherprog)
1876 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1879 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1881 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1882 check->priv.server->waterposition_forceupdate = true;
1884 checkcontents = SV_GenericHitSuperContentsMask(check);
1886 // if the entity is standing on the pusher, it will definitely be moved
1887 // if the entity is not standing on the pusher, but is in the pusher's
1888 // final position, move it
1889 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1891 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1892 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1893 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1894 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1895 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1896 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents);
1897 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1898 if (!trace.startsolid)
1900 //Con_Printf("- not in solid\n");
1905 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1906 //VectorClear(pivot);
1911 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1912 VectorAdd (org, pivot, org);
1913 org2[0] = DotProduct (org, forward);
1914 org2[1] = DotProduct (org, left);
1915 org2[2] = DotProduct (org, up);
1916 VectorSubtract (org2, org, move);
1917 VectorAdd (move, move1, move);
1920 VectorCopy (move1, move);
1922 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1924 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1925 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1926 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1928 // physics objects need better collisions than this code can do
1929 if (movetype == MOVETYPE_PHYSICS)
1931 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1932 SV_LinkEdict(check);
1933 SV_LinkEdict_TouchAreaGrid(check);
1937 // try moving the contacted entity
1938 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1939 if(!SV_PushEntity (&trace, check, move, true))
1941 // entity "check" got teleported
1942 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1943 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1944 continue; // pushed enough
1946 // FIXME: turn players specially
1947 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1948 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1949 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1951 // this trace.fraction < 1 check causes items to fall off of pushers
1952 // if they pass under or through a wall
1953 // the groundentity check causes items to fall off of ledges
1954 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1955 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1957 // if it is still inside the pusher, block
1958 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1959 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1960 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1961 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1962 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1963 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents);
1964 if (trace.startsolid)
1967 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1969 // hack to invoke all necessary movement triggers
1971 if(!SV_PushEntity(&trace2, check, move2, true))
1973 // entity "check" got teleported
1980 // still inside pusher, so it's really blocked
1983 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1985 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1988 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1989 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1993 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1994 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1995 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1996 SV_LinkEdict(pusher);
1998 // move back any entities we already moved
1999 for (i = 0;i < num_moved;i++)
2001 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
2002 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
2003 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
2007 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
2008 if (PRVM_serveredictfunction(pusher, blocked))
2010 PRVM_serverglobalfloat(time) = sv.time;
2011 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
2012 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
2013 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
2018 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
2019 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
2020 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
2029 static void SV_Physics_Pusher (prvm_edict_t *ent)
2031 prvm_prog_t *prog = SVVM_prog;
2032 float thinktime, oldltime, movetime;
2034 oldltime = PRVM_serveredictfloat(ent, ltime);
2036 thinktime = PRVM_serveredictfloat(ent, nextthink);
2037 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
2039 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
2044 movetime = sv.frametime;
2047 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
2048 SV_PushMove (ent, movetime);
2050 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
2052 PRVM_serveredictfloat(ent, nextthink) = 0;
2053 PRVM_serverglobalfloat(time) = sv.time;
2054 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2055 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
2056 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2062 ===============================================================================
2066 ===============================================================================
2069 static float unstickoffsets[] =
2071 // poutting -/+z changes first as they are least weird
2086 typedef enum unstickresult_e
2094 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2096 prvm_prog_t *prog = SVVM_prog;
2099 // if not stuck in a bmodel, just return
2100 if (!SV_TestEntityPosition(ent, vec3_origin))
2101 return UNSTICK_GOOD;
2103 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2105 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2107 VectorCopy(unstickoffsets + i, offset);
2109 //SV_LinkEdict_TouchAreaGrid(ent);
2110 return UNSTICK_UNSTUCK;
2114 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2115 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2117 for(i = 2; i <= maxunstick; ++i)
2119 VectorClear(offset);
2121 if (!SV_TestEntityPosition(ent, offset))
2124 //SV_LinkEdict_TouchAreaGrid(ent);
2125 return UNSTICK_UNSTUCK;
2128 if (!SV_TestEntityPosition(ent, offset))
2131 //SV_LinkEdict_TouchAreaGrid(ent);
2132 return UNSTICK_UNSTUCK;
2136 return UNSTICK_STUCK;
2139 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2141 prvm_prog_t *prog = SVVM_prog;
2143 switch(SV_UnstickEntityReturnOffset(ent, offset))
2147 case UNSTICK_UNSTUCK:
2148 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]);
2151 if (developer_extra.integer)
2152 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2155 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2164 This is a big hack to try and fix the rare case of getting stuck in the world
2168 static void SV_CheckStuck (prvm_edict_t *ent)
2170 prvm_prog_t *prog = SVVM_prog;
2173 switch(SV_UnstickEntityReturnOffset(ent, offset))
2176 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2178 case UNSTICK_UNSTUCK:
2179 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]);
2182 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2183 if (!SV_TestEntityPosition(ent, offset))
2185 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)));
2187 //SV_LinkEdict_TouchAreaGrid(ent);
2190 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2193 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2203 static qboolean SV_CheckWater (prvm_edict_t *ent)
2205 prvm_prog_t *prog = SVVM_prog;
2207 int nNativeContents;
2210 point[0] = PRVM_serveredictvector(ent, origin)[0];
2211 point[1] = PRVM_serveredictvector(ent, origin)[1];
2212 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2214 // DRESK - Support for Entity Contents Transition Event
2215 // NOTE: Some logic needed to be slightly re-ordered
2216 // to not affect performance and allow for the feature.
2218 // Acquire Super Contents Prior to Resets
2219 cont = SV_PointSuperContents(point);
2220 // Acquire Native Contents Here
2221 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2223 // DRESK - Support for Entity Contents Transition Event
2224 if(PRVM_serveredictfloat(ent, watertype))
2225 // Entity did NOT Spawn; Check
2226 SV_CheckContentsTransition(ent, nNativeContents);
2229 PRVM_serveredictfloat(ent, waterlevel) = 0;
2230 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2231 cont = SV_PointSuperContents(point);
2232 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2234 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2235 PRVM_serveredictfloat(ent, waterlevel) = 1;
2236 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2237 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2239 PRVM_serveredictfloat(ent, waterlevel) = 2;
2240 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2241 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2242 PRVM_serveredictfloat(ent, waterlevel) = 3;
2246 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2255 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2257 prvm_prog_t *prog = SVVM_prog;
2259 vec3_t forward, into, side, v_angle;
2261 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2262 AngleVectors (v_angle, forward, NULL, NULL);
2263 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2265 // cut the tangential velocity
2266 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2267 VectorScale (stepnormal, i, into);
2268 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2269 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2270 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2276 =====================
2279 Player has come to a dead stop, possibly due to the problem with limited
2280 float precision at some angle joins in the BSP hull.
2282 Try fixing by pushing one pixel in each direction.
2284 This is a hack, but in the interest of good gameplay...
2285 ======================
2287 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2292 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2295 for (i=0 ; i<8 ; i++)
2297 // try pushing a little in an axial direction
2300 case 0: dir[0] = 2; dir[1] = 0; break;
2301 case 1: dir[0] = 0; dir[1] = 2; break;
2302 case 2: dir[0] = -2; dir[1] = 0; break;
2303 case 3: dir[0] = 0; dir[1] = -2; break;
2304 case 4: dir[0] = 2; dir[1] = 2; break;
2305 case 5: dir[0] = -2; dir[1] = 2; break;
2306 case 6: dir[0] = 2; dir[1] = -2; break;
2307 case 7: dir[0] = -2; dir[1] = -2; break;
2310 SV_PushEntity (&trace, ent, dir, false, true);
2312 // retry the original move
2313 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2314 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2315 PRVM_serveredictvector(ent, velocity)[2] = 0;
2316 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2318 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2319 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2321 Con_DPrint("TryUnstick - success.\n");
2325 // go back to the original pos and try again
2326 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2330 VectorClear (PRVM_serveredictvector(ent, velocity));
2331 Con_DPrint("TryUnstick - failure.\n");
2337 =====================
2340 Only used by players
2341 ======================
2343 static void SV_WalkMove (prvm_edict_t *ent)
2345 prvm_prog_t *prog = SVVM_prog;
2348 //int originalmove_clip;
2349 int originalmove_flags;
2350 int originalmove_groundentity;
2351 int hitsupercontentsmask;
2353 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2354 trace_t downtrace, trace;
2355 qboolean applygravity;
2357 // if frametime is 0 (due to client sending the same timestamp twice),
2359 if (sv.frametime <= 0)
2362 if (sv_gameplayfix_unstickplayers.integer)
2363 SV_CheckStuck (ent);
2365 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2367 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2369 SV_CheckVelocity(ent);
2371 // do a regular slide move unless it looks like you ran into a step
2372 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2374 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2375 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2377 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2379 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2382 // only try this if there was no floor in the way in the trace (no,
2383 // this check seems to be not REALLY necessary, because if clip & 1,
2384 // our trace will hit that thing too)
2385 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2386 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2387 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2388 type = MOVE_MISSILE;
2389 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2390 type = MOVE_WORLDONLY;
2391 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2392 type = MOVE_NOMONSTERS; // only clip against bmodels
2395 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2396 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2397 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2398 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2399 clip |= 1; // but we HAVE found a floor
2402 // if the move did not hit the ground at any point, we're not on ground
2404 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2406 SV_CheckVelocity(ent);
2408 SV_LinkEdict_TouchAreaGrid(ent);
2410 if(clip & 8) // teleport
2413 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2416 if (sv_nostep.integer)
2419 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2420 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2421 //originalmove_clip = clip;
2422 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2423 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2425 // if move didn't block on a step, return
2428 // if move was not trying to move into the step, return
2429 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2432 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2434 // return if gibbed by a trigger
2435 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2438 // return if attempting to jump while airborn (unless sv_jumpstep)
2439 if (!sv_jumpstep.integer)
2440 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2444 // try moving up and forward to go up a step
2445 // back to start pos
2446 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2447 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2450 VectorClear (upmove);
2451 upmove[2] = sv_stepheight.value;
2452 if(!SV_PushEntity(&trace, ent, upmove, true))
2454 // we got teleported when upstepping... must abort the move
2459 PRVM_serveredictvector(ent, velocity)[2] = 0;
2460 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2461 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2464 // we got teleported when upstepping... must abort the move
2465 // note that z velocity handling may not be what QC expects here, but we cannot help it
2469 SV_CheckVelocity(ent);
2471 SV_LinkEdict_TouchAreaGrid(ent);
2473 // check for stuckness, possibly due to the limited precision of floats
2474 // in the clipping hulls
2476 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2477 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2479 //Con_Printf("wall\n");
2480 // stepping up didn't make any progress, revert to original move
2481 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2482 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2483 //clip = originalmove_clip;
2484 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2485 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2486 // now try to unstick if needed
2487 //clip = SV_TryUnstick (ent, oldvel);
2491 //Con_Printf("step - ");
2493 // extra friction based on view angle
2494 if (clip & 2 && sv_wallfriction.integer)
2495 SV_WallFriction (ent, stepnormal);
2497 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2498 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))
2502 VectorClear (downmove);
2503 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2504 if(!SV_PushEntity (&downtrace, ent, downmove, true))
2506 // we got teleported when downstepping... must abort the move
2510 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2512 // this has been disabled so that you can't jump when you are stepping
2513 // up while already jumping (also known as the Quake2 double jump bug)
2515 // LordHavoc: disabled this check so you can walk on monsters/players
2516 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2518 //Con_Printf("onground\n");
2519 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2520 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2526 //Con_Printf("slope\n");
2527 // if the push down didn't end up on good ground, use the move without
2528 // the step up. This happens near wall / slope combinations, and can
2529 // cause the player to hop up higher on a slope too steep to climb
2530 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2531 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2532 //clip = originalmove_clip;
2533 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2534 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2537 SV_CheckVelocity(ent);
2539 SV_LinkEdict_TouchAreaGrid(ent);
2542 //============================================================================
2548 Entities that are "stuck" to another entity
2551 static void SV_Physics_Follow (prvm_edict_t *ent)
2553 prvm_prog_t *prog = SVVM_prog;
2554 vec3_t vf, vr, vu, angles, v;
2558 if (!SV_RunThink (ent))
2561 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2562 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2563 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])
2565 // quick case for no rotation
2566 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2570 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2571 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2572 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2573 AngleVectors (angles, vf, vr, vu);
2574 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];
2575 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];
2576 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];
2577 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2578 angles[1] = PRVM_serveredictvector(e, angles)[1];
2579 angles[2] = PRVM_serveredictvector(e, angles)[2];
2580 AngleVectors (angles, vf, vr, vu);
2581 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2582 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2583 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2585 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2587 //SV_LinkEdict_TouchAreaGrid(ent);
2591 ==============================================================================
2595 ==============================================================================
2600 SV_CheckWaterTransition
2604 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2607 prvm_prog_t *prog = SVVM_prog;
2608 // 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
2610 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2611 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(entorigin));
2612 if (!PRVM_serveredictfloat(ent, watertype))
2614 // just spawned here
2615 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2617 PRVM_serveredictfloat(ent, watertype) = cont;
2618 PRVM_serveredictfloat(ent, waterlevel) = 1;
2622 // DRESK - Support for Entity Contents Transition Event
2623 // NOTE: Call here BEFORE updating the watertype below,
2624 // and suppress watersplash sound if a valid function
2625 // call was made to allow for custom "splash" sounds.
2626 else if( !SV_CheckContentsTransition(ent, cont) )
2627 { // Contents Transition Function Invalid; Potentially Play Water Sound
2628 // check if the entity crossed into or out of water
2629 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2630 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2633 if (cont <= CONTENTS_WATER)
2635 PRVM_serveredictfloat(ent, watertype) = cont;
2636 PRVM_serveredictfloat(ent, waterlevel) = 1;
2640 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2641 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2649 Toss, bounce, and fly movement. When onground, do nothing.
2653 void SV_Physics_Toss (prvm_edict_t *ent)
2655 prvm_prog_t *prog = SVVM_prog;
2660 prvm_edict_t *groundentity;
2661 float d, ent_gravity;
2665 // if onground, return without moving
2666 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2668 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2669 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2671 // don't stick to ground if onground and moving upward
2672 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2674 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2676 // we can trust FL_ONGROUND if groundentity is world because it never moves
2679 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2681 // if ent was supported by a brush model on previous frame,
2682 // and groundentity is now freed, set groundentity to 0 (world)
2683 // which leaves it suspended in the air
2684 PRVM_serveredictedict(ent, groundentity) = 0;
2685 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2688 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2690 // don't slide if still touching the groundentity
2694 ent->priv.server->suspendedinairflag = false;
2696 SV_CheckVelocity (ent);
2699 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2700 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2703 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2705 movetime = sv.frametime;
2706 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2709 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2710 if(!SV_PushEntity(&trace, ent, move, true))
2711 return; // teleported
2712 if (ent->priv.server->free)
2714 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2716 // try to unstick the entity
2717 SV_UnstickEntity(ent);
2718 if(!SV_PushEntity(&trace, ent, move, true))
2719 return; // teleported
2720 if (ent->priv.server->free)
2723 if (trace.fraction == 1)
2725 movetime *= 1 - min(1, trace.fraction);
2726 switch((int)PRVM_serveredictfloat(ent, movetype))
2728 case MOVETYPE_BOUNCEMISSILE:
2729 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2731 bouncefactor = 1.0f;
2733 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2734 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2735 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2738 case MOVETYPE_BOUNCE:
2739 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2741 bouncefactor = 0.5f;
2743 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2745 bouncestop = 60.0f / 800.0f;
2747 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2748 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2751 // LordHavoc: fixed grenades not bouncing when fired down a slope
2752 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2753 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2755 d = PRVM_serveredictvector(ent, velocity)[2];
2756 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2758 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2759 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2760 VectorClear(PRVM_serveredictvector(ent, velocity));
2761 VectorClear(PRVM_serveredictvector(ent, avelocity));
2766 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2767 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2772 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2773 if (trace.plane.normal[2] > 0.7)
2775 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2776 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2777 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2778 ent->priv.server->suspendedinairflag = true;
2779 VectorClear (PRVM_serveredictvector(ent, velocity));
2780 VectorClear (PRVM_serveredictvector(ent, avelocity));
2783 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2789 // check for in water
2790 SV_CheckWaterTransition (ent);
2794 ===============================================================================
2798 ===============================================================================
2805 Monsters freefall when they don't have a ground entity, otherwise
2806 all movement is done with discrete steps.
2808 This is also used for objects that have become still on the ground, but
2809 will fall if the floor is pulled out from under them.
2812 static void SV_Physics_Step (prvm_edict_t *ent)
2814 prvm_prog_t *prog = SVVM_prog;
2815 int flags = (int)PRVM_serveredictfloat(ent, flags);
2818 // Backup Velocity in the event that movetypesteplandevent is called,
2819 // to provide a parameter with the entity's velocity at impact.
2820 vec3_t backupVelocity;
2821 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2822 // don't fall at all if fly/swim
2823 if (!(flags & (FL_FLY | FL_SWIM)))
2825 if (flags & FL_ONGROUND)
2827 // freefall if onground and moving upward
2828 // freefall if not standing on a world surface (it may be a lift or trap door)
2829 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2831 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2832 SV_CheckVelocity(ent);
2833 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2835 SV_LinkEdict_TouchAreaGrid(ent);
2836 ent->priv.server->waterposition_forceupdate = true;
2841 // freefall if not onground
2842 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2844 SV_CheckVelocity(ent);
2845 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2847 SV_LinkEdict_TouchAreaGrid(ent);
2850 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2852 // DRESK - Check for Entity Land Event Function
2853 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2854 { // Valid Function; Execute
2855 // Prepare Parameters
2856 // Assign Velocity at Impact
2857 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2858 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2859 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2861 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2863 PRVM_serverglobalfloat(time) = sv.time;
2864 // Execute VM Function
2865 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2868 // Check for Engine Landing Sound
2869 if(sv_sound_land.string)
2870 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2872 ent->priv.server->waterposition_forceupdate = true;
2877 if (!SV_RunThink(ent))
2880 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2882 ent->priv.server->waterposition_forceupdate = false;
2883 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2884 SV_CheckWaterTransition(ent);
2888 //============================================================================
2890 static void SV_Physics_Entity (prvm_edict_t *ent)
2892 prvm_prog_t *prog = SVVM_prog;
2893 // don't run think/move on newly spawned projectiles as it messes up
2894 // movement interpolation and rocket trails, and is inconsistent with
2895 // respect to entities spawned in the same frame
2896 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2897 // but if it spawns a lower numbered ent, it doesn't - this never moves
2898 // ents in the first frame regardless)
2899 qboolean runmove = ent->priv.server->move;
2900 ent->priv.server->move = true;
2901 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2903 switch ((int) PRVM_serveredictfloat(ent, movetype))
2906 case MOVETYPE_FAKEPUSH:
2907 SV_Physics_Pusher (ent);
2910 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2911 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2914 case MOVETYPE_FOLLOW:
2915 SV_Physics_Follow (ent);
2917 case MOVETYPE_NOCLIP:
2918 if (SV_RunThink(ent))
2921 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2922 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2927 SV_Physics_Step (ent);
2930 if (SV_RunThink (ent))
2934 case MOVETYPE_BOUNCE:
2935 case MOVETYPE_BOUNCEMISSILE:
2936 case MOVETYPE_FLYMISSILE:
2938 case MOVETYPE_FLY_WORLDONLY:
2940 if (SV_RunThink (ent))
2941 SV_Physics_Toss (ent);
2943 case MOVETYPE_PHYSICS:
2944 if (SV_RunThink(ent))
2947 SV_LinkEdict_TouchAreaGrid(ent);
2951 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2956 void SV_Physics_ClientMove(void)
2958 prvm_prog_t *prog = SVVM_prog;
2960 ent = host_client->edict;
2962 // call player physics, this needs the proper frametime
2963 PRVM_serverglobalfloat(frametime) = sv.frametime;
2966 // call standard client pre-think, with frametime = 0
2967 PRVM_serverglobalfloat(time) = sv.time;
2968 PRVM_serverglobalfloat(frametime) = 0;
2969 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2970 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2971 PRVM_serverglobalfloat(frametime) = sv.frametime;
2973 // make sure the velocity is sane (not a NaN)
2974 SV_CheckVelocity(ent);
2976 // perform MOVETYPE_WALK behavior
2979 // call standard player post-think, with frametime = 0
2980 PRVM_serverglobalfloat(time) = sv.time;
2981 PRVM_serverglobalfloat(frametime) = 0;
2982 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2983 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2984 PRVM_serverglobalfloat(frametime) = sv.frametime;
2986 if(PRVM_serveredictfloat(ent, fixangle))
2988 // angle fixing was requested by physics code...
2989 // so store the current angles for later use
2990 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2991 host_client->fixangle_angles_set = TRUE;
2993 // and clear fixangle for the next frame
2994 PRVM_serveredictfloat(ent, fixangle) = 0;
2998 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
3000 prvm_prog_t *prog = SVVM_prog;
3001 // don't do physics on disconnected clients, FrikBot relies on this
3002 if (!host_client->begun)
3005 // make sure the velocity is sane (not a NaN)
3006 SV_CheckVelocity(ent);
3008 // don't run physics here if running asynchronously
3009 if (host_client->clmovement_inputtimeout <= 0)
3012 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
3015 // make sure the velocity is still sane (not a NaN)
3016 SV_CheckVelocity(ent);
3018 // call standard client pre-think
3019 PRVM_serverglobalfloat(time) = sv.time;
3020 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3021 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3023 // make sure the velocity is still sane (not a NaN)
3024 SV_CheckVelocity(ent);
3027 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3029 prvm_prog_t *prog = SVVM_prog;
3030 // don't do physics on disconnected clients, FrikBot relies on this
3031 if (!host_client->begun)
3034 // make sure the velocity is sane (not a NaN)
3035 SV_CheckVelocity(ent);
3037 // call standard player post-think
3038 PRVM_serverglobalfloat(time) = sv.time;
3039 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3040 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3042 // make sure the velocity is still sane (not a NaN)
3043 SV_CheckVelocity(ent);
3045 if(PRVM_serveredictfloat(ent, fixangle))
3047 // angle fixing was requested by physics code...
3048 // so store the current angles for later use
3049 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3050 host_client->fixangle_angles_set = TRUE;
3052 // and clear fixangle for the next frame
3053 PRVM_serveredictfloat(ent, fixangle) = 0;
3056 // decrement the countdown variable used to decide when to go back to
3057 // synchronous physics
3058 if (host_client->clmovement_inputtimeout > sv.frametime)
3059 host_client->clmovement_inputtimeout -= sv.frametime;
3061 host_client->clmovement_inputtimeout = 0;
3064 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3066 prvm_prog_t *prog = SVVM_prog;
3067 // don't do physics on disconnected clients, FrikBot relies on this
3068 if (!host_client->begun)
3070 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3074 // make sure the velocity is sane (not a NaN)
3075 SV_CheckVelocity(ent);
3077 switch ((int) PRVM_serveredictfloat(ent, movetype))
3080 case MOVETYPE_FAKEPUSH:
3081 SV_Physics_Pusher (ent);
3084 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3085 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3088 case MOVETYPE_FOLLOW:
3089 SV_Physics_Follow (ent);
3091 case MOVETYPE_NOCLIP:
3094 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3095 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3098 SV_Physics_Step (ent);
3102 // don't run physics here if running asynchronously
3103 if (host_client->clmovement_inputtimeout <= 0)
3107 case MOVETYPE_BOUNCE:
3108 case MOVETYPE_BOUNCEMISSILE:
3109 case MOVETYPE_FLYMISSILE:
3112 SV_Physics_Toss (ent);
3115 case MOVETYPE_FLY_WORLDONLY:
3119 case MOVETYPE_PHYSICS:
3123 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3127 SV_CheckVelocity (ent);
3130 SV_LinkEdict_TouchAreaGrid(ent);
3132 SV_CheckVelocity (ent);
3141 void SV_Physics (void)
3143 prvm_prog_t *prog = SVVM_prog;
3147 // let the progs know that a new frame has started
3148 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3149 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3150 PRVM_serverglobalfloat(time) = sv.time;
3151 PRVM_serverglobalfloat(frametime) = sv.frametime;
3152 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3154 // run physics engine
3155 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3158 // treat each object in turn
3161 // if force_retouch, relink all the entities
3162 if (PRVM_serverglobalfloat(force_retouch) > 0)
3163 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3164 if (!ent->priv.server->free)
3165 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3167 if (sv_gameplayfix_consistentplayerprethink.integer)
3169 // run physics on the client entities in 3 stages
3170 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3171 if (!ent->priv.server->free)
3172 SV_Physics_ClientEntity_PreThink(ent);
3174 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3175 if (!ent->priv.server->free)
3176 SV_Physics_ClientEntity(ent);
3178 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3179 if (!ent->priv.server->free)
3180 SV_Physics_ClientEntity_PostThink(ent);
3184 // run physics on the client entities
3185 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3187 if (!ent->priv.server->free)
3189 SV_Physics_ClientEntity_PreThink(ent);
3190 SV_Physics_ClientEntity(ent);
3191 SV_Physics_ClientEntity_PostThink(ent);
3196 // run physics on all the non-client entities
3197 if (!sv_freezenonclients.integer)
3199 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3200 if (!ent->priv.server->free)
3201 SV_Physics_Entity(ent);
3202 // make a second pass to see if any ents spawned this frame and make
3203 // sure they run their move/think
3204 if (sv_gameplayfix_delayprojectiles.integer < 0)
3205 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3206 if (!ent->priv.server->move && !ent->priv.server->free)
3207 SV_Physics_Entity(ent);
3210 if (PRVM_serverglobalfloat(force_retouch) > 0)
3211 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3213 // LordHavoc: endframe support
3214 if (PRVM_serverfunction(EndFrame))
3216 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3217 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3218 PRVM_serverglobalfloat(time) = sv.time;
3219 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3222 // decrement prog->num_edicts if the highest number entities died
3223 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3225 if (!sv_freezenonclients.integer)
3226 sv.time += sv.frametime;