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, 0.0f);
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 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, float extend)
252 prvm_prog_t *prog = SVVM_prog;
253 int i, bodysupercontents;
256 prvm_edict_t *traceowner, *touch;
258 // temporary storage because prvm_vec_t may differ from vec_t
259 vec3_t touchmins, touchmaxs;
260 // bounding box of entire move area
261 vec3_t clipboxmins, clipboxmaxs;
262 // size when clipping against monsters
263 vec3_t clipmins2, clipmaxs2;
264 // start and end origin of move
265 vec3_t clipstart, clipend;
268 // matrices to transform into/out of other entity's space
269 matrix4x4_t matrix, imatrix;
270 // model of other entity
272 // list of entities to test for collisions
274 static prvm_edict_t *touchedicts[MAX_EDICTS];
275 if (VectorCompare(start, end))
276 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
278 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
280 VectorCopy(start, clipstart);
281 VectorCopy(end, clipend);
282 VectorClear(clipmins2);
283 VectorClear(clipmaxs2);
284 #if COLLISIONPARANOID >= 3
285 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
289 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, extend, false);
290 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
291 if (cliptrace.startsolid || cliptrace.fraction < 1)
292 cliptrace.ent = prog->edicts;
293 if (type == MOVE_WORLDONLY)
296 if (type == MOVE_MISSILE)
298 // LordHavoc: modified this, was = -15, now -= 15
299 for (i = 0;i < 3;i++)
306 // create the bounding box of the entire move
307 for (i = 0;i < 3;i++)
309 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
310 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
313 // debug override to test against everything
314 if (sv_debugmove.integer)
316 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
317 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
320 // if the passedict is world, make it NULL (to avoid two checks each time)
321 if (passedict == prog->edicts)
323 // precalculate prog value for passedict for comparisons
324 passedictprog = PRVM_EDICT_TO_PROG(passedict);
325 // precalculate passedict's owner edict pointer for comparisons
326 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
329 // because this uses World_EntitiestoBox, we know all entity boxes overlap
330 // the clip region, so we can skip culling checks in the loop below
331 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
332 if (numtouchedicts > MAX_EDICTS)
334 // this never happens
335 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
336 numtouchedicts = MAX_EDICTS;
338 for (i = 0;i < numtouchedicts;i++)
340 touch = touchedicts[i];
342 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
344 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
349 // don't clip against self
350 if (passedict == touch)
352 // don't clip owned entities against owner
353 if (traceowner == touch)
355 // don't clip owner against owned entities
356 if (passedictprog == PRVM_serveredictedict(touch, owner))
358 // don't clip points against points (they can't collide)
359 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
363 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
365 // might interact, so do an exact clip
367 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
369 model = SV_GetModelFromEdict(touch);
370 pitchsign = SV_GetPitchSign(prog, touch);
373 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);
375 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
376 Matrix4x4_Invert_Simple(&imatrix, &matrix);
377 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
378 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
379 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
380 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
381 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
382 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
383 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, extend);
385 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, extend, false);
387 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
399 #if COLLISIONPARANOID >= 1
400 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, float extend)
402 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, float extend)
405 prvm_prog_t *prog = SVVM_prog;
406 vec3_t hullmins, hullmaxs;
407 int i, bodysupercontents;
411 prvm_edict_t *traceowner, *touch;
413 // temporary storage because prvm_vec_t may differ from vec_t
414 vec3_t touchmins, touchmaxs;
415 // bounding box of entire move area
416 vec3_t clipboxmins, clipboxmaxs;
417 // size of the moving object
418 vec3_t clipmins, clipmaxs;
419 // size when clipping against monsters
420 vec3_t clipmins2, clipmaxs2;
421 // start and end origin of move
422 vec3_t clipstart, clipend;
425 // matrices to transform into/out of other entity's space
426 matrix4x4_t matrix, imatrix;
427 // model of other entity
429 // list of entities to test for collisions
431 static prvm_edict_t *touchedicts[MAX_EDICTS];
432 if (VectorCompare(mins, maxs))
434 vec3_t shiftstart, shiftend;
435 VectorAdd(start, mins, shiftstart);
436 VectorAdd(end, mins, shiftend);
437 if (VectorCompare(start, end))
438 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
440 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, extend);
441 VectorSubtract(trace.endpos, mins, trace.endpos);
445 VectorCopy(start, clipstart);
446 VectorCopy(end, clipend);
447 VectorCopy(mins, clipmins);
448 VectorCopy(maxs, clipmaxs);
449 VectorCopy(mins, clipmins2);
450 VectorCopy(maxs, clipmaxs2);
451 #if COLLISIONPARANOID >= 3
452 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
456 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, extend);
457 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
458 if (cliptrace.startsolid || cliptrace.fraction < 1)
459 cliptrace.ent = prog->edicts;
460 if (type == MOVE_WORLDONLY)
463 if (type == MOVE_MISSILE)
465 // LordHavoc: modified this, was = -15, now -= 15
466 for (i = 0;i < 3;i++)
473 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
474 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
475 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
478 VectorCopy(clipmins, hullmins);
479 VectorCopy(clipmaxs, hullmaxs);
482 // create the bounding box of the entire move
483 for (i = 0;i < 3;i++)
485 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
486 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
489 // debug override to test against everything
490 if (sv_debugmove.integer)
492 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
493 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
496 // if the passedict is world, make it NULL (to avoid two checks each time)
497 if (passedict == prog->edicts)
499 // precalculate prog value for passedict for comparisons
500 passedictprog = PRVM_EDICT_TO_PROG(passedict);
501 // figure out whether this is a point trace for comparisons
502 pointtrace = VectorCompare(clipmins, clipmaxs);
503 // precalculate passedict's owner edict pointer for comparisons
504 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
507 // because this uses World_EntitiestoBox, we know all entity boxes overlap
508 // the clip region, so we can skip culling checks in the loop below
509 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
510 if (numtouchedicts > MAX_EDICTS)
512 // this never happens
513 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
514 numtouchedicts = MAX_EDICTS;
516 for (i = 0;i < numtouchedicts;i++)
518 touch = touchedicts[i];
520 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
522 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
527 // don't clip against self
528 if (passedict == touch)
530 // don't clip owned entities against owner
531 if (traceowner == touch)
533 // don't clip owner against owned entities
534 if (passedictprog == PRVM_serveredictedict(touch, owner))
536 // don't clip points against points (they can't collide)
537 if (pointtrace && VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
541 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
543 // might interact, so do an exact clip
545 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
547 model = SV_GetModelFromEdict(touch);
548 pitchsign = SV_GetPitchSign(prog, touch);
551 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);
553 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
554 Matrix4x4_Invert_Simple(&imatrix, &matrix);
555 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
556 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
557 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
558 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
559 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
560 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
561 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, extend);
563 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, extend);
565 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
572 #if COLLISIONPARANOID >= 1
573 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)
575 prvm_prog_t *prog = SVVM_prog;
579 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
582 VectorCopy(trace.endpos, temp);
583 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
584 #if COLLISIONPARANOID < 3
585 if (trace.startsolid || endstuck)
587 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" : "");
593 int SV_PointSuperContents(const vec3_t point)
595 prvm_prog_t *prog = SVVM_prog;
596 int supercontents = 0;
600 // matrices to transform into/out of other entity's space
601 matrix4x4_t matrix, imatrix;
602 // model of other entity
605 // list of entities to test for collisions
607 static prvm_edict_t *touchedicts[MAX_EDICTS];
609 // get world supercontents at this point
610 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
611 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
613 // if sv_gameplayfix_swiminbmodels is off we're done
614 if (!sv_gameplayfix_swiminbmodels.integer)
615 return supercontents;
617 // get list of entities at this point
618 numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
619 if (numtouchedicts > MAX_EDICTS)
621 // this never happens
622 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
623 numtouchedicts = MAX_EDICTS;
625 for (i = 0;i < numtouchedicts;i++)
627 touch = touchedicts[i];
629 // we only care about SOLID_BSP for pointcontents
630 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
633 // might interact, so do an exact clip
634 model = SV_GetModelFromEdict(touch);
635 if (!model || !model->PointSuperContents)
637 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);
638 Matrix4x4_Invert_Simple(&imatrix, &matrix);
639 Matrix4x4_Transform(&imatrix, point, transformed);
640 frame = (int)PRVM_serveredictfloat(touch, frame);
641 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
644 return supercontents;
648 ===============================================================================
650 Linking entities into the world culling system
652 ===============================================================================
655 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
657 prvm_prog_t *prog = SVVM_prog;
658 vec3_t paddedmins, paddedmaxs;
659 if (maxedicts < 1 || resultedicts == NULL)
661 // LordHavoc: discovered this actually causes its own bugs (dm6 teleporters being too close to info_teleport_destination)
662 //VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
663 //VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
664 VectorCopy(mins, paddedmins);
665 VectorCopy(maxs, paddedmaxs);
666 if (sv_areadebug.integer)
668 int numresultedicts = 0;
671 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
673 ed = PRVM_EDICT_NUM(edictindex);
674 if (!ed->priv.required->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
676 resultedicts[numresultedicts++] = ed;
677 if (numresultedicts == maxedicts)
681 return numresultedicts;
684 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
687 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
689 prvm_prog_t *prog = SVVM_prog;
690 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
691 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
692 PRVM_serverglobalfloat(time) = sv.time;
693 PRVM_serverglobalfloat(trace_allsolid) = false;
694 PRVM_serverglobalfloat(trace_startsolid) = false;
695 PRVM_serverglobalfloat(trace_fraction) = 1;
696 PRVM_serverglobalfloat(trace_inwater) = false;
697 PRVM_serverglobalfloat(trace_inopen) = true;
698 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
699 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
700 PRVM_serverglobalfloat(trace_plane_dist) = 0;
701 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
702 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
703 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
704 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
705 PRVM_serverglobalstring(trace_dphittexturename) = 0;
706 prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
709 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
711 prvm_prog_t *prog = SVVM_prog;
712 int i, numtouchedicts, old_self, old_other;
714 static prvm_edict_t *touchedicts[MAX_EDICTS];
716 if (ent == prog->edicts)
717 return; // don't add the world
719 if (ent->priv.server->free)
722 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
725 // build a list of edicts to touch, because the link loop can be corrupted
726 // by IncreaseEdicts called during touch functions
727 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
728 if (numtouchedicts > MAX_EDICTS)
730 // this never happens
731 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
732 numtouchedicts = MAX_EDICTS;
735 old_self = PRVM_serverglobaledict(self);
736 old_other = PRVM_serverglobaledict(other);
737 for (i = 0;i < numtouchedicts;i++)
739 touch = touchedicts[i];
740 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
742 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
745 PRVM_serverglobaledict(self) = old_self;
746 PRVM_serverglobaledict(other) = old_other;
749 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
753 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
755 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
756 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
757 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
758 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];
759 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];
760 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
761 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];
762 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];
763 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
764 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];
765 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];
766 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
767 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];
768 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];
769 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
770 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];
771 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];
772 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
773 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];
774 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];
775 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
776 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];
777 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];
786 void SV_LinkEdict (prvm_edict_t *ent)
788 prvm_prog_t *prog = SVVM_prog;
790 vec3_t mins, maxs, entmins, entmaxs, entangles;
793 if (ent == prog->edicts)
794 return; // don't add the world
796 if (ent->priv.server->free)
799 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
800 if (modelindex < 0 || modelindex >= MAX_MODELS)
802 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
805 model = SV_GetModelByIndex(modelindex);
807 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
808 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
809 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
813 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
815 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
816 // TODO special handling for spheres?
817 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
818 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
819 VectorCopy(PRVM_serveredictvector(ent, angles), entangles);
820 RotateBBox(entmins, entmaxs, entangles, mins, maxs);
821 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
822 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
824 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
828 if (!model->TraceBox)
829 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
831 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
833 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
834 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
836 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
838 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
839 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
843 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
844 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
849 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
850 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
851 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
856 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
857 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
861 // to make items easier to pick up and allow them to be grabbed off
862 // of shelves, the abs sizes are expanded
864 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
875 // because movement is clipped an epsilon away from an actual edge,
876 // we must fully check even when bounding boxes don't quite touch
885 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
886 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
888 World_LinkEdict(&sv.world, ent, mins, maxs);
892 ===============================================================================
896 ===============================================================================
901 SV_TestEntityPosition
903 returns true if the entity is in solid currently
906 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
908 prvm_prog_t *prog = SVVM_prog;
910 vec3_t org, entorigin, entmins, entmaxs;
912 contents = SV_GenericHitSuperContentsMask(ent);
913 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
914 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
915 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
916 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
917 trace = SV_TraceBox(org, entmins, entmaxs, entorigin, ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, contents, collision_extendmovelength.value);
918 if (trace.startsupercontents & contents)
922 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
924 // q1bsp/hlbsp use hulls and if the entity does not exactly match
925 // a hull size it is incorrectly tested, so this code tries to
926 // 'fix' it slightly...
927 // FIXME: this breaks entities larger than the hull size
930 VectorAdd(org, entmins, m1);
931 VectorAdd(org, entmaxs, m2);
932 VectorSubtract(m2, m1, s);
933 #define EPSILON (1.0f / 32.0f)
934 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
935 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
936 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
937 for (i = 0;i < 8;i++)
939 v[0] = (i & 1) ? m2[0] : m1[0];
940 v[1] = (i & 2) ? m2[1] : m1[1];
941 v[2] = (i & 4) ? m2[2] : m1[2];
942 if (SV_PointSuperContents(v) & contents)
947 // if the trace found a better position for the entity, move it there
948 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
951 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
952 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
954 // verify if the endpos is REALLY outside solid
955 VectorCopy(trace.endpos, org);
956 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, contents, collision_extendmovelength.value);
958 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
960 VectorCopy(org, PRVM_serveredictvector(ent, origin));
966 // DRESK - Support for Entity Contents Transition Event
969 SV_CheckContentsTransition
971 returns true if entity had a valid contentstransition function call
974 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
976 prvm_prog_t *prog = SVVM_prog;
977 int bValidFunctionCall;
979 // Default Valid Function Call to False
980 bValidFunctionCall = false;
982 if(PRVM_serveredictfloat(ent, watertype) != nContents)
983 { // Changed Contents
984 // Acquire Contents Transition Function from QC
985 if(PRVM_serveredictfunction(ent, contentstransition))
986 { // Valid Function; Execute
987 // Assign Valid Function
988 bValidFunctionCall = true;
989 // Prepare Parameters (Original Contents, New Contents)
991 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
993 PRVM_G_FLOAT(OFS_PARM1) = nContents;
995 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
997 PRVM_serverglobalfloat(time) = sv.time;
998 // Execute VM Function
999 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1003 // Return if Function Call was Valid
1004 return bValidFunctionCall;
1013 void SV_CheckVelocity (prvm_edict_t *ent)
1015 prvm_prog_t *prog = SVVM_prog;
1022 for (i=0 ; i<3 ; i++)
1024 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1026 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1027 PRVM_serveredictvector(ent, velocity)[i] = 0;
1029 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1031 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1032 PRVM_serveredictvector(ent, origin)[i] = 0;
1036 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1037 // player_run/player_stand1 does not horribly malfunction if the
1038 // velocity becomes a denormalized float
1039 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0001)
1040 VectorClear(PRVM_serveredictvector(ent, velocity));
1042 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1043 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1044 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1046 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1047 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1048 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1049 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1057 Runs thinking code if time. There is some play in the exact time the think
1058 function will be called, because it is called before any movement is done
1059 in a frame. Not used for pushmove objects, because they must be exact.
1060 Returns false if the entity removed itself.
1063 static qboolean SV_RunThink (prvm_edict_t *ent)
1065 prvm_prog_t *prog = SVVM_prog;
1068 // don't let things stay in the past.
1069 // it is possible to start that way by a trigger with a local time.
1070 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1073 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1075 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1076 PRVM_serveredictfloat(ent, nextthink) = 0;
1077 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1078 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1079 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1080 // mods often set nextthink to time to cause a think every frame,
1081 // we don't want to loop in that case, so exit if the new nextthink is
1082 // <= the time the qc was told, also exit if it is past the end of the
1084 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1087 return !ent->priv.server->free;
1094 Two entities have touched, so run their touch functions
1097 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1099 prvm_prog_t *prog = SVVM_prog;
1100 int restorevm_tempstringsbuf_cursize;
1101 int old_self, old_other;
1102 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1104 old_self = PRVM_serverglobaledict(self);
1105 old_other = PRVM_serverglobaledict(other);
1106 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1108 VM_SetTraceGlobals(prog, trace);
1110 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1112 PRVM_serverglobalfloat(time) = sv.time;
1113 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1114 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1115 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1118 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1120 PRVM_serverglobalfloat(time) = sv.time;
1121 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1122 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1123 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1124 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1125 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1126 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1127 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1128 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1129 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1130 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1131 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1134 PRVM_serverglobaledict(self) = old_self;
1135 PRVM_serverglobaledict(other) = old_other;
1136 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1144 Slide off of the impacting object
1145 returns the blocked flags (1 = floor, 2 = step / wall)
1148 #define STOP_EPSILON 0.1
1149 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1154 backoff = -DotProduct (in, normal) * overbounce;
1155 VectorMA(in, backoff, normal, out);
1157 for (i = 0;i < 3;i++)
1158 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1167 The basic solid body movement clip that slides along multiple planes
1168 Returns the clipflags if the velocity was modified (hit something solid)
1172 8 = teleported by touch method
1173 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1176 static float SV_Gravity (prvm_edict_t *ent);
1177 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink);
1178 #define MAX_CLIP_PLANES 5
1179 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1181 prvm_prog_t *prog = SVVM_prog;
1182 int blocked, bumpcount;
1183 int i, j, numplanes;
1184 float d, time_left, gravity;
1185 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1186 prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
1195 VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1199 gravity = SV_Gravity(ent);
1201 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1203 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1204 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1206 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1211 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1212 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1215 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1217 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1220 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1221 if(!SV_PushEntity(&trace, ent, push, false))
1223 // we got teleported by a touch function
1224 // let's abort the move
1229 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
1230 // abort move if we're stuck in the world (and didn't make it out)
1231 if (trace.worldstartsolid && trace.allsolid)
1233 VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
1237 if (trace.fraction == 1)
1239 if (trace.plane.normal[2])
1241 if (trace.plane.normal[2] > 0.7)
1248 Con_Printf ("SV_FlyMove: !trace.ent");
1249 trace.ent = prog->edicts;
1252 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1253 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1256 else if (stepheight)
1258 // step - handle it immediately
1264 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1265 VectorSet(steppush, 0, 0, stepheight);
1266 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1267 if(!SV_PushEntity(&steptrace, ent, steppush, false))
1272 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1273 if(!SV_PushEntity(&steptrace2, ent, push, false))
1278 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1279 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1280 if(!SV_PushEntity(&steptrace3, ent, steppush, false))
1285 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1286 // accept the new position if it made some progress...
1287 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1289 //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]);
1291 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1292 time_left *= 1 - trace.fraction;
1298 //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]);
1299 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1304 // step - return it to caller
1306 // save the trace for player extrafriction
1308 VectorCopy(trace.plane.normal, stepnormal);
1310 if (trace.fraction >= 0.001)
1312 // actually covered some distance
1313 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1317 time_left *= 1 - trace.fraction;
1319 // clipped to another plane
1320 if (numplanes >= MAX_CLIP_PLANES)
1322 // this shouldn't really happen
1323 VectorClear(PRVM_serveredictvector(ent, velocity));
1329 for (i = 0;i < numplanes;i++)
1330 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1334 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1339 VectorCopy(trace.plane.normal, planes[numplanes]);
1342 // modify original_velocity so it parallels all of the clip planes
1343 for (i = 0;i < numplanes;i++)
1345 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1346 for (j = 0;j < numplanes;j++)
1351 if (DotProduct(new_velocity, planes[j]) < 0)
1361 // go along this plane
1362 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1366 // go along the crease
1369 VectorClear(PRVM_serveredictvector(ent, velocity));
1373 CrossProduct(planes[0], planes[1], dir);
1374 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1375 VectorNormalize(dir);
1376 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1377 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1380 // if current velocity is against the original velocity,
1381 // stop dead to avoid tiny occilations in sloping corners
1382 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1384 VectorClear(PRVM_serveredictvector(ent, velocity));
1389 //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]);
1392 if ((blocked & 1) == 0 && bumpcount > 1)
1394 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1395 // flag ONGROUND if there's ground under it
1396 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask);
1400 // LordHavoc: this came from QW and allows you to get out of water more easily
1401 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1402 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1406 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1408 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1409 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1422 static float SV_Gravity (prvm_edict_t *ent)
1424 prvm_prog_t *prog = SVVM_prog;
1427 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1430 return ent_gravity * sv_gravity.value * sv.frametime;
1435 ===============================================================================
1439 ===============================================================================
1442 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1444 prvm_prog_t *prog = SVVM_prog;
1448 vec3_t stuckmins, stuckmaxs;
1449 vec3_t goodmins, goodmaxs;
1453 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1454 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1455 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1456 VectorCopy(pivot, goodmins);
1457 VectorCopy(pivot, goodmaxs);
1458 for (bump = 0;bump < 6;bump++)
1460 int coord = 2-(bump >> 1);
1461 //int coord = (bump >> 1);
1462 int dir = (bump & 1);
1465 for(subbump = 0; ; ++subbump)
1467 VectorCopy(stuckorigin, testorigin);
1471 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1476 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1479 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), collision_extendmovelength.value);
1480 if (stucktrace.bmodelstartsolid)
1482 // BAD BAD, can't fix that
1486 if (stucktrace.fraction >= 1)
1491 // BAD BAD, can't fix that
1495 // we hit something... let's move out of it
1496 VectorSubtract(stucktrace.endpos, testorigin, move);
1497 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1498 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1502 Con_Printf("subbump: %d\n", subbump);
1508 goodmaxs[coord] = stuckmaxs[coord];
1513 goodmins[coord] = stuckmins[coord];
1518 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1523 qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1525 prvm_prog_t *prog = SVVM_prog;
1529 vec3_t stuckmins, stuckmaxs;
1531 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1532 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1533 separation = 0.0f; // when using hulls, it can not be enlarged
1534 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1535 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1536 stuckmins[0] -= separation;
1537 stuckmins[1] -= separation;
1538 stuckmins[2] -= separation;
1539 stuckmaxs[0] += separation;
1540 stuckmaxs[1] += separation;
1541 stuckmaxs[2] += separation;
1542 // first pass we try to get it out of brush entities
1543 // second pass we try to get it out of world only (can't win them all)
1544 for (pass = 0;pass < 2;pass++)
1546 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1547 for (bump = 0;bump < 10;bump++)
1549 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), collision_extendmovelength.value);
1550 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1552 // found a good location, use it
1553 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1556 nudge = -stucktrace.startdepth;
1557 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1567 Does not change the entities velocity at all
1568 The trace struct is filled with the trace that has been done.
1569 Returns true if the push did not result in the entity being teleported by QC code.
1572 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink)
1574 prvm_prog_t *prog = SVVM_prog;
1582 solid = (int)PRVM_serveredictfloat(ent, solid);
1583 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1584 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1585 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1587 // move start position out of solids
1588 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1590 SV_NudgeOutOfSolid(ent);
1593 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1594 VectorAdd(start, push, end);
1596 if (movetype == MOVETYPE_FLYMISSILE)
1597 type = MOVE_MISSILE;
1598 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1599 type = MOVE_WORLDONLY;
1600 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1601 type = MOVE_NOMONSTERS; // only clip against bmodels
1605 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), collision_extendmovelength.value);
1606 // fail the move if stuck in world
1607 if (trace->worldstartsolid)
1610 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1612 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1617 if(!trace->startsolid)
1618 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)
1620 Con_Printf("something eeeeevil happened\n");
1625 SV_LinkEdict_TouchAreaGrid(ent);
1627 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))))
1628 SV_Impact (ent, trace);
1630 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1632 ent->priv.required->mark = 0;
1635 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1637 ent->priv.required->mark = 0;
1642 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1654 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1656 prvm_prog_t *prog = SVVM_prog;
1658 int pusherowner, pusherprog;
1661 float savesolid, movetime2, pushltime;
1662 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1664 int numcheckentities;
1665 static prvm_edict_t *checkentities[MAX_EDICTS];
1666 dp_model_t *pushermodel;
1667 trace_t trace, trace2;
1668 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1669 static unsigned short moved_edicts[MAX_EDICTS];
1672 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])
1674 PRVM_serveredictfloat(pusher, ltime) += movetime;
1678 switch ((int) PRVM_serveredictfloat(pusher, solid))
1680 // LordHavoc: valid pusher types
1683 case SOLID_SLIDEBOX:
1684 case SOLID_CORPSE: // LordHavoc: this would be weird...
1686 // LordHavoc: no collisions
1689 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1690 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1691 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1692 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1693 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1694 PRVM_serveredictfloat(pusher, ltime) += movetime;
1695 SV_LinkEdict(pusher);
1698 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1701 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1702 if (index < 1 || index >= MAX_MODELS)
1704 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1707 pushermodel = SV_GetModelByIndex(index);
1708 pusherowner = PRVM_serveredictedict(pusher, owner);
1709 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1711 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1713 movetime2 = movetime;
1714 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1715 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1716 if (moveangle[0] || moveangle[2])
1718 for (i = 0;i < 3;i++)
1722 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1723 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1727 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1728 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1732 else if (moveangle[1])
1734 for (i = 0;i < 3;i++)
1738 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1739 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1743 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1744 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1750 for (i = 0;i < 3;i++)
1754 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1755 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1759 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1760 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1765 VectorNegate (moveangle, a);
1766 AngleVectorsFLU (a, forward, left, up);
1768 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1769 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1770 pushltime = PRVM_serveredictfloat(pusher, ltime);
1772 // move the pusher to its final position
1774 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1775 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1776 PRVM_serveredictfloat(pusher, ltime) += movetime;
1777 SV_LinkEdict(pusher);
1779 pushermodel = SV_GetModelFromEdict(pusher);
1780 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);
1781 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1783 savesolid = PRVM_serveredictfloat(pusher, solid);
1785 // see if any solid entities are inside the final position
1788 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1789 numcheckentities = 0;
1790 else // MOVETYPE_PUSH
1791 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1792 for (e = 0;e < numcheckentities;e++)
1794 prvm_edict_t *check = checkentities[e];
1795 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1800 case MOVETYPE_FOLLOW:
1801 case MOVETYPE_NOCLIP:
1802 case MOVETYPE_FLY_WORLDONLY:
1808 if (PRVM_serveredictedict(check, owner) == pusherprog)
1811 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1814 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1816 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1817 check->priv.server->waterposition_forceupdate = true;
1819 checkcontents = SV_GenericHitSuperContentsMask(check);
1821 // if the entity is standing on the pusher, it will definitely be moved
1822 // if the entity is not standing on the pusher, but is in the pusher's
1823 // final position, move it
1824 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1826 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1827 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1828 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1829 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1830 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1831 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, collision_extendmovelength.value);
1832 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1833 if (!trace.startsolid)
1835 //Con_Printf("- not in solid\n");
1840 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1841 //VectorClear(pivot);
1846 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1847 VectorAdd (org, pivot, org);
1848 org2[0] = DotProduct (org, forward);
1849 org2[1] = DotProduct (org, left);
1850 org2[2] = DotProduct (org, up);
1851 VectorSubtract (org2, org, move);
1852 VectorAdd (move, move1, move);
1855 VectorCopy (move1, move);
1857 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1859 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1860 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1861 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1863 // physics objects need better collisions than this code can do
1864 if (movetype == MOVETYPE_PHYSICS)
1866 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1867 SV_LinkEdict(check);
1868 SV_LinkEdict_TouchAreaGrid(check);
1872 // try moving the contacted entity
1873 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1874 if(!SV_PushEntity (&trace, check, move, true))
1876 // entity "check" got teleported
1877 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1878 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1879 continue; // pushed enough
1881 // FIXME: turn players specially
1882 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1883 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1884 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1886 // this trace.fraction < 1 check causes items to fall off of pushers
1887 // if they pass under or through a wall
1888 // the groundentity check causes items to fall off of ledges
1889 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1890 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1892 // if it is still inside the pusher, block
1893 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1894 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1895 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1896 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1897 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1898 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, collision_extendmovelength.value);
1899 if (trace.startsolid)
1902 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1904 // hack to invoke all necessary movement triggers
1906 if(!SV_PushEntity(&trace2, check, move2, true))
1908 // entity "check" got teleported
1915 // still inside pusher, so it's really blocked
1918 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1920 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1923 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1924 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1928 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1929 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1930 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1931 SV_LinkEdict(pusher);
1933 // move back any entities we already moved
1934 for (i = 0;i < num_moved;i++)
1936 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1937 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1938 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1942 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1943 if (PRVM_serveredictfunction(pusher, blocked))
1945 PRVM_serverglobalfloat(time) = sv.time;
1946 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1947 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1948 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1953 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1954 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1955 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1964 static void SV_Physics_Pusher (prvm_edict_t *ent)
1966 prvm_prog_t *prog = SVVM_prog;
1967 float thinktime, oldltime, movetime;
1969 oldltime = PRVM_serveredictfloat(ent, ltime);
1971 thinktime = PRVM_serveredictfloat(ent, nextthink);
1972 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1974 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1979 movetime = sv.frametime;
1982 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
1983 SV_PushMove (ent, movetime);
1985 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
1987 PRVM_serveredictfloat(ent, nextthink) = 0;
1988 PRVM_serverglobalfloat(time) = sv.time;
1989 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1990 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1991 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1997 ===============================================================================
2001 ===============================================================================
2004 static float unstickoffsets[] =
2006 // poutting -/+z changes first as they are least weird
2021 typedef enum unstickresult_e
2029 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2031 prvm_prog_t *prog = SVVM_prog;
2034 // if not stuck in a bmodel, just return
2035 if (!SV_TestEntityPosition(ent, vec3_origin))
2036 return UNSTICK_GOOD;
2038 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2040 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2042 VectorCopy(unstickoffsets + i, offset);
2044 //SV_LinkEdict_TouchAreaGrid(ent);
2045 return UNSTICK_UNSTUCK;
2049 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2050 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2052 for(i = 2; i <= maxunstick; ++i)
2054 VectorClear(offset);
2056 if (!SV_TestEntityPosition(ent, offset))
2059 //SV_LinkEdict_TouchAreaGrid(ent);
2060 return UNSTICK_UNSTUCK;
2063 if (!SV_TestEntityPosition(ent, offset))
2066 //SV_LinkEdict_TouchAreaGrid(ent);
2067 return UNSTICK_UNSTUCK;
2071 return UNSTICK_STUCK;
2074 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2076 prvm_prog_t *prog = SVVM_prog;
2078 switch(SV_UnstickEntityReturnOffset(ent, offset))
2082 case UNSTICK_UNSTUCK:
2083 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]);
2086 if (developer_extra.integer)
2087 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2090 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2099 This is a big hack to try and fix the rare case of getting stuck in the world
2103 static void SV_CheckStuck (prvm_edict_t *ent)
2105 prvm_prog_t *prog = SVVM_prog;
2108 switch(SV_UnstickEntityReturnOffset(ent, offset))
2111 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2113 case UNSTICK_UNSTUCK:
2114 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]);
2117 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2118 if (!SV_TestEntityPosition(ent, offset))
2120 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)));
2122 //SV_LinkEdict_TouchAreaGrid(ent);
2125 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2128 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2138 static qboolean SV_CheckWater (prvm_edict_t *ent)
2140 prvm_prog_t *prog = SVVM_prog;
2142 int nNativeContents;
2145 point[0] = PRVM_serveredictvector(ent, origin)[0];
2146 point[1] = PRVM_serveredictvector(ent, origin)[1];
2147 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2149 // DRESK - Support for Entity Contents Transition Event
2150 // NOTE: Some logic needed to be slightly re-ordered
2151 // to not affect performance and allow for the feature.
2153 // Acquire Super Contents Prior to Resets
2154 cont = SV_PointSuperContents(point);
2155 // Acquire Native Contents Here
2156 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2158 // DRESK - Support for Entity Contents Transition Event
2159 if(PRVM_serveredictfloat(ent, watertype))
2160 // Entity did NOT Spawn; Check
2161 SV_CheckContentsTransition(ent, nNativeContents);
2164 PRVM_serveredictfloat(ent, waterlevel) = 0;
2165 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2166 cont = SV_PointSuperContents(point);
2167 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2169 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2170 PRVM_serveredictfloat(ent, waterlevel) = 1;
2171 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2172 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2174 PRVM_serveredictfloat(ent, waterlevel) = 2;
2175 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2176 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2177 PRVM_serveredictfloat(ent, waterlevel) = 3;
2181 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2190 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2192 prvm_prog_t *prog = SVVM_prog;
2194 vec3_t forward, into, side, v_angle;
2196 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2197 AngleVectors (v_angle, forward, NULL, NULL);
2198 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2200 // cut the tangential velocity
2201 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2202 VectorScale (stepnormal, i, into);
2203 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2204 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2205 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2211 =====================
2214 Player has come to a dead stop, possibly due to the problem with limited
2215 float precision at some angle joins in the BSP hull.
2217 Try fixing by pushing one pixel in each direction.
2219 This is a hack, but in the interest of good gameplay...
2220 ======================
2222 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2227 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2230 for (i=0 ; i<8 ; i++)
2232 // try pushing a little in an axial direction
2235 case 0: dir[0] = 2; dir[1] = 0; break;
2236 case 1: dir[0] = 0; dir[1] = 2; break;
2237 case 2: dir[0] = -2; dir[1] = 0; break;
2238 case 3: dir[0] = 0; dir[1] = -2; break;
2239 case 4: dir[0] = 2; dir[1] = 2; break;
2240 case 5: dir[0] = -2; dir[1] = 2; break;
2241 case 6: dir[0] = 2; dir[1] = -2; break;
2242 case 7: dir[0] = -2; dir[1] = -2; break;
2245 SV_PushEntity (&trace, ent, dir, false, true);
2247 // retry the original move
2248 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2249 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2250 PRVM_serveredictvector(ent, velocity)[2] = 0;
2251 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2253 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2254 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2256 Con_DPrint("TryUnstick - success.\n");
2260 // go back to the original pos and try again
2261 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2265 VectorClear (PRVM_serveredictvector(ent, velocity));
2266 Con_DPrint("TryUnstick - failure.\n");
2272 =====================
2275 Only used by players
2276 ======================
2278 static void SV_WalkMove (prvm_edict_t *ent)
2280 prvm_prog_t *prog = SVVM_prog;
2283 //int originalmove_clip;
2284 int originalmove_flags;
2285 int originalmove_groundentity;
2286 int hitsupercontentsmask;
2288 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2289 trace_t downtrace, trace;
2290 qboolean applygravity;
2292 // if frametime is 0 (due to client sending the same timestamp twice),
2294 if (sv.frametime <= 0)
2297 if (sv_gameplayfix_unstickplayers.integer)
2298 SV_CheckStuck (ent);
2300 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2302 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2304 SV_CheckVelocity(ent);
2306 // do a regular slide move unless it looks like you ran into a step
2307 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2309 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2310 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2312 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2314 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2317 // only try this if there was no floor in the way in the trace (no,
2318 // this check seems to be not REALLY necessary, because if clip & 1,
2319 // our trace will hit that thing too)
2320 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2321 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2322 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2323 type = MOVE_MISSILE;
2324 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2325 type = MOVE_WORLDONLY;
2326 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2327 type = MOVE_NOMONSTERS; // only clip against bmodels
2330 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2331 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2332 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), collision_extendmovelength.value);
2333 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2334 clip |= 1; // but we HAVE found a floor
2337 // if the move did not hit the ground at any point, we're not on ground
2339 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2341 SV_CheckVelocity(ent);
2343 SV_LinkEdict_TouchAreaGrid(ent);
2345 if(clip & 8) // teleport
2348 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2351 if (sv_nostep.integer)
2354 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2355 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2356 //originalmove_clip = clip;
2357 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2358 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2360 // if move didn't block on a step, return
2363 // if move was not trying to move into the step, return
2364 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2367 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2369 // return if gibbed by a trigger
2370 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2373 // return if attempting to jump while airborn (unless sv_jumpstep)
2374 if (!sv_jumpstep.integer)
2375 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2379 // try moving up and forward to go up a step
2380 // back to start pos
2381 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2382 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2385 VectorClear (upmove);
2386 upmove[2] = sv_stepheight.value;
2387 if(!SV_PushEntity(&trace, ent, upmove, true))
2389 // we got teleported when upstepping... must abort the move
2394 PRVM_serveredictvector(ent, velocity)[2] = 0;
2395 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2396 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2399 // we got teleported when upstepping... must abort the move
2400 // note that z velocity handling may not be what QC expects here, but we cannot help it
2404 SV_CheckVelocity(ent);
2406 SV_LinkEdict_TouchAreaGrid(ent);
2408 // check for stuckness, possibly due to the limited precision of floats
2409 // in the clipping hulls
2411 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2412 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2414 //Con_Printf("wall\n");
2415 // stepping up didn't make any progress, revert to original move
2416 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2417 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2418 //clip = originalmove_clip;
2419 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2420 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2421 // now try to unstick if needed
2422 //clip = SV_TryUnstick (ent, oldvel);
2426 //Con_Printf("step - ");
2428 // extra friction based on view angle
2429 if (clip & 2 && sv_wallfriction.integer)
2430 SV_WallFriction (ent, stepnormal);
2432 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2433 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))
2437 VectorClear (downmove);
2438 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2439 if(!SV_PushEntity (&downtrace, ent, downmove, true))
2441 // we got teleported when downstepping... must abort the move
2445 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2447 // this has been disabled so that you can't jump when you are stepping
2448 // up while already jumping (also known as the Quake2 double jump bug)
2450 // LordHavoc: disabled this check so you can walk on monsters/players
2451 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2453 //Con_Printf("onground\n");
2454 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2455 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2461 //Con_Printf("slope\n");
2462 // if the push down didn't end up on good ground, use the move without
2463 // the step up. This happens near wall / slope combinations, and can
2464 // cause the player to hop up higher on a slope too steep to climb
2465 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2466 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2467 //clip = originalmove_clip;
2468 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2469 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2472 SV_CheckVelocity(ent);
2474 SV_LinkEdict_TouchAreaGrid(ent);
2477 //============================================================================
2483 Entities that are "stuck" to another entity
2486 static void SV_Physics_Follow (prvm_edict_t *ent)
2488 prvm_prog_t *prog = SVVM_prog;
2489 vec3_t vf, vr, vu, angles, v;
2493 if (!SV_RunThink (ent))
2496 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2497 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2498 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])
2500 // quick case for no rotation
2501 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2505 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2506 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2507 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2508 AngleVectors (angles, vf, vr, vu);
2509 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];
2510 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];
2511 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];
2512 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2513 angles[1] = PRVM_serveredictvector(e, angles)[1];
2514 angles[2] = PRVM_serveredictvector(e, angles)[2];
2515 AngleVectors (angles, vf, vr, vu);
2516 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2517 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2518 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2520 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2522 //SV_LinkEdict_TouchAreaGrid(ent);
2526 ==============================================================================
2530 ==============================================================================
2535 SV_CheckWaterTransition
2539 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2542 prvm_prog_t *prog = SVVM_prog;
2543 // 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
2545 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2546 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(entorigin));
2547 if (!PRVM_serveredictfloat(ent, watertype))
2549 // just spawned here
2550 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2552 PRVM_serveredictfloat(ent, watertype) = cont;
2553 PRVM_serveredictfloat(ent, waterlevel) = 1;
2557 // DRESK - Support for Entity Contents Transition Event
2558 // NOTE: Call here BEFORE updating the watertype below,
2559 // and suppress watersplash sound if a valid function
2560 // call was made to allow for custom "splash" sounds.
2561 else if( !SV_CheckContentsTransition(ent, cont) )
2562 { // Contents Transition Function Invalid; Potentially Play Water Sound
2563 // check if the entity crossed into or out of water
2564 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2565 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2568 if (cont <= CONTENTS_WATER)
2570 PRVM_serveredictfloat(ent, watertype) = cont;
2571 PRVM_serveredictfloat(ent, waterlevel) = 1;
2575 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2576 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2584 Toss, bounce, and fly movement. When onground, do nothing.
2588 void SV_Physics_Toss (prvm_edict_t *ent)
2590 prvm_prog_t *prog = SVVM_prog;
2595 prvm_edict_t *groundentity;
2596 float d, ent_gravity;
2600 // if onground, return without moving
2601 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2603 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2604 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2606 // don't stick to ground if onground and moving upward
2607 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2609 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2611 // we can trust FL_ONGROUND if groundentity is world because it never moves
2614 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2616 // if ent was supported by a brush model on previous frame,
2617 // and groundentity is now freed, set groundentity to 0 (world)
2618 // which leaves it suspended in the air
2619 PRVM_serveredictedict(ent, groundentity) = 0;
2620 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2623 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2625 // don't slide if still touching the groundentity
2629 ent->priv.server->suspendedinairflag = false;
2631 SV_CheckVelocity (ent);
2634 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2635 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2638 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2640 movetime = sv.frametime;
2641 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2644 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2645 if(!SV_PushEntity(&trace, ent, move, true))
2646 return; // teleported
2647 if (ent->priv.server->free)
2649 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2651 // try to unstick the entity
2652 SV_UnstickEntity(ent);
2653 if(!SV_PushEntity(&trace, ent, move, true))
2654 return; // teleported
2655 if (ent->priv.server->free)
2658 if (trace.fraction == 1)
2660 movetime *= 1 - min(1, trace.fraction);
2661 switch((int)PRVM_serveredictfloat(ent, movetype))
2663 case MOVETYPE_BOUNCEMISSILE:
2664 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2666 bouncefactor = 1.0f;
2668 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2669 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2670 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2673 case MOVETYPE_BOUNCE:
2674 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2676 bouncefactor = 0.5f;
2678 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2680 bouncestop = 60.0f / 800.0f;
2682 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2683 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2686 // LordHavoc: fixed grenades not bouncing when fired down a slope
2687 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2688 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2690 d = PRVM_serveredictvector(ent, velocity)[2];
2691 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2693 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2694 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2695 VectorClear(PRVM_serveredictvector(ent, velocity));
2696 VectorClear(PRVM_serveredictvector(ent, avelocity));
2701 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2702 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2707 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2708 if (trace.plane.normal[2] > 0.7)
2710 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2711 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2712 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2713 ent->priv.server->suspendedinairflag = true;
2714 VectorClear (PRVM_serveredictvector(ent, velocity));
2715 VectorClear (PRVM_serveredictvector(ent, avelocity));
2718 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2724 // check for in water
2725 SV_CheckWaterTransition (ent);
2729 ===============================================================================
2733 ===============================================================================
2740 Monsters freefall when they don't have a ground entity, otherwise
2741 all movement is done with discrete steps.
2743 This is also used for objects that have become still on the ground, but
2744 will fall if the floor is pulled out from under them.
2747 static void SV_Physics_Step (prvm_edict_t *ent)
2749 prvm_prog_t *prog = SVVM_prog;
2750 int flags = (int)PRVM_serveredictfloat(ent, flags);
2753 // Backup Velocity in the event that movetypesteplandevent is called,
2754 // to provide a parameter with the entity's velocity at impact.
2755 vec3_t backupVelocity;
2756 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2757 // don't fall at all if fly/swim
2758 if (!(flags & (FL_FLY | FL_SWIM)))
2760 if (flags & FL_ONGROUND)
2762 // freefall if onground and moving upward
2763 // freefall if not standing on a world surface (it may be a lift or trap door)
2764 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2766 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2767 SV_CheckVelocity(ent);
2768 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2770 SV_LinkEdict_TouchAreaGrid(ent);
2771 ent->priv.server->waterposition_forceupdate = true;
2776 // freefall if not onground
2777 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2779 SV_CheckVelocity(ent);
2780 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2782 SV_LinkEdict_TouchAreaGrid(ent);
2785 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2787 // DRESK - Check for Entity Land Event Function
2788 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2789 { // Valid Function; Execute
2790 // Prepare Parameters
2791 // Assign Velocity at Impact
2792 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2793 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2794 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2796 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2798 PRVM_serverglobalfloat(time) = sv.time;
2799 // Execute VM Function
2800 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2803 // Check for Engine Landing Sound
2804 if(sv_sound_land.string)
2805 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2807 ent->priv.server->waterposition_forceupdate = true;
2812 if (!SV_RunThink(ent))
2815 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2817 ent->priv.server->waterposition_forceupdate = false;
2818 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2819 SV_CheckWaterTransition(ent);
2823 //============================================================================
2825 static void SV_Physics_Entity (prvm_edict_t *ent)
2827 prvm_prog_t *prog = SVVM_prog;
2828 // don't run think/move on newly spawned projectiles as it messes up
2829 // movement interpolation and rocket trails, and is inconsistent with
2830 // respect to entities spawned in the same frame
2831 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2832 // but if it spawns a lower numbered ent, it doesn't - this never moves
2833 // ents in the first frame regardless)
2834 qboolean runmove = ent->priv.server->move;
2835 ent->priv.server->move = true;
2836 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2838 switch ((int) PRVM_serveredictfloat(ent, movetype))
2841 case MOVETYPE_FAKEPUSH:
2842 SV_Physics_Pusher (ent);
2845 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2846 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2849 case MOVETYPE_FOLLOW:
2850 SV_Physics_Follow (ent);
2852 case MOVETYPE_NOCLIP:
2853 if (SV_RunThink(ent))
2856 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2857 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2862 SV_Physics_Step (ent);
2865 if (SV_RunThink (ent))
2869 case MOVETYPE_BOUNCE:
2870 case MOVETYPE_BOUNCEMISSILE:
2871 case MOVETYPE_FLYMISSILE:
2873 case MOVETYPE_FLY_WORLDONLY:
2875 if (SV_RunThink (ent))
2876 SV_Physics_Toss (ent);
2878 case MOVETYPE_PHYSICS:
2879 if (SV_RunThink(ent))
2882 SV_LinkEdict_TouchAreaGrid(ent);
2886 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2891 void SV_Physics_ClientMove(void)
2893 prvm_prog_t *prog = SVVM_prog;
2895 ent = host_client->edict;
2897 // call player physics, this needs the proper frametime
2898 PRVM_serverglobalfloat(frametime) = sv.frametime;
2901 // call standard client pre-think, with frametime = 0
2902 PRVM_serverglobalfloat(time) = sv.time;
2903 PRVM_serverglobalfloat(frametime) = 0;
2904 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2905 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2906 PRVM_serverglobalfloat(frametime) = sv.frametime;
2908 // make sure the velocity is sane (not a NaN)
2909 SV_CheckVelocity(ent);
2911 // perform MOVETYPE_WALK behavior
2914 // call standard player post-think, with frametime = 0
2915 PRVM_serverglobalfloat(time) = sv.time;
2916 PRVM_serverglobalfloat(frametime) = 0;
2917 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2918 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2919 PRVM_serverglobalfloat(frametime) = sv.frametime;
2921 if(PRVM_serveredictfloat(ent, fixangle))
2923 // angle fixing was requested by physics code...
2924 // so store the current angles for later use
2925 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2926 host_client->fixangle_angles_set = TRUE;
2928 // and clear fixangle for the next frame
2929 PRVM_serveredictfloat(ent, fixangle) = 0;
2933 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2935 prvm_prog_t *prog = SVVM_prog;
2936 // don't do physics on disconnected clients, FrikBot relies on this
2937 if (!host_client->begun)
2940 // make sure the velocity is sane (not a NaN)
2941 SV_CheckVelocity(ent);
2943 // don't run physics here if running asynchronously
2944 if (host_client->clmovement_inputtimeout <= 0)
2947 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2950 // make sure the velocity is still sane (not a NaN)
2951 SV_CheckVelocity(ent);
2953 // call standard client pre-think
2954 PRVM_serverglobalfloat(time) = sv.time;
2955 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2956 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2958 // make sure the velocity is still sane (not a NaN)
2959 SV_CheckVelocity(ent);
2962 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2964 prvm_prog_t *prog = SVVM_prog;
2965 // don't do physics on disconnected clients, FrikBot relies on this
2966 if (!host_client->begun)
2969 // make sure the velocity is sane (not a NaN)
2970 SV_CheckVelocity(ent);
2972 // call standard player post-think
2973 PRVM_serverglobalfloat(time) = sv.time;
2974 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2975 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2977 // make sure the velocity is still sane (not a NaN)
2978 SV_CheckVelocity(ent);
2980 if(PRVM_serveredictfloat(ent, fixangle))
2982 // angle fixing was requested by physics code...
2983 // so store the current angles for later use
2984 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2985 host_client->fixangle_angles_set = TRUE;
2987 // and clear fixangle for the next frame
2988 PRVM_serveredictfloat(ent, fixangle) = 0;
2991 // decrement the countdown variable used to decide when to go back to
2992 // synchronous physics
2993 if (host_client->clmovement_inputtimeout > sv.frametime)
2994 host_client->clmovement_inputtimeout -= sv.frametime;
2996 host_client->clmovement_inputtimeout = 0;
2999 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3001 prvm_prog_t *prog = SVVM_prog;
3002 // don't do physics on disconnected clients, FrikBot relies on this
3003 if (!host_client->begun)
3005 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3009 // make sure the velocity is sane (not a NaN)
3010 SV_CheckVelocity(ent);
3012 switch ((int) PRVM_serveredictfloat(ent, movetype))
3015 case MOVETYPE_FAKEPUSH:
3016 SV_Physics_Pusher (ent);
3019 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3020 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3023 case MOVETYPE_FOLLOW:
3024 SV_Physics_Follow (ent);
3026 case MOVETYPE_NOCLIP:
3029 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3030 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3033 SV_Physics_Step (ent);
3037 // don't run physics here if running asynchronously
3038 if (host_client->clmovement_inputtimeout <= 0)
3042 case MOVETYPE_BOUNCE:
3043 case MOVETYPE_BOUNCEMISSILE:
3044 case MOVETYPE_FLYMISSILE:
3047 SV_Physics_Toss (ent);
3050 case MOVETYPE_FLY_WORLDONLY:
3054 case MOVETYPE_PHYSICS:
3058 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3062 SV_CheckVelocity (ent);
3065 SV_LinkEdict_TouchAreaGrid(ent);
3067 SV_CheckVelocity (ent);
3076 void SV_Physics (void)
3078 prvm_prog_t *prog = SVVM_prog;
3082 // let the progs know that a new frame has started
3083 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3084 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3085 PRVM_serverglobalfloat(time) = sv.time;
3086 PRVM_serverglobalfloat(frametime) = sv.frametime;
3087 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3089 // run physics engine
3090 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3093 // treat each object in turn
3096 // if force_retouch, relink all the entities
3097 if (PRVM_serverglobalfloat(force_retouch) > 0)
3098 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3099 if (!ent->priv.server->free)
3100 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3102 if (sv_gameplayfix_consistentplayerprethink.integer)
3104 // run physics on the client entities in 3 stages
3105 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3106 if (!ent->priv.server->free)
3107 SV_Physics_ClientEntity_PreThink(ent);
3109 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3110 if (!ent->priv.server->free)
3111 SV_Physics_ClientEntity(ent);
3113 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3114 if (!ent->priv.server->free)
3115 SV_Physics_ClientEntity_PostThink(ent);
3119 // run physics on the client entities
3120 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3122 if (!ent->priv.server->free)
3124 SV_Physics_ClientEntity_PreThink(ent);
3125 SV_Physics_ClientEntity(ent);
3126 SV_Physics_ClientEntity_PostThink(ent);
3131 // run physics on all the non-client entities
3132 if (!sv_freezenonclients.integer)
3134 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3135 if (!ent->priv.server->free)
3136 SV_Physics_Entity(ent);
3137 // make a second pass to see if any ents spawned this frame and make
3138 // sure they run their move/think
3139 if (sv_gameplayfix_delayprojectiles.integer < 0)
3140 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3141 if (!ent->priv.server->move && !ent->priv.server->free)
3142 SV_Physics_Entity(ent);
3145 if (PRVM_serverglobalfloat(force_retouch) > 0)
3146 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3148 // LordHavoc: endframe support
3149 if (PRVM_serverfunction(EndFrame))
3151 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3152 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3153 PRVM_serverglobalfloat(time) = sv.time;
3154 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3157 // decrement prog->num_edicts if the highest number entities died
3158 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3160 if (!sv_freezenonclients.integer)
3161 sv.time += sv.frametime;