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, int skipsupercontentsmask, int skipmaterialflagsmask)
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, skipsupercontentsmask, skipmaterialflagsmask);
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, skipsupercontentsmask, skipmaterialflagsmask);
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, skipsupercontentsmask, skipmaterialflagsmask, 0.0f);
236 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
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, int skipsupercontentsmask, int skipmaterialflagsmask, 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, skipsupercontentsmask, skipmaterialflagsmask);
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, skipsupercontentsmask, skipmaterialflagsmask, 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, skipsupercontentsmask, skipmaterialflagsmask, extend);
385 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 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, int skipsupercontentsmask, int skipmaterialflagsmask, 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, int skipsupercontentsmask, int skipmaterialflagsmask, 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, skipsupercontentsmask, skipmaterialflagsmask);
440 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 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, skipsupercontentsmask, skipmaterialflagsmask, 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, skipsupercontentsmask, skipmaterialflagsmask, extend);
563 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 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, int skipsupercontentsmask, int skipmaterialflagsmask)
575 prvm_prog_t *prog = SVVM_prog;
579 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
582 VectorCopy(trace.endpos, temp);
583 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask).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);
860 if (sv_legacy_bbox_expand.integer)
862 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
864 // to make items easier to pick up and allow them to be grabbed off
865 // of shelves, the abs sizes are expanded
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
886 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
887 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
889 World_LinkEdict(&sv.world, ent, mins, maxs);
893 ===============================================================================
897 ===============================================================================
902 SV_TestEntityPosition
904 returns true if the entity is in solid currently
907 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
909 prvm_prog_t *prog = SVVM_prog;
910 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
911 int skipsupercontentsmask = 0;
912 int skipmaterialflagsmask = 0;
913 vec3_t org, entorigin, entmins, entmaxs;
915 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
916 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
917 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
918 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
919 trace = SV_TraceBox(org, entmins, entmaxs, entorigin, ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
920 if (trace.startsupercontents & hitsupercontentsmask)
924 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
926 // q1bsp/hlbsp use hulls and if the entity does not exactly match
927 // a hull size it is incorrectly tested, so this code tries to
928 // 'fix' it slightly...
929 // FIXME: this breaks entities larger than the hull size
932 VectorAdd(org, entmins, m1);
933 VectorAdd(org, entmaxs, m2);
934 VectorSubtract(m2, m1, s);
935 #define EPSILON (1.0f / 32.0f)
936 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
937 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
938 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
939 for (i = 0;i < 8;i++)
941 v[0] = (i & 1) ? m2[0] : m1[0];
942 v[1] = (i & 2) ? m2[1] : m1[1];
943 v[2] = (i & 4) ? m2[2] : m1[2];
944 if (SV_PointSuperContents(v) & hitsupercontentsmask)
949 // if the trace found a better position for the entity, move it there
950 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
953 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
954 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
956 // verify if the endpos is REALLY outside solid
957 VectorCopy(trace.endpos, org);
958 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
960 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
962 VectorCopy(org, PRVM_serveredictvector(ent, origin));
968 // DRESK - Support for Entity Contents Transition Event
971 SV_CheckContentsTransition
973 returns true if entity had a valid contentstransition function call
976 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
978 prvm_prog_t *prog = SVVM_prog;
979 int bValidFunctionCall;
981 // Default Valid Function Call to False
982 bValidFunctionCall = false;
984 if(PRVM_serveredictfloat(ent, watertype) != nContents)
985 { // Changed Contents
986 // Acquire Contents Transition Function from QC
987 if(PRVM_serveredictfunction(ent, contentstransition))
988 { // Valid Function; Execute
989 // Assign Valid Function
990 bValidFunctionCall = true;
991 // Prepare Parameters (Original Contents, New Contents)
993 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
995 PRVM_G_FLOAT(OFS_PARM1) = nContents;
997 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
999 PRVM_serverglobalfloat(time) = sv.time;
1000 // Execute VM Function
1001 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1005 // Return if Function Call was Valid
1006 return bValidFunctionCall;
1015 void SV_CheckVelocity (prvm_edict_t *ent)
1017 prvm_prog_t *prog = SVVM_prog;
1024 for (i=0 ; i<3 ; i++)
1026 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1028 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1029 PRVM_serveredictvector(ent, velocity)[i] = 0;
1031 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1033 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1034 PRVM_serveredictvector(ent, origin)[i] = 0;
1038 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1039 // player_run/player_stand1 does not horribly malfunction if the
1040 // velocity becomes a denormalized float
1041 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0000001)
1042 VectorClear(PRVM_serveredictvector(ent, velocity));
1044 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1045 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1046 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1048 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1049 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1050 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1051 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1059 Runs thinking code if time. There is some play in the exact time the think
1060 function will be called, because it is called before any movement is done
1061 in a frame. Not used for pushmove objects, because they must be exact.
1062 Returns false if the entity removed itself.
1065 static qboolean SV_RunThink (prvm_edict_t *ent)
1067 prvm_prog_t *prog = SVVM_prog;
1070 // don't let things stay in the past.
1071 // it is possible to start that way by a trigger with a local time.
1072 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1075 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1077 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1078 PRVM_serveredictfloat(ent, nextthink) = 0;
1079 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1080 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1081 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1082 // mods often set nextthink to time to cause a think every frame,
1083 // we don't want to loop in that case, so exit if the new nextthink is
1084 // <= the time the qc was told, also exit if it is past the end of the
1086 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1089 return !ent->priv.server->free;
1096 Two entities have touched, so run their touch functions
1099 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1101 prvm_prog_t *prog = SVVM_prog;
1102 int restorevm_tempstringsbuf_cursize;
1103 int old_self, old_other;
1104 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1106 old_self = PRVM_serverglobaledict(self);
1107 old_other = PRVM_serverglobaledict(other);
1108 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1110 VM_SetTraceGlobals(prog, trace);
1112 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1114 PRVM_serverglobalfloat(time) = sv.time;
1115 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1116 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1117 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1120 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1122 PRVM_serverglobalfloat(time) = sv.time;
1123 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1124 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1125 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1126 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1127 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1128 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1129 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1130 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1131 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1132 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1133 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1136 PRVM_serverglobaledict(self) = old_self;
1137 PRVM_serverglobaledict(other) = old_other;
1138 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1146 Slide off of the impacting object
1147 returns the blocked flags (1 = floor, 2 = step / wall)
1150 #define STOP_EPSILON 0.1
1151 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1156 backoff = -DotProduct (in, normal) * overbounce;
1157 VectorMA(in, backoff, normal, out);
1159 for (i = 0;i < 3;i++)
1160 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1169 The basic solid body movement clip that slides along multiple planes
1170 Returns the clipflags if the velocity was modified (hit something solid)
1174 8 = teleported by touch method
1175 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1178 static float SV_Gravity (prvm_edict_t *ent);
1179 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink);
1180 #define MAX_CLIP_PLANES 5
1181 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float stepheight)
1183 prvm_prog_t *prog = SVVM_prog;
1184 int blocked, bumpcount;
1185 int i, j, numplanes;
1186 float d, time_left, gravity;
1187 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1188 prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
1197 VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1201 gravity = SV_Gravity(ent);
1203 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1205 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1206 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1208 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1213 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1214 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1217 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1219 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1222 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1223 if(!SV_PushEntity(&trace, ent, push, false))
1225 // we got teleported by a touch function
1226 // let's abort the move
1231 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
1232 // abort move if we're stuck in the world (and didn't make it out)
1233 if (trace.worldstartsolid && trace.allsolid)
1235 VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
1239 if (trace.fraction == 1)
1241 if (trace.plane.normal[2])
1243 if (trace.plane.normal[2] > 0.7)
1250 Con_Printf ("SV_FlyMove: !trace.ent");
1251 trace.ent = prog->edicts;
1254 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1255 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1258 else if (stepheight)
1260 // step - handle it immediately
1266 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1267 VectorSet(steppush, 0, 0, stepheight);
1268 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1269 if(!SV_PushEntity(&steptrace, ent, steppush, false))
1274 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1275 if(!SV_PushEntity(&steptrace2, ent, push, false))
1280 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1281 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1282 if(!SV_PushEntity(&steptrace3, ent, steppush, false))
1287 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1288 // accept the new position if it made some progress...
1289 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1291 //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]);
1293 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1294 time_left *= 1 - trace.fraction;
1300 //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]);
1301 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1306 // step - return it to caller
1308 // save the trace for player extrafriction
1310 VectorCopy(trace.plane.normal, stepnormal);
1312 if (trace.fraction >= 0.001)
1314 // actually covered some distance
1315 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1319 time_left *= 1 - trace.fraction;
1321 // clipped to another plane
1322 if (numplanes >= MAX_CLIP_PLANES)
1324 // this shouldn't really happen
1325 VectorClear(PRVM_serveredictvector(ent, velocity));
1331 for (i = 0;i < numplanes;i++)
1332 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1336 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1341 VectorCopy(trace.plane.normal, planes[numplanes]);
1344 // modify original_velocity so it parallels all of the clip planes
1345 for (i = 0;i < numplanes;i++)
1347 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1348 for (j = 0;j < numplanes;j++)
1353 if (DotProduct(new_velocity, planes[j]) < 0)
1363 // go along this plane
1364 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1368 // go along the crease
1371 VectorClear(PRVM_serveredictvector(ent, velocity));
1375 CrossProduct(planes[0], planes[1], dir);
1376 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1377 VectorNormalize(dir);
1378 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1379 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1382 // if current velocity is against the original velocity,
1383 // stop dead to avoid tiny occilations in sloping corners
1384 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1386 VectorClear(PRVM_serveredictvector(ent, velocity));
1391 //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]);
1394 if ((blocked & 1) == 0 && bumpcount > 1)
1396 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1397 // flag ONGROUND if there's ground under it
1398 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
1402 // LordHavoc: this came from QW and allows you to get out of water more easily
1403 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1404 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1408 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1410 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1411 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1424 static float SV_Gravity (prvm_edict_t *ent)
1426 prvm_prog_t *prog = SVVM_prog;
1429 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1432 return ent_gravity * sv_gravity.value * sv.frametime;
1437 ===============================================================================
1441 ===============================================================================
1444 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1446 prvm_prog_t *prog = SVVM_prog;
1450 vec3_t stuckmins, stuckmaxs;
1451 vec3_t goodmins, goodmaxs;
1455 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1456 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1457 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1458 VectorCopy(pivot, goodmins);
1459 VectorCopy(pivot, goodmaxs);
1460 for (bump = 0;bump < 6;bump++)
1462 int coord = 2-(bump >> 1);
1463 //int coord = (bump >> 1);
1464 int dir = (bump & 1);
1467 for(subbump = 0; ; ++subbump)
1469 VectorCopy(stuckorigin, testorigin);
1473 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1478 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1481 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1482 if (stucktrace.bmodelstartsolid)
1484 // BAD BAD, can't fix that
1488 if (stucktrace.fraction >= 1)
1493 // BAD BAD, can't fix that
1497 // we hit something... let's move out of it
1498 VectorSubtract(stucktrace.endpos, testorigin, move);
1499 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1500 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1504 Con_Printf("subbump: %d\n", subbump);
1510 goodmaxs[coord] = stuckmaxs[coord];
1515 goodmins[coord] = stuckmins[coord];
1520 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1525 qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1527 prvm_prog_t *prog = SVVM_prog;
1531 vec3_t stuckmins, stuckmaxs;
1533 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1534 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1535 separation = 0.0f; // when using hulls, it can not be enlarged
1536 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1537 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1538 stuckmins[0] -= separation;
1539 stuckmins[1] -= separation;
1540 stuckmins[2] -= separation;
1541 stuckmaxs[0] += separation;
1542 stuckmaxs[1] += separation;
1543 stuckmaxs[2] += separation;
1544 // first pass we try to get it out of brush entities
1545 // second pass we try to get it out of world only (can't win them all)
1546 for (pass = 0;pass < 2;pass++)
1548 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1549 for (bump = 0;bump < 10;bump++)
1551 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1552 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1554 // found a good location, use it
1555 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1558 nudge = -stucktrace.startdepth;
1559 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1569 Does not change the entities velocity at all
1570 The trace struct is filled with the trace that has been done.
1571 Returns true if the push did not result in the entity being teleported by QC code.
1574 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink)
1576 prvm_prog_t *prog = SVVM_prog;
1584 solid = (int)PRVM_serveredictfloat(ent, solid);
1585 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1586 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1587 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1589 // move start position out of solids
1590 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1592 SV_NudgeOutOfSolid(ent);
1595 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1596 VectorAdd(start, push, end);
1598 if (movetype == MOVETYPE_FLYMISSILE)
1599 type = MOVE_MISSILE;
1600 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1601 type = MOVE_WORLDONLY;
1602 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1603 type = MOVE_NOMONSTERS; // only clip against bmodels
1607 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1608 // fail the move if stuck in world
1609 if (trace->worldstartsolid)
1612 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1614 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1619 if(!trace->startsolid)
1620 if(SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), PRVM_serveredictvector(ent, origin), type, ent, SV_GenericHitSuperContentsMask(ent), 0).startsolid)
1622 Con_Printf("something eeeeevil happened\n");
1627 SV_LinkEdict_TouchAreaGrid(ent);
1629 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))))
1630 SV_Impact (ent, trace);
1632 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1634 ent->priv.required->mark = 0;
1637 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1639 ent->priv.required->mark = 0;
1644 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1656 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1658 prvm_prog_t *prog = SVVM_prog;
1660 int pusherowner, pusherprog;
1663 float savesolid, movetime2, pushltime;
1664 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1666 int numcheckentities;
1667 static prvm_edict_t *checkentities[MAX_EDICTS];
1668 dp_model_t *pushermodel;
1669 trace_t trace, trace2;
1670 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1671 static unsigned short moved_edicts[MAX_EDICTS];
1674 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])
1676 PRVM_serveredictfloat(pusher, ltime) += movetime;
1680 switch ((int) PRVM_serveredictfloat(pusher, solid))
1682 // LordHavoc: valid pusher types
1685 case SOLID_SLIDEBOX:
1686 case SOLID_CORPSE: // LordHavoc: this would be weird...
1688 // LordHavoc: no collisions
1691 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1692 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1693 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1694 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1695 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1696 PRVM_serveredictfloat(pusher, ltime) += movetime;
1697 SV_LinkEdict(pusher);
1700 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1703 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1704 if (index < 1 || index >= MAX_MODELS)
1706 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1709 pushermodel = SV_GetModelByIndex(index);
1710 pusherowner = PRVM_serveredictedict(pusher, owner);
1711 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1713 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1715 movetime2 = movetime;
1716 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1717 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1718 if (moveangle[0] || moveangle[2])
1720 for (i = 0;i < 3;i++)
1724 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1725 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1729 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1730 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1734 else if (moveangle[1])
1736 for (i = 0;i < 3;i++)
1740 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1741 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1745 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1746 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1752 for (i = 0;i < 3;i++)
1756 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1757 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1761 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1762 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1767 VectorNegate (moveangle, a);
1768 AngleVectorsFLU (a, forward, left, up);
1770 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1771 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1772 pushltime = PRVM_serveredictfloat(pusher, ltime);
1774 // move the pusher to its final position
1776 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1777 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1778 PRVM_serveredictfloat(pusher, ltime) += movetime;
1779 SV_LinkEdict(pusher);
1781 pushermodel = SV_GetModelFromEdict(pusher);
1782 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);
1783 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1785 savesolid = PRVM_serveredictfloat(pusher, solid);
1787 // see if any solid entities are inside the final position
1790 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1791 numcheckentities = 0;
1792 else // MOVETYPE_PUSH
1793 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1794 for (e = 0;e < numcheckentities;e++)
1796 prvm_edict_t *check = checkentities[e];
1797 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1802 case MOVETYPE_FOLLOW:
1803 case MOVETYPE_NOCLIP:
1804 case MOVETYPE_FLY_WORLDONLY:
1810 if (PRVM_serveredictedict(check, owner) == pusherprog)
1813 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1816 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1818 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1819 check->priv.server->waterposition_forceupdate = true;
1821 checkcontents = SV_GenericHitSuperContentsMask(check);
1823 // if the entity is standing on the pusher, it will definitely be moved
1824 // if the entity is not standing on the pusher, but is in the pusher's
1825 // final position, move it
1826 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1828 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1829 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1830 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1831 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1832 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1833 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, 0, 0, collision_extendmovelength.value);
1834 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1835 if (!trace.startsolid)
1837 //Con_Printf("- not in solid\n");
1842 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1843 //VectorClear(pivot);
1848 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1849 VectorAdd (org, pivot, org);
1850 org2[0] = DotProduct (org, forward);
1851 org2[1] = DotProduct (org, left);
1852 org2[2] = DotProduct (org, up);
1853 VectorSubtract (org2, org, move);
1854 VectorAdd (move, move1, move);
1857 VectorCopy (move1, move);
1859 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1861 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1862 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1863 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1865 // physics objects need better collisions than this code can do
1866 if (movetype == MOVETYPE_PHYSICS)
1868 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1869 SV_LinkEdict(check);
1870 SV_LinkEdict_TouchAreaGrid(check);
1874 // try moving the contacted entity
1875 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1876 if(!SV_PushEntity (&trace, check, move, true))
1878 // entity "check" got teleported
1879 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1880 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1881 continue; // pushed enough
1883 // FIXME: turn players specially
1884 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1885 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1886 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1888 // this trace.fraction < 1 check causes items to fall off of pushers
1889 // if they pass under or through a wall
1890 // the groundentity check causes items to fall off of ledges
1891 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1892 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1894 // if it is still inside the pusher, block
1895 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1896 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1897 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1898 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1899 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1900 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents, 0, 0, collision_extendmovelength.value);
1901 if (trace.startsolid)
1904 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1906 // hack to invoke all necessary movement triggers
1908 if(!SV_PushEntity(&trace2, check, move2, true))
1910 // entity "check" got teleported
1917 // still inside pusher, so it's really blocked
1920 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1922 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1925 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1926 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1930 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1931 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1932 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1933 SV_LinkEdict(pusher);
1935 // move back any entities we already moved
1936 for (i = 0;i < num_moved;i++)
1938 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1939 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1940 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1944 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1945 if (PRVM_serveredictfunction(pusher, blocked))
1947 PRVM_serverglobalfloat(time) = sv.time;
1948 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1949 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1950 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1955 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1956 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1957 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1966 static void SV_Physics_Pusher (prvm_edict_t *ent)
1968 prvm_prog_t *prog = SVVM_prog;
1969 float thinktime, oldltime, movetime;
1971 oldltime = PRVM_serveredictfloat(ent, ltime);
1973 thinktime = PRVM_serveredictfloat(ent, nextthink);
1974 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1976 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1981 movetime = sv.frametime;
1984 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
1985 SV_PushMove (ent, movetime);
1987 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
1989 PRVM_serveredictfloat(ent, nextthink) = 0;
1990 PRVM_serverglobalfloat(time) = sv.time;
1991 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1992 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1993 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1999 ===============================================================================
2003 ===============================================================================
2006 static float unstickoffsets[] =
2008 // poutting -/+z changes first as they are least weird
2023 typedef enum unstickresult_e
2031 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2033 prvm_prog_t *prog = SVVM_prog;
2036 // if not stuck in a bmodel, just return
2037 if (!SV_TestEntityPosition(ent, vec3_origin))
2038 return UNSTICK_GOOD;
2040 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2042 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2044 VectorCopy(unstickoffsets + i, offset);
2046 //SV_LinkEdict_TouchAreaGrid(ent);
2047 return UNSTICK_UNSTUCK;
2051 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2052 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2054 for(i = 2; i <= maxunstick; ++i)
2056 VectorClear(offset);
2058 if (!SV_TestEntityPosition(ent, offset))
2061 //SV_LinkEdict_TouchAreaGrid(ent);
2062 return UNSTICK_UNSTUCK;
2065 if (!SV_TestEntityPosition(ent, offset))
2068 //SV_LinkEdict_TouchAreaGrid(ent);
2069 return UNSTICK_UNSTUCK;
2073 return UNSTICK_STUCK;
2076 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2078 prvm_prog_t *prog = SVVM_prog;
2080 switch(SV_UnstickEntityReturnOffset(ent, offset))
2084 case UNSTICK_UNSTUCK:
2085 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]);
2088 if (developer_extra.integer)
2089 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2092 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2101 This is a big hack to try and fix the rare case of getting stuck in the world
2105 static void SV_CheckStuck (prvm_edict_t *ent)
2107 prvm_prog_t *prog = SVVM_prog;
2110 switch(SV_UnstickEntityReturnOffset(ent, offset))
2113 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2115 case UNSTICK_UNSTUCK:
2116 Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2119 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2120 if (!SV_TestEntityPosition(ent, offset))
2122 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2124 //SV_LinkEdict_TouchAreaGrid(ent);
2127 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2130 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2140 static qboolean SV_CheckWater (prvm_edict_t *ent)
2142 prvm_prog_t *prog = SVVM_prog;
2144 int nNativeContents;
2147 point[0] = PRVM_serveredictvector(ent, origin)[0];
2148 point[1] = PRVM_serveredictvector(ent, origin)[1];
2149 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2151 // DRESK - Support for Entity Contents Transition Event
2152 // NOTE: Some logic needed to be slightly re-ordered
2153 // to not affect performance and allow for the feature.
2155 // Acquire Super Contents Prior to Resets
2156 cont = SV_PointSuperContents(point);
2157 // Acquire Native Contents Here
2158 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(cont);
2160 // DRESK - Support for Entity Contents Transition Event
2161 if(PRVM_serveredictfloat(ent, watertype))
2162 // Entity did NOT Spawn; Check
2163 SV_CheckContentsTransition(ent, nNativeContents);
2166 PRVM_serveredictfloat(ent, waterlevel) = 0;
2167 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2168 cont = SV_PointSuperContents(point);
2169 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2171 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2172 PRVM_serveredictfloat(ent, waterlevel) = 1;
2173 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2174 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2176 PRVM_serveredictfloat(ent, waterlevel) = 2;
2177 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2178 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2179 PRVM_serveredictfloat(ent, waterlevel) = 3;
2183 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2192 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2194 prvm_prog_t *prog = SVVM_prog;
2196 vec3_t forward, into, side, v_angle;
2198 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2199 AngleVectors (v_angle, forward, NULL, NULL);
2200 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2202 // cut the tangential velocity
2203 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2204 VectorScale (stepnormal, i, into);
2205 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2206 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2207 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2213 =====================
2216 Player has come to a dead stop, possibly due to the problem with limited
2217 float precision at some angle joins in the BSP hull.
2219 Try fixing by pushing one pixel in each direction.
2221 This is a hack, but in the interest of good gameplay...
2222 ======================
2224 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2229 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2232 for (i=0 ; i<8 ; i++)
2234 // try pushing a little in an axial direction
2237 case 0: dir[0] = 2; dir[1] = 0; break;
2238 case 1: dir[0] = 0; dir[1] = 2; break;
2239 case 2: dir[0] = -2; dir[1] = 0; break;
2240 case 3: dir[0] = 0; dir[1] = -2; break;
2241 case 4: dir[0] = 2; dir[1] = 2; break;
2242 case 5: dir[0] = -2; dir[1] = 2; break;
2243 case 6: dir[0] = 2; dir[1] = -2; break;
2244 case 7: dir[0] = -2; dir[1] = -2; break;
2247 SV_PushEntity (&trace, ent, dir, false, true);
2249 // retry the original move
2250 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2251 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2252 PRVM_serveredictvector(ent, velocity)[2] = 0;
2253 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2255 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2256 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2258 Con_DPrint("TryUnstick - success.\n");
2262 // go back to the original pos and try again
2263 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2267 VectorClear (PRVM_serveredictvector(ent, velocity));
2268 Con_DPrint("TryUnstick - failure.\n");
2274 =====================
2277 Only used by players
2278 ======================
2280 static void SV_WalkMove (prvm_edict_t *ent)
2282 prvm_prog_t *prog = SVVM_prog;
2285 //int originalmove_clip;
2286 int originalmove_flags;
2287 int originalmove_groundentity;
2288 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2289 int skipsupercontentsmask = 0;
2290 int skipmaterialflagsmask = 0;
2292 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2293 trace_t downtrace, trace;
2294 qboolean applygravity;
2296 // if frametime is 0 (due to client sending the same timestamp twice),
2298 if (sv.frametime <= 0)
2301 if (sv_gameplayfix_unstickplayers.integer)
2302 SV_CheckStuck (ent);
2304 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2306 SV_CheckVelocity(ent);
2308 // do a regular slide move unless it looks like you ran into a step
2309 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2311 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2312 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2314 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2316 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2319 // only try this if there was no floor in the way in the trace (no,
2320 // this check seems to be not REALLY necessary, because if clip & 1,
2321 // our trace will hit that thing too)
2322 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2323 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2324 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2325 type = MOVE_MISSILE;
2326 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2327 type = MOVE_WORLDONLY;
2328 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2329 type = MOVE_NOMONSTERS; // only clip against bmodels
2332 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2333 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2334 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
2335 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2336 clip |= 1; // but we HAVE found a floor
2339 // if the move did not hit the ground at any point, we're not on ground
2341 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2343 SV_CheckVelocity(ent);
2345 SV_LinkEdict_TouchAreaGrid(ent);
2347 if(clip & 8) // teleport
2350 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2353 if (sv_nostep.integer)
2356 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2357 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2358 //originalmove_clip = clip;
2359 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2360 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2362 // if move didn't block on a step, return
2365 // if move was not trying to move into the step, return
2366 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2369 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2371 // return if gibbed by a trigger
2372 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2375 // return if attempting to jump while airborn (unless sv_jumpstep)
2376 if (!sv_jumpstep.integer)
2377 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2381 // try moving up and forward to go up a step
2382 // back to start pos
2383 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2384 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2387 VectorClear (upmove);
2388 upmove[2] = sv_stepheight.value;
2389 if(!SV_PushEntity(&trace, ent, upmove, true))
2391 // we got teleported when upstepping... must abort the move
2396 PRVM_serveredictvector(ent, velocity)[2] = 0;
2397 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 0);
2398 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2401 // we got teleported when upstepping... must abort the move
2402 // note that z velocity handling may not be what QC expects here, but we cannot help it
2406 SV_CheckVelocity(ent);
2408 SV_LinkEdict_TouchAreaGrid(ent);
2410 // check for stuckness, possibly due to the limited precision of floats
2411 // in the clipping hulls
2413 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2414 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2416 //Con_Printf("wall\n");
2417 // stepping up didn't make any progress, revert to original move
2418 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2419 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2420 //clip = originalmove_clip;
2421 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2422 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2423 // now try to unstick if needed
2424 //clip = SV_TryUnstick (ent, oldvel);
2428 //Con_Printf("step - ");
2430 // extra friction based on view angle
2431 if (clip & 2 && sv_wallfriction.integer)
2432 SV_WallFriction (ent, stepnormal);
2434 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2435 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))
2439 VectorClear (downmove);
2440 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2441 if(!SV_PushEntity (&downtrace, ent, downmove, true))
2443 // we got teleported when downstepping... must abort the move
2447 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2449 // this has been disabled so that you can't jump when you are stepping
2450 // up while already jumping (also known as the Quake2 double jump bug)
2452 // LordHavoc: disabled this check so you can walk on monsters/players
2453 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2455 //Con_Printf("onground\n");
2456 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2457 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2463 //Con_Printf("slope\n");
2464 // if the push down didn't end up on good ground, use the move without
2465 // the step up. This happens near wall / slope combinations, and can
2466 // cause the player to hop up higher on a slope too steep to climb
2467 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2468 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2469 //clip = originalmove_clip;
2470 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2471 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2474 SV_CheckVelocity(ent);
2476 SV_LinkEdict_TouchAreaGrid(ent);
2479 //============================================================================
2485 Entities that are "stuck" to another entity
2488 static void SV_Physics_Follow (prvm_edict_t *ent)
2490 prvm_prog_t *prog = SVVM_prog;
2491 vec3_t vf, vr, vu, angles, v;
2494 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2495 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2496 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])
2498 // quick case for no rotation
2499 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2503 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2504 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2505 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2506 AngleVectors (angles, vf, vr, vu);
2507 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];
2508 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];
2509 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];
2510 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2511 angles[1] = PRVM_serveredictvector(e, angles)[1];
2512 angles[2] = PRVM_serveredictvector(e, angles)[2];
2513 AngleVectors (angles, vf, vr, vu);
2514 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2515 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2516 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2518 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2520 //SV_LinkEdict_TouchAreaGrid(ent);
2524 ==============================================================================
2528 ==============================================================================
2533 SV_CheckWaterTransition
2537 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2540 prvm_prog_t *prog = SVVM_prog;
2541 // 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
2543 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2544 cont = Mod_Q1BSP_NativeContentsFromSuperContents(SV_PointSuperContents(entorigin));
2545 if (!PRVM_serveredictfloat(ent, watertype))
2547 // just spawned here
2548 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2550 PRVM_serveredictfloat(ent, watertype) = cont;
2551 PRVM_serveredictfloat(ent, waterlevel) = 1;
2555 // DRESK - Support for Entity Contents Transition Event
2556 // NOTE: Call here BEFORE updating the watertype below,
2557 // and suppress watersplash sound if a valid function
2558 // call was made to allow for custom "splash" sounds.
2559 else if( !SV_CheckContentsTransition(ent, cont) )
2560 { // Contents Transition Function Invalid; Potentially Play Water Sound
2561 // check if the entity crossed into or out of water
2562 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2563 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2566 if (cont <= CONTENTS_WATER)
2568 PRVM_serveredictfloat(ent, watertype) = cont;
2569 PRVM_serveredictfloat(ent, waterlevel) = 1;
2573 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2574 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2582 Toss, bounce, and fly movement. When onground, do nothing.
2586 void SV_Physics_Toss (prvm_edict_t *ent)
2588 prvm_prog_t *prog = SVVM_prog;
2593 prvm_edict_t *groundentity;
2594 float d, ent_gravity;
2598 // if onground, return without moving
2599 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2601 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2602 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2604 // don't stick to ground if onground and moving upward
2605 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2607 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2609 // we can trust FL_ONGROUND if groundentity is world because it never moves
2612 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2614 // if ent was supported by a brush model on previous frame,
2615 // and groundentity is now freed, set groundentity to 0 (world)
2616 // which leaves it suspended in the air
2617 PRVM_serveredictedict(ent, groundentity) = 0;
2618 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2621 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2623 // don't slide if still touching the groundentity
2627 ent->priv.server->suspendedinairflag = false;
2629 SV_CheckVelocity (ent);
2632 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2633 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2636 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2638 movetime = sv.frametime;
2639 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2642 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2643 if(!SV_PushEntity(&trace, ent, move, true))
2644 return; // teleported
2645 if (ent->priv.server->free)
2647 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2649 // try to unstick the entity
2650 SV_UnstickEntity(ent);
2651 if(!SV_PushEntity(&trace, ent, move, true))
2652 return; // teleported
2653 if (ent->priv.server->free)
2656 if (trace.fraction == 1)
2658 movetime *= 1 - min(1, trace.fraction);
2659 switch((int)PRVM_serveredictfloat(ent, movetype))
2661 case MOVETYPE_BOUNCEMISSILE:
2662 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2664 bouncefactor = 1.0f;
2666 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2667 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2668 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2671 case MOVETYPE_BOUNCE:
2672 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2674 bouncefactor = 0.5f;
2676 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2678 bouncestop = 60.0f / 800.0f;
2680 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2681 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2684 // LordHavoc: fixed grenades not bouncing when fired down a slope
2685 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2686 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2688 d = PRVM_serveredictvector(ent, velocity)[2];
2689 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2691 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2692 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2693 VectorClear(PRVM_serveredictvector(ent, velocity));
2694 VectorClear(PRVM_serveredictvector(ent, avelocity));
2699 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2700 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2705 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2706 if (trace.plane.normal[2] > 0.7)
2708 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2709 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2710 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2711 ent->priv.server->suspendedinairflag = true;
2712 VectorClear (PRVM_serveredictvector(ent, velocity));
2713 VectorClear (PRVM_serveredictvector(ent, avelocity));
2718 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2719 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2726 // check for in water
2727 SV_CheckWaterTransition (ent);
2731 ===============================================================================
2735 ===============================================================================
2742 Monsters freefall when they don't have a ground entity, otherwise
2743 all movement is done with discrete steps.
2745 This is also used for objects that have become still on the ground, but
2746 will fall if the floor is pulled out from under them.
2749 static void SV_Physics_Step (prvm_edict_t *ent)
2751 prvm_prog_t *prog = SVVM_prog;
2752 int flags = (int)PRVM_serveredictfloat(ent, flags);
2755 // Backup Velocity in the event that movetypesteplandevent is called,
2756 // to provide a parameter with the entity's velocity at impact.
2757 vec3_t backupVelocity;
2758 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2759 // don't fall at all if fly/swim
2760 if (!(flags & (FL_FLY | FL_SWIM)))
2762 if (flags & FL_ONGROUND)
2764 // freefall if onground and moving upward
2765 // freefall if not standing on a world surface (it may be a lift or trap door)
2766 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2768 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2769 SV_CheckVelocity(ent);
2770 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2772 SV_LinkEdict_TouchAreaGrid(ent);
2773 ent->priv.server->waterposition_forceupdate = true;
2778 // freefall if not onground
2779 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2781 SV_CheckVelocity(ent);
2782 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2784 SV_LinkEdict_TouchAreaGrid(ent);
2787 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2789 // DRESK - Check for Entity Land Event Function
2790 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2791 { // Valid Function; Execute
2792 // Prepare Parameters
2793 // Assign Velocity at Impact
2794 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2795 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2796 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2798 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2800 PRVM_serverglobalfloat(time) = sv.time;
2801 // Execute VM Function
2802 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2805 // Check for Engine Landing Sound
2806 if(sv_sound_land.string)
2807 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2809 ent->priv.server->waterposition_forceupdate = true;
2814 //============================================================================
2816 static void SV_Physics_Entity (prvm_edict_t *ent)
2818 prvm_prog_t *prog = SVVM_prog;
2819 // don't run think/move on newly spawned projectiles as it messes up
2820 // movement interpolation and rocket trails, and is inconsistent with
2821 // respect to entities spawned in the same frame
2822 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2823 // but if it spawns a lower numbered ent, it doesn't - this never moves
2824 // ents in the first frame regardless)
2825 qboolean runmove = ent->priv.server->move;
2826 ent->priv.server->move = true;
2827 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2829 switch ((int) PRVM_serveredictfloat(ent, movetype))
2832 case MOVETYPE_FAKEPUSH:
2833 SV_Physics_Pusher (ent);
2836 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2837 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2840 case MOVETYPE_FOLLOW:
2841 if(SV_RunThink(ent))
2842 SV_Physics_Follow (ent);
2844 case MOVETYPE_NOCLIP:
2845 if (SV_RunThink(ent))
2848 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2849 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2854 SV_Physics_Step (ent);
2856 if (SV_RunThink(ent))
2857 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2859 ent->priv.server->waterposition_forceupdate = false;
2860 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2861 SV_CheckWaterTransition(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 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2888 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2893 static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
2895 prvm_prog_t *prog = SVVM_prog;
2897 // don't run think at all, that is done during server frames
2898 // instead, call the movetypes directly so they match client input
2900 // This probably only makes sense for CSQC-networked (SendEntity field set) player entities
2901 switch ((int) PRVM_serveredictfloat(ent, movetype))
2904 case MOVETYPE_FAKEPUSH:
2905 // push physics relies heavily on think times and calls, and so cannot be predicted currently
2906 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2910 case MOVETYPE_FOLLOW:
2911 SV_Physics_Follow (ent);
2913 case MOVETYPE_NOCLIP:
2914 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2915 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2918 SV_Physics_Step (ent);
2924 case MOVETYPE_BOUNCE:
2925 case MOVETYPE_BOUNCEMISSILE:
2926 case MOVETYPE_FLYMISSILE:
2927 SV_Physics_Toss (ent);
2930 case MOVETYPE_FLY_WORLDONLY:
2933 case MOVETYPE_PHYSICS:
2936 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2938 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2943 void SV_Physics_ClientMove(void)
2945 prvm_prog_t *prog = SVVM_prog;
2947 ent = host_client->edict;
2949 // call player physics, this needs the proper frametime
2950 PRVM_serverglobalfloat(frametime) = sv.frametime;
2953 // call standard client pre-think, with frametime = 0
2954 PRVM_serverglobalfloat(time) = sv.time;
2955 PRVM_serverglobalfloat(frametime) = 0;
2956 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2957 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2958 PRVM_serverglobalfloat(frametime) = sv.frametime;
2960 // make sure the velocity is sane (not a NaN)
2961 SV_CheckVelocity(ent);
2963 // perform movetype behaviour
2964 // note: will always be MOVETYPE_WALK if disableclientprediction = 0
2965 SV_Physics_ClientEntity_NoThink (ent);
2967 // call standard player post-think, with frametime = 0
2968 PRVM_serverglobalfloat(time) = sv.time;
2969 PRVM_serverglobalfloat(frametime) = 0;
2970 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2971 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2972 PRVM_serverglobalfloat(frametime) = sv.frametime;
2974 if(PRVM_serveredictfloat(ent, fixangle))
2976 // angle fixing was requested by physics code...
2977 // so store the current angles for later use
2978 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2979 host_client->fixangle_angles_set = TRUE;
2981 // and clear fixangle for the next frame
2982 PRVM_serveredictfloat(ent, fixangle) = 0;
2986 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2988 prvm_prog_t *prog = SVVM_prog;
2989 // don't do physics on disconnected clients, FrikBot relies on this
2990 if (!host_client->begun)
2993 // make sure the velocity is sane (not a NaN)
2994 SV_CheckVelocity(ent);
2996 // don't run physics here if running asynchronously
2997 if (host_client->clmovement_inputtimeout <= 0)
3000 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
3003 // make sure the velocity is still sane (not a NaN)
3004 SV_CheckVelocity(ent);
3006 // call standard client pre-think
3007 PRVM_serverglobalfloat(time) = sv.time;
3008 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3009 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3011 // make sure the velocity is still sane (not a NaN)
3012 SV_CheckVelocity(ent);
3015 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3017 prvm_prog_t *prog = SVVM_prog;
3018 // don't do physics on disconnected clients, FrikBot relies on this
3019 if (!host_client->begun)
3022 // make sure the velocity is sane (not a NaN)
3023 SV_CheckVelocity(ent);
3025 // call standard player post-think
3026 PRVM_serverglobalfloat(time) = sv.time;
3027 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3028 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3030 // make sure the velocity is still sane (not a NaN)
3031 SV_CheckVelocity(ent);
3033 if(PRVM_serveredictfloat(ent, fixangle))
3035 // angle fixing was requested by physics code...
3036 // so store the current angles for later use
3037 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3038 host_client->fixangle_angles_set = TRUE;
3040 // and clear fixangle for the next frame
3041 PRVM_serveredictfloat(ent, fixangle) = 0;
3044 // decrement the countdown variable used to decide when to go back to
3045 // synchronous physics
3046 if (host_client->clmovement_inputtimeout > sv.frametime)
3047 host_client->clmovement_inputtimeout -= sv.frametime;
3049 host_client->clmovement_inputtimeout = 0;
3052 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3054 prvm_prog_t *prog = SVVM_prog;
3055 // don't do physics on disconnected clients, FrikBot relies on this
3056 if (!host_client->begun)
3058 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3062 // make sure the velocity is sane (not a NaN)
3063 SV_CheckVelocity(ent);
3065 switch ((int) PRVM_serveredictfloat(ent, movetype))
3068 case MOVETYPE_FAKEPUSH:
3069 SV_Physics_Pusher (ent);
3072 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3073 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3076 case MOVETYPE_FOLLOW:
3078 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3079 SV_Physics_Follow (ent);
3081 case MOVETYPE_NOCLIP:
3083 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3086 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3087 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3091 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3092 SV_Physics_Step (ent);
3093 if (SV_RunThink(ent))
3094 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
3096 ent->priv.server->waterposition_forceupdate = false;
3097 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
3098 SV_CheckWaterTransition(ent);
3103 // don't run physics here if running asynchronously
3104 if (host_client->clmovement_inputtimeout <= 0)
3108 case MOVETYPE_BOUNCE:
3109 case MOVETYPE_BOUNCEMISSILE:
3110 case MOVETYPE_FLYMISSILE:
3113 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3114 SV_Physics_Toss (ent);
3117 case MOVETYPE_FLY_WORLDONLY:
3119 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3122 case MOVETYPE_PHYSICS:
3126 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
3128 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3132 SV_CheckVelocity (ent);
3135 SV_LinkEdict_TouchAreaGrid(ent);
3137 SV_CheckVelocity (ent);
3146 void SV_Physics (void)
3148 prvm_prog_t *prog = SVVM_prog;
3152 // let the progs know that a new frame has started
3153 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3154 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3155 PRVM_serverglobalfloat(time) = sv.time;
3156 PRVM_serverglobalfloat(frametime) = sv.frametime;
3157 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3159 // run physics engine
3160 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3163 // treat each object in turn
3166 // if force_retouch, relink all the entities
3167 if (PRVM_serverglobalfloat(force_retouch) > 0)
3168 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3169 if (!ent->priv.server->free)
3170 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3172 if (sv_gameplayfix_consistentplayerprethink.integer)
3174 // run physics on the client entities in 3 stages
3175 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3176 if (!ent->priv.server->free)
3177 SV_Physics_ClientEntity_PreThink(ent);
3179 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3180 if (!ent->priv.server->free)
3181 SV_Physics_ClientEntity(ent);
3183 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3184 if (!ent->priv.server->free)
3185 SV_Physics_ClientEntity_PostThink(ent);
3189 // run physics on the client entities
3190 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3192 if (!ent->priv.server->free)
3194 SV_Physics_ClientEntity_PreThink(ent);
3195 SV_Physics_ClientEntity(ent);
3196 SV_Physics_ClientEntity_PostThink(ent);
3201 // run physics on all the non-client entities
3202 if (!sv_freezenonclients.integer)
3204 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3205 if (!ent->priv.server->free)
3206 SV_Physics_Entity(ent);
3207 // make a second pass to see if any ents spawned this frame and make
3208 // sure they run their move/think
3209 if (sv_gameplayfix_delayprojectiles.integer < 0)
3210 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3211 if (!ent->priv.server->move && !ent->priv.server->free)
3212 SV_Physics_Entity(ent);
3215 if (PRVM_serverglobalfloat(force_retouch) > 0)
3216 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3218 // LordHavoc: endframe support
3219 if (PRVM_serverfunction(EndFrame))
3221 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3222 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3223 PRVM_serverglobalfloat(time) = sv.time;
3224 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3227 // decrement prog->num_edicts if the highest number entities died
3228 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3230 if (!sv_freezenonclients.integer)
3231 sv.time += sv.frametime;