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];
131 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
133 VectorCopy(start, clipstart);
134 VectorClear(clipmins2);
135 VectorClear(clipmaxs2);
136 #if COLLISIONPARANOID >= 3
137 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
141 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
142 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
143 if (cliptrace.startsolid || cliptrace.fraction < 1)
144 cliptrace.ent = prog->edicts;
145 if (type == MOVE_WORLDONLY)
148 if (type == MOVE_MISSILE)
150 // LadyHavoc: modified this, was = -15, now -= 15
151 for (i = 0;i < 3;i++)
158 // create the bounding box of the entire move
159 for (i = 0;i < 3;i++)
161 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
162 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
165 // debug override to test against everything
166 if (sv_debugmove.integer)
168 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = (vec_t)-999999999;
169 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = (vec_t)999999999;
172 // if the passedict is world, make it NULL (to avoid two checks each time)
173 if (passedict == prog->edicts)
175 // precalculate prog value for passedict for comparisons
176 passedictprog = PRVM_EDICT_TO_PROG(passedict);
177 // precalculate passedict's owner edict pointer for comparisons
178 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
180 clipgroup = passedict ? (int)PRVM_serveredictfloat(passedict, clipgroup) : 0;
183 // because this uses World_EntitiestoBox, we know all entity boxes overlap
184 // the clip region, so we can skip culling checks in the loop below
185 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
186 if (numtouchedicts > MAX_EDICTS)
188 // this never happens
189 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
190 numtouchedicts = MAX_EDICTS;
192 for (i = 0;i < numtouchedicts;i++)
194 touch = touchedicts[i];
196 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
198 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
203 // don't clip against self
204 if (passedict == touch)
206 // don't clip owned entities against owner
207 if (traceowner == touch)
209 // don't clip owner against owned entities
210 if (passedictprog == PRVM_serveredictedict(touch, owner))
212 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
213 if (clipgroup && clipgroup == (int)PRVM_serveredictfloat(touch, clipgroup))
215 // don't clip points against points (they can't collide)
216 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
220 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
222 // might interact, so do an exact clip
224 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
226 model = SV_GetModelFromEdict(touch);
227 pitchsign = SV_GetPitchSign(prog, touch);
230 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);
232 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
233 Matrix4x4_Invert_Simple(&imatrix, &matrix);
234 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
235 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
236 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
237 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
238 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
239 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
240 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);
242 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
244 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
256 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)
258 prvm_prog_t *prog = SVVM_prog;
259 int i, bodysupercontents;
262 prvm_edict_t *traceowner, *touch;
264 // temporary storage because prvm_vec_t may differ from vec_t
265 vec3_t touchmins, touchmaxs;
266 // bounding box of entire move area
267 vec3_t clipboxmins, clipboxmaxs;
268 // size when clipping against monsters
269 vec3_t clipmins2, clipmaxs2;
270 // start and end origin of move
271 vec3_t clipstart, clipend;
274 // matrices to transform into/out of other entity's space
275 matrix4x4_t matrix, imatrix;
276 // model of other entity
278 // list of entities to test for collisions
280 static prvm_edict_t *touchedicts[MAX_EDICTS];
282 if (VectorCompare(start, end))
283 return SV_TracePoint(start, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
285 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
287 VectorCopy(start, clipstart);
288 VectorCopy(end, clipend);
289 VectorClear(clipmins2);
290 VectorClear(clipmaxs2);
291 #if COLLISIONPARANOID >= 3
292 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
296 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, false);
297 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
298 if (cliptrace.startsolid || cliptrace.fraction < 1)
299 cliptrace.ent = prog->edicts;
300 if (type == MOVE_WORLDONLY)
303 if (type == MOVE_MISSILE)
305 // LadyHavoc: modified this, was = -15, now -= 15
306 for (i = 0;i < 3;i++)
313 // create the bounding box of the entire move
314 for (i = 0;i < 3;i++)
316 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
317 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
320 // debug override to test against everything
321 if (sv_debugmove.integer)
323 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = (vec_t)-999999999;
324 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = (vec_t)999999999;
327 // if the passedict is world, make it NULL (to avoid two checks each time)
328 if (passedict == prog->edicts)
330 // precalculate prog value for passedict for comparisons
331 passedictprog = PRVM_EDICT_TO_PROG(passedict);
332 // precalculate passedict's owner edict pointer for comparisons
333 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
335 clipgroup = passedict ? (int)PRVM_serveredictfloat(passedict, clipgroup) : 0;
338 // because this uses World_EntitiestoBox, we know all entity boxes overlap
339 // the clip region, so we can skip culling checks in the loop below
340 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
341 if (numtouchedicts > MAX_EDICTS)
343 // this never happens
344 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
345 numtouchedicts = MAX_EDICTS;
347 for (i = 0;i < numtouchedicts;i++)
349 touch = touchedicts[i];
351 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
353 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
358 // don't clip against self
359 if (passedict == touch)
361 // don't clip owned entities against owner
362 if (traceowner == touch)
364 // don't clip owner against owned entities
365 if (passedictprog == PRVM_serveredictedict(touch, owner))
367 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
368 if (clipgroup && clipgroup == (int)PRVM_serveredictfloat(touch, clipgroup))
370 // don't clip points against points (they can't collide)
371 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
375 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
377 // might interact, so do an exact clip
379 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
381 model = SV_GetModelFromEdict(touch);
382 pitchsign = SV_GetPitchSign(prog, touch);
385 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);
387 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
388 Matrix4x4_Invert_Simple(&imatrix, &matrix);
389 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
390 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
391 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
392 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
393 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
394 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
395 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);
397 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, false);
399 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
411 #if COLLISIONPARANOID >= 1
412 static 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)
414 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)
417 prvm_prog_t *prog = SVVM_prog;
418 vec3_t hullmins, hullmaxs;
419 int i, bodysupercontents;
423 prvm_edict_t *traceowner, *touch;
425 // temporary storage because prvm_vec_t may differ from vec_t
426 vec3_t touchmins, touchmaxs;
427 // bounding box of entire move area
428 vec3_t clipboxmins, clipboxmaxs;
429 // size of the moving object
430 vec3_t clipmins, clipmaxs;
431 // size when clipping against monsters
432 vec3_t clipmins2, clipmaxs2;
433 // start and end origin of move
434 vec3_t clipstart, clipend;
437 // matrices to transform into/out of other entity's space
438 matrix4x4_t matrix, imatrix;
439 // model of other entity
441 // list of entities to test for collisions
443 static prvm_edict_t *touchedicts[MAX_EDICTS];
445 if (VectorCompare(mins, maxs))
447 vec3_t shiftstart, shiftend;
448 VectorAdd(start, mins, shiftstart);
449 VectorAdd(end, mins, shiftend);
450 if (VectorCompare(start, end))
451 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
453 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
454 VectorSubtract(trace.endpos, mins, trace.endpos);
458 VectorCopy(start, clipstart);
459 VectorCopy(end, clipend);
460 VectorCopy(mins, clipmins);
461 VectorCopy(maxs, clipmaxs);
462 VectorCopy(mins, clipmins2);
463 VectorCopy(maxs, clipmaxs2);
464 #if COLLISIONPARANOID >= 3
465 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
469 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
470 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
471 if (cliptrace.startsolid || cliptrace.fraction < 1)
472 cliptrace.ent = prog->edicts;
473 if (type == MOVE_WORLDONLY)
476 if (type == MOVE_MISSILE)
478 // LadyHavoc: modified this, was = -15, now -= 15
479 for (i = 0;i < 3;i++)
486 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
487 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
488 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
491 VectorCopy(clipmins, hullmins);
492 VectorCopy(clipmaxs, hullmaxs);
495 // create the bounding box of the entire move
496 for (i = 0;i < 3;i++)
498 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
499 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
502 // debug override to test against everything
503 if (sv_debugmove.integer)
505 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = (vec_t)-999999999;
506 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = (vec_t)999999999;
509 // if the passedict is world, make it NULL (to avoid two checks each time)
510 if (passedict == prog->edicts)
512 // precalculate prog value for passedict for comparisons
513 passedictprog = PRVM_EDICT_TO_PROG(passedict);
514 // figure out whether this is a point trace for comparisons
515 pointtrace = VectorCompare(clipmins, clipmaxs);
516 // precalculate passedict's owner edict pointer for comparisons
517 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
519 clipgroup = passedict ? (int)PRVM_serveredictfloat(passedict, clipgroup) : 0;
522 // because this uses World_EntitiestoBox, we know all entity boxes overlap
523 // the clip region, so we can skip culling checks in the loop below
524 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
525 if (numtouchedicts > MAX_EDICTS)
527 // this never happens
528 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
529 numtouchedicts = MAX_EDICTS;
531 for (i = 0;i < numtouchedicts;i++)
533 touch = touchedicts[i];
535 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
537 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
542 // don't clip against self
543 if (passedict == touch)
545 // don't clip owned entities against owner
546 if (traceowner == touch)
548 // don't clip owner against owned entities
549 if (passedictprog == PRVM_serveredictedict(touch, owner))
551 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
552 if (clipgroup && clipgroup == (int)PRVM_serveredictfloat(touch, clipgroup))
554 // don't clip points against points (they can't collide)
555 if (pointtrace && VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
559 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
561 // might interact, so do an exact clip
563 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
565 model = SV_GetModelFromEdict(touch);
566 pitchsign = SV_GetPitchSign(prog, touch);
569 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);
571 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
572 Matrix4x4_Invert_Simple(&imatrix, &matrix);
573 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
574 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
575 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
576 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
577 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
578 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
579 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);
581 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);
583 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
590 #if COLLISIONPARANOID >= 1
591 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)
593 prvm_prog_t *prog = SVVM_prog;
597 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
600 VectorCopy(trace.endpos, temp);
601 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend).startsolid;
602 #if COLLISIONPARANOID < 3
603 if (trace.startsolid || endstuck)
605 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" : "");
611 int SV_PointSuperContents(const vec3_t point)
613 prvm_prog_t *prog = SVVM_prog;
614 int supercontents = 0;
618 // matrices to transform into/out of other entity's space
619 matrix4x4_t matrix, imatrix;
620 // model of other entity
623 // list of entities to test for collisions
625 static prvm_edict_t *touchedicts[MAX_EDICTS];
627 // get world supercontents at this point
628 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
629 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
631 // if sv_gameplayfix_swiminbmodels is off we're done
632 if (!sv_gameplayfix_swiminbmodels.integer)
633 return supercontents;
635 // get list of entities at this point
636 numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
637 if (numtouchedicts > MAX_EDICTS)
639 // this never happens
640 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
641 numtouchedicts = MAX_EDICTS;
643 for (i = 0;i < numtouchedicts;i++)
645 touch = touchedicts[i];
647 // we only care about SOLID_BSP for pointcontents
648 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
651 // might interact, so do an exact clip
652 model = SV_GetModelFromEdict(touch);
653 if (!model || !model->PointSuperContents)
655 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);
656 Matrix4x4_Invert_Simple(&imatrix, &matrix);
657 Matrix4x4_Transform(&imatrix, point, transformed);
658 frame = (int)PRVM_serveredictfloat(touch, frame);
659 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
662 return supercontents;
666 ===============================================================================
668 Linking entities into the world culling system
670 ===============================================================================
673 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
675 prvm_prog_t *prog = SVVM_prog;
676 vec3_t paddedmins, paddedmaxs;
677 if (maxedicts < 1 || resultedicts == NULL)
679 // LadyHavoc: discovered this actually causes its own bugs (dm6 teleporters being too close to info_teleport_destination)
680 //VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
681 //VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
682 VectorCopy(mins, paddedmins);
683 VectorCopy(maxs, paddedmaxs);
684 if (sv_areadebug.integer)
686 int numresultedicts = 0;
689 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
691 ed = PRVM_EDICT_NUM(edictindex);
692 if (!ed->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
694 resultedicts[numresultedicts++] = ed;
695 if (numresultedicts == maxedicts)
699 return numresultedicts;
702 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
705 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
707 prvm_prog_t *prog = SVVM_prog;
708 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
709 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
710 PRVM_serverglobalfloat(time) = sv.time;
711 PRVM_serverglobalfloat(trace_allsolid) = false;
712 PRVM_serverglobalfloat(trace_startsolid) = false;
713 PRVM_serverglobalfloat(trace_fraction) = 1;
714 PRVM_serverglobalfloat(trace_inwater) = false;
715 PRVM_serverglobalfloat(trace_inopen) = true;
716 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
717 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
718 PRVM_serverglobalfloat(trace_plane_dist) = 0;
719 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
720 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
721 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
722 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
723 PRVM_serverglobalstring(trace_dphittexturename) = 0;
724 prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
727 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
729 prvm_prog_t *prog = SVVM_prog;
730 int i, numtouchedicts, old_self, old_other;
732 static prvm_edict_t *touchedicts[MAX_EDICTS];
734 if (ent == prog->edicts)
735 return; // don't add the world
740 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
743 // build a list of edicts to touch, because the link loop can be corrupted
744 // by IncreaseEdicts called during touch functions
745 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
746 if (numtouchedicts > MAX_EDICTS)
748 // this never happens
749 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
750 numtouchedicts = MAX_EDICTS;
753 old_self = PRVM_serverglobaledict(self);
754 old_other = PRVM_serverglobaledict(other);
755 for (i = 0;i < numtouchedicts;i++)
757 touch = touchedicts[i];
758 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
760 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
763 PRVM_serverglobaledict(self) = old_self;
764 PRVM_serverglobaledict(other) = old_other;
767 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
771 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
773 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
774 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
775 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[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]; }
778 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
779 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]; }
780 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]; }
781 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
782 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]; }
783 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]; }
784 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
785 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]; }
786 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]; }
787 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
788 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]; }
789 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]; }
790 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
791 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]; }
792 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]; }
793 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
794 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]; }
795 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]; }
804 void SV_LinkEdict (prvm_edict_t *ent)
806 prvm_prog_t *prog = SVVM_prog;
808 vec3_t mins, maxs, entmins, entmaxs, entangles;
811 if (ent == prog->edicts)
812 return; // don't add the world
817 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
818 if (modelindex < 0 || modelindex >= MAX_MODELS)
820 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
823 model = SV_GetModelByIndex(modelindex);
825 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
826 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
827 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
831 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
833 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
834 // TODO special handling for spheres?
835 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
836 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
837 VectorCopy(PRVM_serveredictvector(ent, angles), entangles);
838 RotateBBox(entmins, entmaxs, entangles, mins, maxs);
839 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
840 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
842 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
846 if (!model->TraceBox)
847 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
849 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
851 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
852 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
854 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
856 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
857 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
861 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
862 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
867 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
868 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
869 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
874 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
875 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
878 if (sv_legacy_bbox_expand.integer)
880 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
882 // to make items easier to pick up and allow them to be grabbed off
883 // of shelves, the abs sizes are expanded
893 // because movement is clipped an epsilon away from an actual edge,
894 // we must fully check even when bounding boxes don't quite touch
904 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
905 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
907 World_LinkEdict(&sv.world, ent, mins, maxs, sv_areagrid_link_SOLID_NOT.integer);
911 ===============================================================================
915 ===============================================================================
920 SV_TestEntityPosition
922 returns true if the entity is in solid currently
925 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
927 prvm_prog_t *prog = SVVM_prog;
928 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
929 int skipsupercontentsmask = 0;
930 int skipmaterialflagsmask = 0;
931 vec3_t org, entorigin, entmins, entmaxs;
933 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
934 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
935 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
936 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
937 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);
938 if (trace.startsupercontents & hitsupercontentsmask)
942 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
944 // q1bsp/hlbsp use hulls and if the entity does not exactly match
945 // a hull size it is incorrectly tested, so this code tries to
946 // 'fix' it slightly...
947 // FIXME: this breaks entities larger than the hull size
950 VectorAdd(org, entmins, m1);
951 VectorAdd(org, entmaxs, m2);
952 VectorSubtract(m2, m1, s);
953 #define EPSILON (1.0f / 32.0f)
954 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
955 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
956 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
957 for (i = 0;i < 8;i++)
959 v[0] = (i & 1) ? m2[0] : m1[0];
960 v[1] = (i & 2) ? m2[1] : m1[1];
961 v[2] = (i & 4) ? m2[2] : m1[2];
962 if (SV_PointSuperContents(v) & hitsupercontentsmask)
967 // if the trace found a better position for the entity, move it there
968 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
971 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
972 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
974 // verify if the endpos is REALLY outside solid
975 VectorCopy(trace.endpos, org);
976 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
978 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
980 VectorCopy(org, PRVM_serveredictvector(ent, origin));
986 // DRESK - Support for Entity Contents Transition Event
989 SV_CheckContentsTransition
991 returns true if entity had a valid contentstransition function call
994 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
996 prvm_prog_t *prog = SVVM_prog;
997 int bValidFunctionCall;
999 // Default Valid Function Call to False
1000 bValidFunctionCall = false;
1002 if(PRVM_serveredictfloat(ent, watertype) != nContents)
1003 { // Changed Contents
1004 // Acquire Contents Transition Function from QC
1005 if(PRVM_serveredictfunction(ent, contentstransition))
1006 { // Valid Function; Execute
1007 // Assign Valid Function
1008 bValidFunctionCall = true;
1009 // Prepare Parameters (Original Contents, New Contents)
1010 // Original Contents
1011 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
1013 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1015 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1017 PRVM_serverglobalfloat(time) = sv.time;
1018 // Execute VM Function
1019 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1023 // Return if Function Call was Valid
1024 return bValidFunctionCall;
1033 void SV_CheckVelocity (prvm_edict_t *ent)
1035 prvm_prog_t *prog = SVVM_prog;
1042 for (i=0 ; i<3 ; i++)
1044 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1046 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1047 PRVM_serveredictvector(ent, velocity)[i] = 0;
1049 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1051 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1052 PRVM_serveredictvector(ent, origin)[i] = 0;
1056 // LadyHavoc: a hack to ensure that the (rather silly) id1 quakec
1057 // player_run/player_stand1 does not horribly malfunction if the
1058 // velocity becomes a denormalized float
1059 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0000001)
1060 VectorClear(PRVM_serveredictvector(ent, velocity));
1062 // LadyHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1063 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1064 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1066 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1067 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1068 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1069 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1077 Runs thinking code if time. There is some play in the exact time the think
1078 function will be called, because it is called before any movement is done
1079 in a frame. Not used for pushmove objects, because they must be exact.
1080 Returns false if the entity removed itself.
1083 static qbool SV_RunThink (prvm_edict_t *ent)
1085 prvm_prog_t *prog = SVVM_prog;
1088 // don't let things stay in the past.
1089 // it is possible to start that way by a trigger with a local time.
1090 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1093 for (iterations = 0;iterations < 128 && !ent->free;iterations++)
1095 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1096 PRVM_serveredictfloat(ent, nextthink) = 0;
1097 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1098 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1099 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1100 // mods often set nextthink to time to cause a think every frame,
1101 // we don't want to loop in that case, so exit if the new nextthink is
1102 // <= the time the qc was told, also exit if it is past the end of the
1104 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1114 Two entities have touched, so run their touch functions
1117 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1119 prvm_prog_t *prog = SVVM_prog;
1120 int restorevm_tempstringsbuf_cursize;
1121 int old_self, old_other;
1122 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1124 old_self = PRVM_serverglobaledict(self);
1125 old_other = PRVM_serverglobaledict(other);
1126 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1128 VM_SetTraceGlobals(prog, trace);
1130 if (!e1->free && !e2->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1132 PRVM_serverglobalfloat(time) = sv.time;
1133 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1134 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1135 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1138 if (!e1->free && !e2->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1140 PRVM_serverglobalfloat(time) = sv.time;
1141 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1142 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1143 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1144 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1145 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1146 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1147 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1148 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1149 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1150 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1151 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1154 PRVM_serverglobaledict(self) = old_self;
1155 PRVM_serverglobaledict(other) = old_other;
1156 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1164 Slide off of the impacting object
1165 returns the blocked flags (1 = floor, 2 = step / wall)
1168 #define STOP_EPSILON 0.1
1169 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1174 backoff = -DotProduct (in, normal) * overbounce;
1175 VectorMA(in, backoff, normal, out);
1177 for (i = 0;i < 3;i++)
1178 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1187 The basic solid body movement clip that slides along multiple planes
1188 Returns the clipflags if the velocity was modified (hit something solid)
1192 8 = teleported by touch method
1193 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1196 static float SV_Gravity (prvm_edict_t *ent);
1197 static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink);
1198 #define MAX_CLIP_PLANES 5
1199 static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float stepheight)
1201 prvm_prog_t *prog = SVVM_prog;
1202 int blocked, bumpcount;
1203 int i, j, numplanes;
1204 float d, time_left, gravity;
1205 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1206 prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
1215 VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1219 gravity = SV_Gravity(ent);
1221 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1223 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1224 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1226 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1231 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1232 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1235 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1237 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1240 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1241 if(!SV_PushEntity(&trace, ent, push, false))
1243 // we got teleported by a touch function
1244 // let's abort the move
1249 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
1250 // abort move if we're stuck in the world (and didn't make it out)
1251 if (trace.worldstartsolid && trace.allsolid)
1253 VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
1257 if (trace.fraction == 1)
1260 time_left *= 1 - trace.fraction;
1262 if (trace.plane.normal[2])
1264 if (trace.plane.normal[2] > 0.7)
1271 Con_Printf ("SV_FlyMove: !trace.ent");
1272 trace.ent = prog->edicts;
1275 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1276 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1279 else if (stepheight)
1281 // step - handle it immediately
1287 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1288 VectorSet(steppush, 0, 0, stepheight);
1289 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1290 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1291 if(!SV_PushEntity(&steptrace, ent, steppush, false))
1296 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1297 if(!SV_PushEntity(&steptrace2, ent, push, false))
1302 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1303 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1304 if(!SV_PushEntity(&steptrace3, ent, steppush, false))
1309 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1310 // accept the new position if it made some progress...
1311 // previously this checked if absolute distance >= 0.03125 which made stepping up unreliable
1312 if (PRVM_serveredictvector(ent, origin)[0] - org[0] || PRVM_serveredictvector(ent, origin)[1] - org[1])
1314 //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]);
1316 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1317 time_left *= 1 - trace.fraction;
1323 //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]);
1324 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1329 // step - return it to caller
1331 // save the trace for player extrafriction
1333 VectorCopy(trace.plane.normal, stepnormal);
1335 if (trace.fraction >= 0.001)
1337 // actually covered some distance
1338 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1342 // clipped to another plane
1343 if (numplanes >= MAX_CLIP_PLANES)
1345 // this shouldn't really happen
1346 VectorClear(PRVM_serveredictvector(ent, velocity));
1352 for (i = 0;i < numplanes;i++)
1353 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1357 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1362 VectorCopy(trace.plane.normal, planes[numplanes]);
1365 // modify original_velocity so it parallels all of the clip planes
1366 for (i = 0;i < numplanes;i++)
1368 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1369 for (j = 0;j < numplanes;j++)
1374 if (DotProduct(new_velocity, planes[j]) < 0)
1384 // go along this plane
1385 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1389 // go along the crease
1392 VectorClear(PRVM_serveredictvector(ent, velocity));
1396 CrossProduct(planes[0], planes[1], dir);
1397 // LadyHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1398 VectorNormalize(dir);
1399 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1400 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1403 // if current velocity is against the original velocity,
1404 // stop dead to avoid tiny occilations in sloping corners
1405 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1407 VectorClear(PRVM_serveredictvector(ent, velocity));
1412 //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]);
1415 if ((blocked & 1) == 0 && bumpcount > 1)
1417 // LadyHavoc: fix the 'fall to your death in a wedge corner' glitch
1418 // flag ONGROUND if there's ground under it
1419 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
1423 // LadyHavoc: this came from QW and allows you to get out of water more easily
1424 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1425 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1429 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1431 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1432 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1445 static float SV_Gravity (prvm_edict_t *ent)
1447 prvm_prog_t *prog = SVVM_prog;
1450 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1453 return ent_gravity * sv_gravity.value * sv.frametime;
1458 ===============================================================================
1462 ===============================================================================
1465 static qbool SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1467 prvm_prog_t *prog = SVVM_prog;
1471 vec3_t stuckmins, stuckmaxs;
1472 vec3_t goodmins, goodmaxs;
1476 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1477 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1478 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1479 VectorCopy(pivot, goodmins);
1480 VectorCopy(pivot, goodmaxs);
1481 for (bump = 0;bump < 6;bump++)
1483 int coord = 2-(bump >> 1);
1484 //int coord = (bump >> 1);
1485 int dir = (bump & 1);
1488 for(subbump = 0; ; ++subbump)
1490 VectorCopy(stuckorigin, testorigin);
1494 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1499 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1502 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1503 if (stucktrace.bmodelstartsolid)
1505 // BAD BAD, can't fix that
1509 if (stucktrace.fraction >= 1)
1514 // BAD BAD, can't fix that
1518 // we hit something... let's move out of it
1519 VectorSubtract(stucktrace.endpos, testorigin, move);
1520 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1521 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1525 Con_Printf("subbump: %d\n", subbump);
1531 goodmaxs[coord] = stuckmaxs[coord];
1536 goodmins[coord] = stuckmins[coord];
1541 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1546 qbool SV_NudgeOutOfSolid(prvm_edict_t *ent)
1548 prvm_prog_t *prog = SVVM_prog;
1552 vec3_t stuckmins, stuckmaxs;
1554 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1555 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1556 separation = 0.0f; // when using hulls, it can not be enlarged
1557 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1558 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1559 stuckmins[0] -= separation;
1560 stuckmins[1] -= separation;
1561 stuckmins[2] -= separation;
1562 stuckmaxs[0] += separation;
1563 stuckmaxs[1] += separation;
1564 stuckmaxs[2] += separation;
1565 // first pass we try to get it out of brush entities
1566 // second pass we try to get it out of world only (can't win them all)
1567 for (pass = 0;pass < 2;pass++)
1569 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1570 for (bump = 0;bump < 10;bump++)
1572 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1573 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1575 // found a good location, use it
1576 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1579 nudge = -stucktrace.startdepth;
1580 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1590 Does not change the entities velocity at all
1591 The trace struct is filled with the trace that has been done.
1592 Returns true if the push did not result in the entity being teleported by QC code.
1595 static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink)
1597 prvm_prog_t *prog = SVVM_prog;
1605 solid = (int)PRVM_serveredictfloat(ent, solid);
1606 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1607 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1608 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1610 // move start position out of solids
1611 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1613 SV_NudgeOutOfSolid(ent);
1616 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1617 VectorAdd(start, push, end);
1619 if (movetype == MOVETYPE_FLYMISSILE)
1620 type = MOVE_MISSILE;
1621 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1622 type = MOVE_WORLDONLY;
1623 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1624 type = MOVE_NOMONSTERS; // only clip against bmodels
1628 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1629 // fail the move if stuck in world
1630 if (trace->worldstartsolid)
1633 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1635 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1640 if(!trace->startsolid)
1641 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)
1643 Con_Printf("something eeeeevil happened\n");
1648 SV_LinkEdict_TouchAreaGrid(ent);
1650 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))))
1651 SV_Impact (ent, trace);
1653 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1655 ent->priv.required->mark = 0;
1658 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1660 ent->priv.required->mark = 0;
1665 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1677 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1679 prvm_prog_t *prog = SVVM_prog;
1681 int pusherowner, pusherprog;
1684 float savesolid, movetime2, pushltime;
1685 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1687 int numcheckentities;
1688 static prvm_edict_t *checkentities[MAX_EDICTS];
1689 model_t *pushermodel;
1690 trace_t trace, trace2;
1691 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1692 static unsigned short moved_edicts[MAX_EDICTS];
1695 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])
1697 PRVM_serveredictfloat(pusher, ltime) += movetime;
1701 switch ((int) PRVM_serveredictfloat(pusher, solid))
1703 // LadyHavoc: valid pusher types
1706 case SOLID_SLIDEBOX:
1707 case SOLID_CORPSE: // LadyHavoc: this would be weird...
1709 // LadyHavoc: no collisions
1712 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1713 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1714 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1715 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1716 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1717 PRVM_serveredictfloat(pusher, ltime) += movetime;
1718 SV_LinkEdict(pusher);
1721 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1724 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1725 if (index < 1 || index >= MAX_MODELS)
1727 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1730 pushermodel = SV_GetModelByIndex(index);
1731 pusherowner = PRVM_serveredictedict(pusher, owner);
1732 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1734 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1736 movetime2 = movetime;
1737 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1738 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1739 if (moveangle[0] || moveangle[2])
1741 for (i = 0;i < 3;i++)
1745 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1746 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1750 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1751 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1755 else if (moveangle[1])
1757 for (i = 0;i < 3;i++)
1761 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1762 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1766 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1767 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1773 for (i = 0;i < 3;i++)
1777 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1778 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1782 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1783 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1788 VectorNegate (moveangle, a);
1789 AngleVectorsFLU (a, forward, left, up);
1791 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1792 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1793 pushltime = PRVM_serveredictfloat(pusher, ltime);
1795 // move the pusher to its final position
1797 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1798 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1799 PRVM_serveredictfloat(pusher, ltime) += movetime;
1800 SV_LinkEdict(pusher);
1802 pushermodel = SV_GetModelFromEdict(pusher);
1803 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);
1804 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1806 savesolid = PRVM_serveredictfloat(pusher, solid);
1808 // see if any solid entities are inside the final position
1811 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1812 numcheckentities = 0;
1813 else // MOVETYPE_PUSH
1814 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1815 for (e = 0;e < numcheckentities;e++)
1817 prvm_edict_t *check = checkentities[e];
1818 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1823 case MOVETYPE_FOLLOW:
1824 case MOVETYPE_NOCLIP:
1825 case MOVETYPE_FLY_WORLDONLY:
1831 if (PRVM_serveredictedict(check, owner) == pusherprog)
1834 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1837 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1839 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1840 check->priv.server->waterposition_forceupdate = true;
1842 checkcontents = SV_GenericHitSuperContentsMask(check);
1844 // if the entity is standing on the pusher, it will definitely be moved
1845 // if the entity is not standing on the pusher, but is in the pusher's
1846 // final position, move it
1847 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1849 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1850 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1851 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1852 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1853 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1854 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);
1855 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1856 if (!trace.startsolid)
1858 //Con_Printf("- not in solid\n");
1863 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1864 //VectorClear(pivot);
1869 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1870 VectorAdd (org, pivot, org);
1871 org2[0] = DotProduct (org, forward);
1872 org2[1] = DotProduct (org, left);
1873 org2[2] = DotProduct (org, up);
1874 VectorSubtract (org2, org, move);
1875 VectorAdd (move, move1, move);
1878 VectorCopy (move1, move);
1880 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1882 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1883 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1884 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1886 // physics objects need better collisions than this code can do
1887 if (movetype == MOVETYPE_PHYSICS)
1889 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1890 SV_LinkEdict(check);
1891 SV_LinkEdict_TouchAreaGrid(check);
1895 // try moving the contacted entity
1896 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1897 if(!SV_PushEntity (&trace, check, move, true))
1899 // entity "check" got teleported
1900 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1901 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1902 continue; // pushed enough
1904 // FIXME: turn players specially
1905 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1906 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1907 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1909 // this trace.fraction < 1 check causes items to fall off of pushers
1910 // if they pass under or through a wall
1911 // the groundentity check causes items to fall off of ledges
1912 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1913 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1915 // if it is still inside the pusher, block
1916 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1917 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1918 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1919 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1920 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1921 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);
1922 if (trace.startsolid)
1925 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1927 // hack to invoke all necessary movement triggers
1929 if(!SV_PushEntity(&trace2, check, move2, true))
1931 // entity "check" got teleported
1938 // still inside pusher, so it's really blocked
1941 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1943 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1946 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1947 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1951 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1952 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1953 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1954 SV_LinkEdict(pusher);
1956 // move back any entities we already moved
1957 for (i = 0;i < num_moved;i++)
1959 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1960 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1961 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1965 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1966 if (PRVM_serveredictfunction(pusher, blocked))
1968 PRVM_serverglobalfloat(time) = sv.time;
1969 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1970 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1971 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1976 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1977 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1978 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1987 static void SV_Physics_Pusher (prvm_edict_t *ent)
1989 prvm_prog_t *prog = SVVM_prog;
1990 prvm_vec_t thinktime, oldltime, movetime;
1992 oldltime = PRVM_serveredictfloat(ent, ltime);
1994 thinktime = PRVM_serveredictfloat(ent, nextthink);
1995 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1997 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
2002 movetime = sv.frametime;
2005 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
2006 SV_PushMove (ent, movetime);
2008 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
2010 PRVM_serveredictfloat(ent, nextthink) = 0;
2011 PRVM_serverglobalfloat(time) = sv.time;
2012 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2013 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
2014 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2020 ===============================================================================
2024 ===============================================================================
2027 static float unstickoffsets[] =
2029 // poutting -/+z changes first as they are least weird
2044 typedef enum unstickresult_e
2052 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2054 prvm_prog_t *prog = SVVM_prog;
2057 // if not stuck in a bmodel, just return
2058 if (!SV_TestEntityPosition(ent, vec3_origin))
2059 return UNSTICK_GOOD;
2061 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2063 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2065 VectorCopy(unstickoffsets + i, offset);
2067 //SV_LinkEdict_TouchAreaGrid(ent);
2068 return UNSTICK_UNSTUCK;
2072 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2073 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2075 for(i = 2; i <= maxunstick; ++i)
2077 VectorClear(offset);
2079 if (!SV_TestEntityPosition(ent, offset))
2082 //SV_LinkEdict_TouchAreaGrid(ent);
2083 return UNSTICK_UNSTUCK;
2086 if (!SV_TestEntityPosition(ent, offset))
2089 //SV_LinkEdict_TouchAreaGrid(ent);
2090 return UNSTICK_UNSTUCK;
2094 return UNSTICK_STUCK;
2097 qbool SV_UnstickEntity (prvm_edict_t *ent)
2099 prvm_prog_t *prog = SVVM_prog;
2101 switch(SV_UnstickEntityReturnOffset(ent, offset))
2105 case UNSTICK_UNSTUCK:
2106 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]);
2109 if (developer_extra.integer)
2110 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2113 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2122 This is a big hack to try and fix the rare case of getting stuck in the world
2126 static void SV_CheckStuck (prvm_edict_t *ent)
2128 prvm_prog_t *prog = SVVM_prog;
2131 switch(SV_UnstickEntityReturnOffset(ent, offset))
2134 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2136 case UNSTICK_UNSTUCK:
2137 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]);
2140 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2141 if (!SV_TestEntityPosition(ent, offset))
2143 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)));
2145 //SV_LinkEdict_TouchAreaGrid(ent);
2148 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2151 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2161 static qbool SV_CheckWater (prvm_edict_t *ent)
2163 prvm_prog_t *prog = SVVM_prog;
2165 int nNativeContents;
2168 point[0] = PRVM_serveredictvector(ent, origin)[0];
2169 point[1] = PRVM_serveredictvector(ent, origin)[1];
2170 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2172 // DRESK - Support for Entity Contents Transition Event
2173 // NOTE: Some logic needed to be slightly re-ordered
2174 // to not affect performance and allow for the feature.
2176 // Acquire Super Contents Prior to Resets
2177 cont = SV_PointSuperContents(point);
2178 // Acquire Native Contents Here
2179 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(cont);
2181 // DRESK - Support for Entity Contents Transition Event
2182 if(PRVM_serveredictfloat(ent, watertype))
2183 // Entity did NOT Spawn; Check
2184 SV_CheckContentsTransition(ent, nNativeContents);
2187 PRVM_serveredictfloat(ent, waterlevel) = 0;
2188 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2189 cont = SV_PointSuperContents(point);
2190 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2192 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2193 PRVM_serveredictfloat(ent, waterlevel) = 1;
2194 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2195 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2197 PRVM_serveredictfloat(ent, waterlevel) = 2;
2198 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2199 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2200 PRVM_serveredictfloat(ent, waterlevel) = 3;
2204 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2213 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2215 prvm_prog_t *prog = SVVM_prog;
2217 vec3_t forward, into, side, v_angle;
2219 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2220 AngleVectors (v_angle, forward, NULL, NULL);
2221 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2223 // cut the tangential velocity
2224 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2225 VectorScale (stepnormal, i, into);
2226 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2227 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2228 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2234 =====================
2237 Player has come to a dead stop, possibly due to the problem with limited
2238 float precision at some angle joins in the BSP hull.
2240 Try fixing by pushing one pixel in each direction.
2242 This is a hack, but in the interest of good gameplay...
2243 ======================
2245 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2250 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2253 for (i=0 ; i<8 ; i++)
2255 // try pushing a little in an axial direction
2258 case 0: dir[0] = 2; dir[1] = 0; break;
2259 case 1: dir[0] = 0; dir[1] = 2; break;
2260 case 2: dir[0] = -2; dir[1] = 0; break;
2261 case 3: dir[0] = 0; dir[1] = -2; break;
2262 case 4: dir[0] = 2; dir[1] = 2; break;
2263 case 5: dir[0] = -2; dir[1] = 2; break;
2264 case 6: dir[0] = 2; dir[1] = -2; break;
2265 case 7: dir[0] = -2; dir[1] = -2; break;
2268 SV_PushEntity (&trace, ent, dir, false, true);
2270 // retry the original move
2271 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2272 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2273 PRVM_serveredictvector(ent, velocity)[2] = 0;
2274 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2276 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2277 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2279 Con_DPrint("TryUnstick - success.\n");
2283 // go back to the original pos and try again
2284 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2288 VectorClear (PRVM_serveredictvector(ent, velocity));
2289 Con_DPrint("TryUnstick - failure.\n");
2295 =====================
2298 Only used by players
2299 ======================
2301 static void SV_WalkMove (prvm_edict_t *ent)
2303 prvm_prog_t *prog = SVVM_prog;
2306 //int originalmove_clip;
2307 int originalmove_flags;
2308 int originalmove_groundentity;
2309 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2310 int skipsupercontentsmask = 0;
2311 int skipmaterialflagsmask = 0;
2313 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2314 trace_t downtrace, trace;
2317 // if frametime is 0 (due to client sending the same timestamp twice),
2319 if (sv.frametime <= 0)
2322 if (sv_gameplayfix_unstickplayers.integer)
2323 SV_CheckStuck (ent);
2325 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2327 SV_CheckVelocity(ent);
2329 // do a regular slide move unless it looks like you ran into a step
2330 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2332 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2333 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2335 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2337 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2340 // only try this if there was no floor in the way in the trace (no,
2341 // this check seems to be not REALLY necessary, because if clip & 1,
2342 // our trace will hit that thing too)
2343 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2344 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2345 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2346 type = MOVE_MISSILE;
2347 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2348 type = MOVE_WORLDONLY;
2349 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2350 type = MOVE_NOMONSTERS; // only clip against bmodels
2353 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2354 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2355 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
2356 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2358 clip |= 1; // but we HAVE found a floor
2359 // set groundentity so we get carried when walking onto a mover with sv_gameplayfix_nogravityonground
2360 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2364 // if the move did not hit the ground at any point, we're not on ground
2366 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2368 SV_CheckVelocity(ent);
2370 SV_LinkEdict_TouchAreaGrid(ent);
2372 if(clip & 8) // teleport
2375 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2378 if (sv_nostep.integer)
2381 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2382 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2383 //originalmove_clip = clip;
2384 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2385 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2387 // if move didn't block on a step, return
2390 // if move was not trying to move into the step, return
2391 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2394 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2396 // return if gibbed by a trigger
2397 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2400 // return if attempting to jump while airborn (unless sv_jumpstep)
2401 if (!sv_jumpstep.integer)
2402 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2406 // try moving up and forward to go up a step
2407 // back to start pos
2408 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2409 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2412 VectorClear (upmove);
2413 upmove[2] = sv_stepheight.value;
2414 if(!SV_PushEntity(&trace, ent, upmove, true))
2416 // we got teleported when upstepping... must abort the move
2421 PRVM_serveredictvector(ent, velocity)[2] = 0;
2422 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 0);
2423 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2426 // we got teleported when upstepping... must abort the move
2427 // note that z velocity handling may not be what QC expects here, but we cannot help it
2431 SV_CheckVelocity(ent);
2433 SV_LinkEdict_TouchAreaGrid(ent);
2435 // check for stuckness, possibly due to the limited precision of floats
2436 // in the clipping hulls
2438 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2439 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2441 //Con_Printf("wall\n");
2442 // stepping up didn't make any progress, revert to original move
2443 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2444 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2445 //clip = originalmove_clip;
2446 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2447 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2448 // now try to unstick if needed
2449 //clip = SV_TryUnstick (ent, oldvel);
2453 //Con_Printf("step - ");
2455 // extra friction based on view angle
2456 if (clip & 2 && sv_wallfriction.integer)
2457 SV_WallFriction (ent, stepnormal);
2459 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2460 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))
2464 VectorClear (downmove);
2465 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2466 if(!SV_PushEntity (&downtrace, ent, downmove, true))
2468 // we got teleported when downstepping... must abort the move
2472 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2474 // this has been disabled so that you can't jump when you are stepping
2475 // up while already jumping (also known as the Quake2 double jump bug)
2477 // LadyHavoc: disabled this check so you can walk on monsters/players
2478 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2480 //Con_Printf("onground\n");
2481 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2482 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2488 //Con_Printf("slope\n");
2489 // if the push down didn't end up on good ground, use the move without
2490 // the step up. This happens near wall / slope combinations, and can
2491 // cause the player to hop up higher on a slope too steep to climb
2492 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2493 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2494 //clip = originalmove_clip;
2495 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2496 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2499 SV_CheckVelocity(ent);
2501 SV_LinkEdict_TouchAreaGrid(ent);
2504 //============================================================================
2510 Entities that are "stuck" to another entity
2513 static void SV_Physics_Follow (prvm_edict_t *ent)
2515 prvm_prog_t *prog = SVVM_prog;
2516 vec3_t vf, vr, vu, angles, v;
2519 // LadyHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2520 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2521 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])
2523 // quick case for no rotation
2524 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2528 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2529 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2530 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2531 AngleVectors (angles, vf, vr, vu);
2532 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];
2533 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];
2534 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];
2535 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2536 angles[1] = PRVM_serveredictvector(e, angles)[1];
2537 angles[2] = PRVM_serveredictvector(e, angles)[2];
2538 AngleVectors (angles, vf, vr, vu);
2539 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2540 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2541 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2543 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2545 //SV_LinkEdict_TouchAreaGrid(ent);
2549 ==============================================================================
2553 ==============================================================================
2558 SV_CheckWaterTransition
2562 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2565 prvm_prog_t *prog = SVVM_prog;
2566 // LadyHavoc: 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
2568 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2569 cont = Mod_Q1BSP_NativeContentsFromSuperContents(SV_PointSuperContents(entorigin));
2570 if (!PRVM_serveredictfloat(ent, watertype))
2572 // just spawned here
2573 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2575 PRVM_serveredictfloat(ent, watertype) = cont;
2576 PRVM_serveredictfloat(ent, waterlevel) = 1;
2580 // DRESK - Support for Entity Contents Transition Event
2581 // NOTE: Call here BEFORE updating the watertype below,
2582 // and suppress watersplash sound if a valid function
2583 // call was made to allow for custom "splash" sounds.
2584 else if( !SV_CheckContentsTransition(ent, cont) )
2585 { // Contents Transition Function Invalid; Potentially Play Water Sound
2586 // check if the entity crossed into or out of water
2587 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2588 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2591 if (cont <= CONTENTS_WATER)
2593 PRVM_serveredictfloat(ent, watertype) = cont;
2594 PRVM_serveredictfloat(ent, waterlevel) = 1;
2598 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2599 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2607 Toss, bounce, and fly movement. When onground, do nothing.
2611 void SV_Physics_Toss (prvm_edict_t *ent)
2613 prvm_prog_t *prog = SVVM_prog;
2618 prvm_edict_t *groundentity;
2619 float d, ent_gravity;
2623 // if onground, return without moving
2624 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2626 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2627 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2629 // don't stick to ground if onground and moving upward
2630 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2632 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2634 // we can trust FL_ONGROUND if groundentity is world because it never moves
2637 else if (ent->priv.server->suspendedinairflag && groundentity->free)
2639 // if ent was supported by a brush model on previous frame,
2640 // and groundentity is now freed, set groundentity to 0 (world)
2641 // which leaves it suspended in the air
2642 PRVM_serveredictedict(ent, groundentity) = 0;
2643 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2646 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2648 // don't slide if still touching the groundentity
2652 ent->priv.server->suspendedinairflag = false;
2654 SV_CheckVelocity (ent);
2657 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2658 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2661 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2663 movetime = sv.frametime;
2664 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2667 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2668 if(!SV_PushEntity(&trace, ent, move, true))
2669 return; // teleported
2672 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2674 // try to unstick the entity
2675 SV_UnstickEntity(ent);
2676 if(!SV_PushEntity(&trace, ent, move, true))
2677 return; // teleported
2681 if (trace.fraction == 1)
2683 movetime *= 1 - min(1, trace.fraction);
2684 switch((int)PRVM_serveredictfloat(ent, movetype))
2686 case MOVETYPE_BOUNCEMISSILE:
2687 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2689 bouncefactor = 1.0f;
2691 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2692 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2693 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2696 case MOVETYPE_BOUNCE:
2697 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2699 bouncefactor = 0.5f;
2701 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2703 bouncestop = 60.0f / 800.0f;
2705 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2706 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2709 // LadyHavoc: fixed grenades not bouncing when fired down a slope
2710 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2711 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2713 d = PRVM_serveredictvector(ent, velocity)[2];
2714 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2716 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2717 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2718 VectorClear(PRVM_serveredictvector(ent, velocity));
2719 VectorClear(PRVM_serveredictvector(ent, avelocity));
2724 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2725 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2730 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2731 if (trace.plane.normal[2] > 0.7)
2733 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2734 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2735 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2736 ent->priv.server->suspendedinairflag = true;
2737 VectorClear (PRVM_serveredictvector(ent, velocity));
2738 VectorClear (PRVM_serveredictvector(ent, avelocity));
2743 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2744 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2751 // check for in water
2752 SV_CheckWaterTransition (ent);
2756 ===============================================================================
2760 ===============================================================================
2767 Monsters freefall when they don't have a ground entity, otherwise
2768 all movement is done with discrete steps.
2770 This is also used for objects that have become still on the ground, but
2771 will fall if the floor is pulled out from under them.
2774 static void SV_Physics_Step (prvm_edict_t *ent)
2776 prvm_prog_t *prog = SVVM_prog;
2777 int flags = (int)PRVM_serveredictfloat(ent, flags);
2780 // Backup Velocity in the event that movetypesteplandevent is called,
2781 // to provide a parameter with the entity's velocity at impact.
2782 vec3_t backupVelocity;
2783 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2784 // don't fall at all if fly/swim
2785 if (!(flags & (FL_FLY | FL_SWIM)))
2787 if (flags & FL_ONGROUND)
2789 // freefall if onground and moving upward
2790 // freefall if not standing on a world surface (it may be a lift or trap door)
2791 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2793 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2794 SV_CheckVelocity(ent);
2795 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2797 SV_LinkEdict_TouchAreaGrid(ent);
2798 ent->priv.server->waterposition_forceupdate = true;
2803 // freefall if not onground
2804 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2806 SV_CheckVelocity(ent);
2807 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2809 SV_LinkEdict_TouchAreaGrid(ent);
2812 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2814 // DRESK - Check for Entity Land Event Function
2815 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2816 { // Valid Function; Execute
2817 // Prepare Parameters
2818 // Assign Velocity at Impact
2819 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2820 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2821 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2823 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2825 PRVM_serverglobalfloat(time) = sv.time;
2826 // Execute VM Function
2827 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2830 // Check for Engine Landing Sound
2831 if(sv_sound_land.string)
2832 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2834 ent->priv.server->waterposition_forceupdate = true;
2839 //============================================================================
2841 static void SV_Physics_Entity (prvm_edict_t *ent)
2843 prvm_prog_t *prog = SVVM_prog;
2844 // don't run think/move on newly spawned projectiles as it messes up
2845 // movement interpolation and rocket trails, and is inconsistent with
2846 // respect to entities spawned in the same frame
2847 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2848 // but if it spawns a lower numbered ent, it doesn't - this never moves
2849 // ents in the first frame regardless)
2850 qbool runmove = ent->priv.server->move;
2851 ent->priv.server->move = true;
2852 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2854 switch ((int) PRVM_serveredictfloat(ent, movetype))
2857 case MOVETYPE_FAKEPUSH:
2858 SV_Physics_Pusher (ent);
2861 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2862 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2865 case MOVETYPE_FOLLOW:
2866 if(SV_RunThink(ent))
2867 SV_Physics_Follow (ent);
2869 case MOVETYPE_NOCLIP:
2870 if (SV_RunThink(ent))
2873 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2874 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2879 SV_Physics_Step (ent);
2881 if (SV_RunThink(ent))
2882 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2884 ent->priv.server->waterposition_forceupdate = false;
2885 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2886 SV_CheckWaterTransition(ent);
2890 if (SV_RunThink (ent))
2894 case MOVETYPE_BOUNCE:
2895 case MOVETYPE_BOUNCEMISSILE:
2896 case MOVETYPE_FLYMISSILE:
2898 case MOVETYPE_FLY_WORLDONLY:
2900 if (SV_RunThink (ent))
2901 SV_Physics_Toss (ent);
2903 case MOVETYPE_PHYSICS:
2904 if (SV_RunThink(ent))
2907 SV_LinkEdict_TouchAreaGrid(ent);
2911 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2913 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2918 static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
2920 prvm_prog_t *prog = SVVM_prog;
2922 // don't run think at all, that is done during server frames
2923 // instead, call the movetypes directly so they match client input
2925 // This probably only makes sense for CSQC-networked (SendEntity field set) player entities
2926 switch ((int) PRVM_serveredictfloat(ent, movetype))
2929 case MOVETYPE_FAKEPUSH:
2930 // push physics relies heavily on think times and calls, and so cannot be predicted currently
2931 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2935 case MOVETYPE_FOLLOW:
2936 SV_Physics_Follow (ent);
2938 case MOVETYPE_NOCLIP:
2939 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2940 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2943 SV_Physics_Step (ent);
2949 case MOVETYPE_BOUNCE:
2950 case MOVETYPE_BOUNCEMISSILE:
2951 case MOVETYPE_FLYMISSILE:
2952 SV_Physics_Toss (ent);
2955 case MOVETYPE_FLY_WORLDONLY:
2958 case MOVETYPE_PHYSICS:
2961 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2963 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2968 void SV_Physics_ClientMove(void)
2970 prvm_prog_t *prog = SVVM_prog;
2972 ent = host_client->edict;
2974 // call player physics, this needs the proper frametime
2975 PRVM_serverglobalfloat(frametime) = sv.frametime;
2978 // call standard client pre-think, with frametime = 0
2979 PRVM_serverglobalfloat(time) = sv.time;
2980 PRVM_serverglobalfloat(frametime) = 0;
2981 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2982 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2983 PRVM_serverglobalfloat(frametime) = sv.frametime;
2985 // make sure the velocity is sane (not a NaN)
2986 SV_CheckVelocity(ent);
2988 // perform movetype behaviour
2989 // note: will always be MOVETYPE_WALK if disableclientprediction = 0
2990 SV_Physics_ClientEntity_NoThink (ent);
2992 // call standard player post-think, with frametime = 0
2993 PRVM_serverglobalfloat(time) = sv.time;
2994 PRVM_serverglobalfloat(frametime) = 0;
2995 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2996 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2997 PRVM_serverglobalfloat(frametime) = sv.frametime;
2999 if(PRVM_serveredictfloat(ent, fixangle))
3001 // angle fixing was requested by physics code...
3002 // so store the current angles for later use
3003 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3004 host_client->fixangle_angles_set = true;
3006 // and clear fixangle for the next frame
3007 PRVM_serveredictfloat(ent, fixangle) = 0;
3011 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
3013 prvm_prog_t *prog = SVVM_prog;
3014 // don't do physics on disconnected clients, FrikBot relies on this
3015 if (!host_client->begun)
3018 // make sure the velocity is sane (not a NaN)
3019 SV_CheckVelocity(ent);
3021 // don't run physics here if running asynchronously
3022 if (host_client->clmovement_inputtimeout <= 0)
3025 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
3028 // make sure the velocity is still sane (not a NaN)
3029 SV_CheckVelocity(ent);
3031 // call standard client pre-think
3032 PRVM_serverglobalfloat(time) = sv.time;
3033 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3034 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3036 // make sure the velocity is still sane (not a NaN)
3037 SV_CheckVelocity(ent);
3040 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3042 prvm_prog_t *prog = SVVM_prog;
3043 // don't do physics on disconnected clients, FrikBot relies on this
3044 if (!host_client->begun)
3047 // make sure the velocity is sane (not a NaN)
3048 SV_CheckVelocity(ent);
3050 // call standard player post-think
3051 PRVM_serverglobalfloat(time) = sv.time;
3052 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3053 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3055 // make sure the velocity is still sane (not a NaN)
3056 SV_CheckVelocity(ent);
3058 if(PRVM_serveredictfloat(ent, fixangle))
3060 // angle fixing was requested by physics code...
3061 // so store the current angles for later use
3062 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3063 host_client->fixangle_angles_set = true;
3065 // and clear fixangle for the next frame
3066 PRVM_serveredictfloat(ent, fixangle) = 0;
3069 // decrement the countdown variable used to decide when to go back to
3070 // synchronous physics
3071 if (host_client->clmovement_inputtimeout > sv.frametime)
3072 host_client->clmovement_inputtimeout -= sv.frametime;
3074 host_client->clmovement_inputtimeout = 0;
3077 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3079 prvm_prog_t *prog = SVVM_prog;
3080 // don't do physics on disconnected clients, FrikBot relies on this
3081 if (!host_client->begun)
3083 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3087 // make sure the velocity is sane (not a NaN)
3088 SV_CheckVelocity(ent);
3090 switch ((int) PRVM_serveredictfloat(ent, movetype))
3093 case MOVETYPE_FAKEPUSH:
3094 SV_Physics_Pusher (ent);
3097 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3098 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3101 case MOVETYPE_FOLLOW:
3103 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3104 SV_Physics_Follow (ent);
3106 case MOVETYPE_NOCLIP:
3108 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3111 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3112 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3116 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3117 SV_Physics_Step (ent);
3118 if (SV_RunThink(ent))
3119 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
3121 ent->priv.server->waterposition_forceupdate = false;
3122 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
3123 SV_CheckWaterTransition(ent);
3128 // don't run physics here if running asynchronously
3129 if (host_client->clmovement_inputtimeout <= 0)
3133 case MOVETYPE_BOUNCE:
3134 case MOVETYPE_BOUNCEMISSILE:
3135 case MOVETYPE_FLYMISSILE:
3138 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3139 SV_Physics_Toss (ent);
3142 case MOVETYPE_FLY_WORLDONLY:
3144 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3147 case MOVETYPE_PHYSICS:
3151 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
3153 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3157 SV_CheckVelocity (ent);
3160 SV_LinkEdict_TouchAreaGrid(ent);
3162 SV_CheckVelocity (ent);
3171 void SV_Physics (void)
3173 prvm_prog_t *prog = SVVM_prog;
3177 // free memory for resources that are no longer referenced
3178 PRVM_GarbageCollection(prog);
3180 // let the progs know that a new frame has started
3181 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3182 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3183 PRVM_serverglobalfloat(time) = sv.time;
3184 PRVM_serverglobalfloat(frametime) = sv.frametime;
3185 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3188 // run physics engine
3189 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3193 // treat each object in turn
3196 // if force_retouch, relink all the entities
3197 if (PRVM_serverglobalfloat(force_retouch) > 0)
3198 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3200 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3202 if (sv_gameplayfix_consistentplayerprethink.integer)
3204 // run physics on the client entities in 3 stages
3205 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3207 SV_Physics_ClientEntity_PreThink(ent);
3209 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3211 SV_Physics_ClientEntity(ent);
3213 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3215 SV_Physics_ClientEntity_PostThink(ent);
3219 // run physics on the client entities
3220 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3224 SV_Physics_ClientEntity_PreThink(ent);
3225 SV_Physics_ClientEntity(ent);
3226 SV_Physics_ClientEntity_PostThink(ent);
3231 // run physics on all the non-client entities
3232 if (!sv_freezenonclients.integer)
3234 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3236 SV_Physics_Entity(ent);
3237 // make a second pass to see if any ents spawned this frame and make
3238 // sure they run their move/think
3239 if (sv_gameplayfix_delayprojectiles.integer < 0)
3240 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3241 if (!ent->priv.server->move && !ent->free)
3242 SV_Physics_Entity(ent);
3245 if (PRVM_serverglobalfloat(force_retouch) > 0)
3246 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3248 // LadyHavoc: endframe support
3249 if (PRVM_serverfunction(EndFrame))
3251 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3252 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3253 PRVM_serverglobalfloat(time) = sv.time;
3254 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3257 // decrement prog->num_edicts if the highest number entities died
3258 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3260 if (!sv_freezenonclients.integer)
3261 sv.time += sv.frametime;