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] = -999999999;
169 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 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] = -999999999;
324 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 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 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] = -999999999;
506 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 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)
593 prvm_prog_t *prog = SVVM_prog;
597 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
600 VectorCopy(trace.endpos, temp);
601 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask).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->priv.required->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
737 if (ent->priv.server->free)
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
814 if (ent->priv.server->free)
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);
879 // to make items easier to pick up and allow them to be grabbed off
880 // of shelves, the abs sizes are expanded
882 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
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
903 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
904 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
906 World_LinkEdict(&sv.world, ent, mins, maxs);
910 ===============================================================================
914 ===============================================================================
919 SV_TestEntityPosition
921 returns true if the entity is in solid currently
924 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
926 prvm_prog_t *prog = SVVM_prog;
927 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
928 int skipsupercontentsmask = 0;
929 int skipmaterialflagsmask = 0;
930 vec3_t org, entorigin, entmins, entmaxs;
932 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
933 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
934 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
935 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
936 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);
937 if (trace.startsupercontents & hitsupercontentsmask)
941 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
943 // q1bsp/hlbsp use hulls and if the entity does not exactly match
944 // a hull size it is incorrectly tested, so this code tries to
945 // 'fix' it slightly...
946 // FIXME: this breaks entities larger than the hull size
949 VectorAdd(org, entmins, m1);
950 VectorAdd(org, entmaxs, m2);
951 VectorSubtract(m2, m1, s);
952 #define EPSILON (1.0f / 32.0f)
953 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
954 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
955 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
956 for (i = 0;i < 8;i++)
958 v[0] = (i & 1) ? m2[0] : m1[0];
959 v[1] = (i & 2) ? m2[1] : m1[1];
960 v[2] = (i & 4) ? m2[2] : m1[2];
961 if (SV_PointSuperContents(v) & hitsupercontentsmask)
966 // if the trace found a better position for the entity, move it there
967 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
970 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
971 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
973 // verify if the endpos is REALLY outside solid
974 VectorCopy(trace.endpos, org);
975 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
977 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
979 VectorCopy(org, PRVM_serveredictvector(ent, origin));
985 // DRESK - Support for Entity Contents Transition Event
988 SV_CheckContentsTransition
990 returns true if entity had a valid contentstransition function call
993 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
995 prvm_prog_t *prog = SVVM_prog;
996 int bValidFunctionCall;
998 // Default Valid Function Call to False
999 bValidFunctionCall = false;
1001 if(PRVM_serveredictfloat(ent, watertype) != nContents)
1002 { // Changed Contents
1003 // Acquire Contents Transition Function from QC
1004 if(PRVM_serveredictfunction(ent, contentstransition))
1005 { // Valid Function; Execute
1006 // Assign Valid Function
1007 bValidFunctionCall = true;
1008 // Prepare Parameters (Original Contents, New Contents)
1009 // Original Contents
1010 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
1012 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1014 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1016 PRVM_serverglobalfloat(time) = sv.time;
1017 // Execute VM Function
1018 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1022 // Return if Function Call was Valid
1023 return bValidFunctionCall;
1032 void SV_CheckVelocity (prvm_edict_t *ent)
1034 prvm_prog_t *prog = SVVM_prog;
1041 for (i=0 ; i<3 ; i++)
1043 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1045 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1046 PRVM_serveredictvector(ent, velocity)[i] = 0;
1048 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1050 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1051 PRVM_serveredictvector(ent, origin)[i] = 0;
1055 // LadyHavoc: a hack to ensure that the (rather silly) id1 quakec
1056 // player_run/player_stand1 does not horribly malfunction if the
1057 // velocity becomes a denormalized float
1058 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0000001)
1059 VectorClear(PRVM_serveredictvector(ent, velocity));
1061 // LadyHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1062 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1063 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1065 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1066 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1067 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1068 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1076 Runs thinking code if time. There is some play in the exact time the think
1077 function will be called, because it is called before any movement is done
1078 in a frame. Not used for pushmove objects, because they must be exact.
1079 Returns false if the entity removed itself.
1082 static qboolean SV_RunThink (prvm_edict_t *ent)
1084 prvm_prog_t *prog = SVVM_prog;
1087 // don't let things stay in the past.
1088 // it is possible to start that way by a trigger with a local time.
1089 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1092 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1094 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1095 PRVM_serveredictfloat(ent, nextthink) = 0;
1096 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1097 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1098 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1099 // mods often set nextthink to time to cause a think every frame,
1100 // we don't want to loop in that case, so exit if the new nextthink is
1101 // <= the time the qc was told, also exit if it is past the end of the
1103 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1106 return !ent->priv.server->free;
1113 Two entities have touched, so run their touch functions
1116 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1118 prvm_prog_t *prog = SVVM_prog;
1119 int restorevm_tempstringsbuf_cursize;
1120 int old_self, old_other;
1121 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1123 old_self = PRVM_serverglobaledict(self);
1124 old_other = PRVM_serverglobaledict(other);
1125 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1127 VM_SetTraceGlobals(prog, trace);
1129 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1131 PRVM_serverglobalfloat(time) = sv.time;
1132 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1133 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1134 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1137 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1139 PRVM_serverglobalfloat(time) = sv.time;
1140 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1141 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1142 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1143 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1144 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1145 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1146 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1147 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1148 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1149 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1150 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1153 PRVM_serverglobaledict(self) = old_self;
1154 PRVM_serverglobaledict(other) = old_other;
1155 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1163 Slide off of the impacting object
1164 returns the blocked flags (1 = floor, 2 = step / wall)
1167 #define STOP_EPSILON 0.1
1168 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1173 backoff = -DotProduct (in, normal) * overbounce;
1174 VectorMA(in, backoff, normal, out);
1176 for (i = 0;i < 3;i++)
1177 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1186 The basic solid body movement clip that slides along multiple planes
1187 Returns the clipflags if the velocity was modified (hit something solid)
1191 8 = teleported by touch method
1192 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1195 static float SV_Gravity (prvm_edict_t *ent);
1196 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink);
1197 #define MAX_CLIP_PLANES 5
1198 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float stepheight)
1200 prvm_prog_t *prog = SVVM_prog;
1201 int blocked, bumpcount;
1202 int i, j, numplanes;
1203 float d, time_left, gravity;
1204 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1205 prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
1215 VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1219 gravity = SV_Gravity(ent);
1220 moving = PRVM_serveredictvector(ent, velocity)[0] || PRVM_serveredictvector(ent, velocity)[1];
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)
1259 if (trace.plane.normal[2])
1261 if (trace.plane.normal[2] > 0.7)
1268 Con_Printf ("SV_FlyMove: !trace.ent");
1269 trace.ent = prog->edicts;
1272 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1273 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1276 else if (stepheight)
1278 // step - handle it immediately
1284 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1285 VectorSet(steppush, 0, 0, stepheight);
1286 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1287 if(!SV_PushEntity(&steptrace, ent, steppush, false))
1292 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1293 if(!SV_PushEntity(&steptrace2, ent, push, false))
1298 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1299 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1300 if(!SV_PushEntity(&steptrace3, ent, steppush, false))
1305 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1306 // accept the new position if it made some progress...
1307 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1309 //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]);
1311 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1312 time_left *= 1 - trace.fraction;
1318 //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]);
1319 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1324 // step - return it to caller
1326 // save the trace for player extrafriction
1328 VectorCopy(trace.plane.normal, stepnormal);
1330 if (trace.fraction >= 0.001)
1332 // actually covered some distance
1333 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1337 time_left *= 1 - trace.fraction;
1339 // clipped to another plane
1340 if (numplanes >= MAX_CLIP_PLANES)
1342 // this shouldn't really happen
1343 VectorClear(PRVM_serveredictvector(ent, velocity));
1349 for (i = 0;i < numplanes;i++)
1350 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1354 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1359 VectorCopy(trace.plane.normal, planes[numplanes]);
1362 // modify original_velocity so it parallels all of the clip planes
1363 for (i = 0;i < numplanes;i++)
1365 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1366 for (j = 0;j < numplanes;j++)
1371 if (DotProduct(new_velocity, planes[j]) < 0)
1381 // go along this plane
1382 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1386 // go along the crease
1389 VectorClear(PRVM_serveredictvector(ent, velocity));
1393 CrossProduct(planes[0], planes[1], dir);
1394 // LadyHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1395 VectorNormalize(dir);
1396 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1397 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1400 // if current velocity is against the original velocity,
1401 // stop dead to avoid tiny occilations in sloping corners
1402 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1404 VectorClear(PRVM_serveredictvector(ent, velocity));
1409 //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]);
1412 if ((blocked & 1) == 0 && bumpcount > 1)
1414 // LadyHavoc: fix the 'fall to your death in a wedge corner' glitch
1415 // flag ONGROUND if there's ground under it
1416 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
1420 // LadyHavoc: this came from QW and allows you to get out of water more easily
1421 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1422 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1424 // Mario: this workaround introduces a new bug: sliding down ramps occurs when moving sideways
1425 moving = PRVM_serveredictvector(ent, velocity)[0] || PRVM_serveredictvector(ent, velocity)[1];
1428 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) || (((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) && moving))
1430 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1431 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1444 static float SV_Gravity (prvm_edict_t *ent)
1446 prvm_prog_t *prog = SVVM_prog;
1449 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1452 return ent_gravity * sv_gravity.value * sv.frametime;
1457 ===============================================================================
1461 ===============================================================================
1464 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1466 prvm_prog_t *prog = SVVM_prog;
1470 vec3_t stuckmins, stuckmaxs;
1471 vec3_t goodmins, goodmaxs;
1475 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1476 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1477 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1478 VectorCopy(pivot, goodmins);
1479 VectorCopy(pivot, goodmaxs);
1480 for (bump = 0;bump < 6;bump++)
1482 int coord = 2-(bump >> 1);
1483 //int coord = (bump >> 1);
1484 int dir = (bump & 1);
1487 for(subbump = 0; ; ++subbump)
1489 VectorCopy(stuckorigin, testorigin);
1493 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1498 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1501 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1502 if (stucktrace.bmodelstartsolid)
1504 // BAD BAD, can't fix that
1508 if (stucktrace.fraction >= 1)
1513 // BAD BAD, can't fix that
1517 // we hit something... let's move out of it
1518 VectorSubtract(stucktrace.endpos, testorigin, move);
1519 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1520 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1524 Con_Printf("subbump: %d\n", subbump);
1530 goodmaxs[coord] = stuckmaxs[coord];
1535 goodmins[coord] = stuckmins[coord];
1540 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1545 qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1547 prvm_prog_t *prog = SVVM_prog;
1551 vec3_t stuckmins, stuckmaxs;
1553 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1554 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1555 separation = 0.0f; // when using hulls, it can not be enlarged
1556 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1557 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1558 stuckmins[0] -= separation;
1559 stuckmins[1] -= separation;
1560 stuckmins[2] -= separation;
1561 stuckmaxs[0] += separation;
1562 stuckmaxs[1] += separation;
1563 stuckmaxs[2] += separation;
1564 // first pass we try to get it out of brush entities
1565 // second pass we try to get it out of world only (can't win them all)
1566 for (pass = 0;pass < 2;pass++)
1568 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1569 for (bump = 0;bump < 10;bump++)
1571 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1572 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1574 // found a good location, use it
1575 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1578 nudge = -stucktrace.startdepth;
1579 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1589 Does not change the entities velocity at all
1590 The trace struct is filled with the trace that has been done.
1591 Returns true if the push did not result in the entity being teleported by QC code.
1594 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink)
1596 prvm_prog_t *prog = SVVM_prog;
1604 solid = (int)PRVM_serveredictfloat(ent, solid);
1605 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1606 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1607 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1609 // move start position out of solids
1610 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1612 SV_NudgeOutOfSolid(ent);
1615 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1616 VectorAdd(start, push, end);
1618 if (movetype == MOVETYPE_FLYMISSILE)
1619 type = MOVE_MISSILE;
1620 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1621 type = MOVE_WORLDONLY;
1622 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1623 type = MOVE_NOMONSTERS; // only clip against bmodels
1627 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1628 // fail the move if stuck in world
1629 if (trace->worldstartsolid)
1632 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1634 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1639 if(!trace->startsolid)
1640 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)
1642 Con_Printf("something eeeeevil happened\n");
1647 SV_LinkEdict_TouchAreaGrid(ent);
1649 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))))
1650 SV_Impact (ent, trace);
1652 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1654 ent->priv.required->mark = 0;
1657 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1659 ent->priv.required->mark = 0;
1664 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1676 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1678 prvm_prog_t *prog = SVVM_prog;
1680 int pusherowner, pusherprog;
1683 float savesolid, movetime2, pushltime;
1684 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1686 int numcheckentities;
1687 static prvm_edict_t *checkentities[MAX_EDICTS];
1688 dp_model_t *pushermodel;
1689 trace_t trace, trace2;
1690 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1691 static unsigned short moved_edicts[MAX_EDICTS];
1694 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])
1696 PRVM_serveredictfloat(pusher, ltime) += movetime;
1700 switch ((int) PRVM_serveredictfloat(pusher, solid))
1702 // LadyHavoc: valid pusher types
1705 case SOLID_SLIDEBOX:
1706 case SOLID_CORPSE: // LadyHavoc: this would be weird...
1708 // LadyHavoc: no collisions
1711 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1712 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1713 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1714 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1715 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1716 PRVM_serveredictfloat(pusher, ltime) += movetime;
1717 SV_LinkEdict(pusher);
1720 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1723 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1724 if (index < 1 || index >= MAX_MODELS)
1726 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1729 pushermodel = SV_GetModelByIndex(index);
1730 pusherowner = PRVM_serveredictedict(pusher, owner);
1731 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1733 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1735 movetime2 = movetime;
1736 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1737 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1738 if (moveangle[0] || moveangle[2])
1740 for (i = 0;i < 3;i++)
1744 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1745 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1749 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1750 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1754 else if (moveangle[1])
1756 for (i = 0;i < 3;i++)
1760 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1761 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1765 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1766 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1772 for (i = 0;i < 3;i++)
1776 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1777 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1781 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1782 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1787 VectorNegate (moveangle, a);
1788 AngleVectorsFLU (a, forward, left, up);
1790 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1791 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1792 pushltime = PRVM_serveredictfloat(pusher, ltime);
1794 // move the pusher to its final position
1796 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1797 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1798 PRVM_serveredictfloat(pusher, ltime) += movetime;
1799 SV_LinkEdict(pusher);
1801 pushermodel = SV_GetModelFromEdict(pusher);
1802 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);
1803 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1805 savesolid = PRVM_serveredictfloat(pusher, solid);
1807 // see if any solid entities are inside the final position
1810 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1811 numcheckentities = 0;
1812 else // MOVETYPE_PUSH
1813 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1814 for (e = 0;e < numcheckentities;e++)
1816 prvm_edict_t *check = checkentities[e];
1817 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1822 case MOVETYPE_FOLLOW:
1823 case MOVETYPE_NOCLIP:
1824 case MOVETYPE_FLY_WORLDONLY:
1830 if (PRVM_serveredictedict(check, owner) == pusherprog)
1833 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1836 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1838 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1839 check->priv.server->waterposition_forceupdate = true;
1841 checkcontents = SV_GenericHitSuperContentsMask(check);
1843 // if the entity is standing on the pusher, it will definitely be moved
1844 // if the entity is not standing on the pusher, but is in the pusher's
1845 // final position, move it
1846 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1848 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1849 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1850 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1851 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1852 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1853 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);
1854 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1855 if (!trace.startsolid)
1857 //Con_Printf("- not in solid\n");
1862 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1863 //VectorClear(pivot);
1868 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1869 VectorAdd (org, pivot, org);
1870 org2[0] = DotProduct (org, forward);
1871 org2[1] = DotProduct (org, left);
1872 org2[2] = DotProduct (org, up);
1873 VectorSubtract (org2, org, move);
1874 VectorAdd (move, move1, move);
1877 VectorCopy (move1, move);
1879 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1881 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1882 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1883 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1885 // physics objects need better collisions than this code can do
1886 if (movetype == MOVETYPE_PHYSICS)
1888 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1889 SV_LinkEdict(check);
1890 SV_LinkEdict_TouchAreaGrid(check);
1894 // try moving the contacted entity
1895 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1896 if(!SV_PushEntity (&trace, check, move, true))
1898 // entity "check" got teleported
1899 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1900 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1901 continue; // pushed enough
1903 // FIXME: turn players specially
1904 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1905 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1906 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1908 // this trace.fraction < 1 check causes items to fall off of pushers
1909 // if they pass under or through a wall
1910 // the groundentity check causes items to fall off of ledges
1911 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1912 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1914 // if it is still inside the pusher, block
1915 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1916 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1917 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1918 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1919 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1920 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);
1921 if (trace.startsolid)
1924 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1926 // hack to invoke all necessary movement triggers
1928 if(!SV_PushEntity(&trace2, check, move2, true))
1930 // entity "check" got teleported
1937 // still inside pusher, so it's really blocked
1940 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1942 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1945 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1946 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1950 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1951 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1952 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1953 SV_LinkEdict(pusher);
1955 // move back any entities we already moved
1956 for (i = 0;i < num_moved;i++)
1958 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1959 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1960 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1964 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1965 if (PRVM_serveredictfunction(pusher, blocked))
1967 PRVM_serverglobalfloat(time) = sv.time;
1968 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1969 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1970 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1975 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1976 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1977 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1986 static void SV_Physics_Pusher (prvm_edict_t *ent)
1988 prvm_prog_t *prog = SVVM_prog;
1989 double thinktime, oldltime, movetime;
1991 oldltime = PRVM_serveredictfloat(ent, ltime);
1993 thinktime = PRVM_serveredictfloat(ent, nextthink);
1994 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1996 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
2001 movetime = sv.frametime;
2004 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
2005 SV_PushMove (ent, movetime);
2007 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
2009 PRVM_serveredictfloat(ent, nextthink) = 0;
2010 PRVM_serverglobalfloat(time) = sv.time;
2011 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2012 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
2013 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2019 ===============================================================================
2023 ===============================================================================
2026 static float unstickoffsets[] =
2028 // poutting -/+z changes first as they are least weird
2043 typedef enum unstickresult_e
2051 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2053 prvm_prog_t *prog = SVVM_prog;
2056 // if not stuck in a bmodel, just return
2057 if (!SV_TestEntityPosition(ent, vec3_origin))
2058 return UNSTICK_GOOD;
2060 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2062 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2064 VectorCopy(unstickoffsets + i, offset);
2066 //SV_LinkEdict_TouchAreaGrid(ent);
2067 return UNSTICK_UNSTUCK;
2071 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2072 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2074 for(i = 2; i <= maxunstick; ++i)
2076 VectorClear(offset);
2078 if (!SV_TestEntityPosition(ent, offset))
2081 //SV_LinkEdict_TouchAreaGrid(ent);
2082 return UNSTICK_UNSTUCK;
2085 if (!SV_TestEntityPosition(ent, offset))
2088 //SV_LinkEdict_TouchAreaGrid(ent);
2089 return UNSTICK_UNSTUCK;
2093 return UNSTICK_STUCK;
2096 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2098 prvm_prog_t *prog = SVVM_prog;
2100 switch(SV_UnstickEntityReturnOffset(ent, offset))
2104 case UNSTICK_UNSTUCK:
2105 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]);
2108 if (developer_extra.integer)
2109 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2112 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2121 This is a big hack to try and fix the rare case of getting stuck in the world
2125 static void SV_CheckStuck (prvm_edict_t *ent)
2127 prvm_prog_t *prog = SVVM_prog;
2130 switch(SV_UnstickEntityReturnOffset(ent, offset))
2133 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2135 case UNSTICK_UNSTUCK:
2136 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]);
2139 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2140 if (!SV_TestEntityPosition(ent, offset))
2142 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)));
2144 //SV_LinkEdict_TouchAreaGrid(ent);
2147 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2150 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2160 static qboolean SV_CheckWater (prvm_edict_t *ent)
2162 prvm_prog_t *prog = SVVM_prog;
2164 int nNativeContents;
2167 point[0] = PRVM_serveredictvector(ent, origin)[0];
2168 point[1] = PRVM_serveredictvector(ent, origin)[1];
2169 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2171 // DRESK - Support for Entity Contents Transition Event
2172 // NOTE: Some logic needed to be slightly re-ordered
2173 // to not affect performance and allow for the feature.
2175 // Acquire Super Contents Prior to Resets
2176 cont = SV_PointSuperContents(point);
2177 // Acquire Native Contents Here
2178 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(cont);
2180 // DRESK - Support for Entity Contents Transition Event
2181 if(PRVM_serveredictfloat(ent, watertype))
2182 // Entity did NOT Spawn; Check
2183 SV_CheckContentsTransition(ent, nNativeContents);
2186 PRVM_serveredictfloat(ent, waterlevel) = 0;
2187 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2188 cont = SV_PointSuperContents(point);
2189 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2191 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2192 PRVM_serveredictfloat(ent, waterlevel) = 1;
2193 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2194 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2196 PRVM_serveredictfloat(ent, waterlevel) = 2;
2197 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2198 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2199 PRVM_serveredictfloat(ent, waterlevel) = 3;
2203 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2212 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2214 prvm_prog_t *prog = SVVM_prog;
2216 vec3_t forward, into, side, v_angle;
2218 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2219 AngleVectors (v_angle, forward, NULL, NULL);
2220 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2222 // cut the tangential velocity
2223 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2224 VectorScale (stepnormal, i, into);
2225 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2226 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2227 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2233 =====================
2236 Player has come to a dead stop, possibly due to the problem with limited
2237 float precision at some angle joins in the BSP hull.
2239 Try fixing by pushing one pixel in each direction.
2241 This is a hack, but in the interest of good gameplay...
2242 ======================
2244 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2249 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2252 for (i=0 ; i<8 ; i++)
2254 // try pushing a little in an axial direction
2257 case 0: dir[0] = 2; dir[1] = 0; break;
2258 case 1: dir[0] = 0; dir[1] = 2; break;
2259 case 2: dir[0] = -2; dir[1] = 0; break;
2260 case 3: dir[0] = 0; dir[1] = -2; break;
2261 case 4: dir[0] = 2; dir[1] = 2; break;
2262 case 5: dir[0] = -2; dir[1] = 2; break;
2263 case 6: dir[0] = 2; dir[1] = -2; break;
2264 case 7: dir[0] = -2; dir[1] = -2; break;
2267 SV_PushEntity (&trace, ent, dir, false, true);
2269 // retry the original move
2270 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2271 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2272 PRVM_serveredictvector(ent, velocity)[2] = 0;
2273 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2275 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2276 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2278 Con_DPrint("TryUnstick - success.\n");
2282 // go back to the original pos and try again
2283 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2287 VectorClear (PRVM_serveredictvector(ent, velocity));
2288 Con_DPrint("TryUnstick - failure.\n");
2294 =====================
2297 Only used by players
2298 ======================
2300 static void SV_WalkMove (prvm_edict_t *ent)
2302 prvm_prog_t *prog = SVVM_prog;
2305 //int originalmove_clip;
2306 int originalmove_flags;
2307 int originalmove_groundentity;
2308 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2309 int skipsupercontentsmask = 0;
2310 int skipmaterialflagsmask = 0;
2312 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2313 trace_t downtrace, trace;
2314 qboolean applygravity;
2316 // if frametime is 0 (due to client sending the same timestamp twice),
2318 if (sv.frametime <= 0)
2321 if (sv_gameplayfix_unstickplayers.integer)
2322 SV_CheckStuck (ent);
2324 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2326 SV_CheckVelocity(ent);
2328 // do a regular slide move unless it looks like you ran into a step
2329 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2331 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2332 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2334 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2336 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2339 // only try this if there was no floor in the way in the trace (no,
2340 // this check seems to be not REALLY necessary, because if clip & 1,
2341 // our trace will hit that thing too)
2342 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2343 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2344 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2345 type = MOVE_MISSILE;
2346 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2347 type = MOVE_WORLDONLY;
2348 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2349 type = MOVE_NOMONSTERS; // only clip against bmodels
2352 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2353 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2354 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
2355 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2356 clip |= 1; // but we HAVE found a floor
2359 // if the move did not hit the ground at any point, we're not on ground
2361 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2363 SV_CheckVelocity(ent);
2365 SV_LinkEdict_TouchAreaGrid(ent);
2367 if(clip & 8) // teleport
2370 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2373 if (sv_nostep.integer)
2376 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2377 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2378 //originalmove_clip = clip;
2379 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2380 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2382 // if move didn't block on a step, return
2385 // if move was not trying to move into the step, return
2386 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2389 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2391 // return if gibbed by a trigger
2392 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2395 // return if attempting to jump while airborn (unless sv_jumpstep)
2396 if (!sv_jumpstep.integer)
2397 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2401 // try moving up and forward to go up a step
2402 // back to start pos
2403 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2404 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2407 VectorClear (upmove);
2408 upmove[2] = sv_stepheight.value;
2409 if(!SV_PushEntity(&trace, ent, upmove, true))
2411 // we got teleported when upstepping... must abort the move
2416 PRVM_serveredictvector(ent, velocity)[2] = 0;
2417 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 0);
2418 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2421 // we got teleported when upstepping... must abort the move
2422 // note that z velocity handling may not be what QC expects here, but we cannot help it
2426 SV_CheckVelocity(ent);
2428 SV_LinkEdict_TouchAreaGrid(ent);
2430 // check for stuckness, possibly due to the limited precision of floats
2431 // in the clipping hulls
2433 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2434 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2436 //Con_Printf("wall\n");
2437 // stepping up didn't make any progress, revert to original move
2438 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2439 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2440 //clip = originalmove_clip;
2441 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2442 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2443 // now try to unstick if needed
2444 //clip = SV_TryUnstick (ent, oldvel);
2448 //Con_Printf("step - ");
2450 // extra friction based on view angle
2451 if (clip & 2 && sv_wallfriction.integer)
2452 SV_WallFriction (ent, stepnormal);
2454 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2455 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))
2459 VectorClear (downmove);
2460 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2461 if(!SV_PushEntity (&downtrace, ent, downmove, true))
2463 // we got teleported when downstepping... must abort the move
2467 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2469 // this has been disabled so that you can't jump when you are stepping
2470 // up while already jumping (also known as the Quake2 double jump bug)
2472 // LadyHavoc: disabled this check so you can walk on monsters/players
2473 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2475 //Con_Printf("onground\n");
2476 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2477 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2483 //Con_Printf("slope\n");
2484 // if the push down didn't end up on good ground, use the move without
2485 // the step up. This happens near wall / slope combinations, and can
2486 // cause the player to hop up higher on a slope too steep to climb
2487 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2488 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2489 //clip = originalmove_clip;
2490 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2491 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2494 SV_CheckVelocity(ent);
2496 SV_LinkEdict_TouchAreaGrid(ent);
2499 //============================================================================
2505 Entities that are "stuck" to another entity
2508 static void SV_Physics_Follow (prvm_edict_t *ent)
2510 prvm_prog_t *prog = SVVM_prog;
2511 vec3_t vf, vr, vu, angles, v;
2514 // LadyHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2515 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2516 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])
2518 // quick case for no rotation
2519 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2523 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2524 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2525 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2526 AngleVectors (angles, vf, vr, vu);
2527 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];
2528 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];
2529 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];
2530 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2531 angles[1] = PRVM_serveredictvector(e, angles)[1];
2532 angles[2] = PRVM_serveredictvector(e, angles)[2];
2533 AngleVectors (angles, vf, vr, vu);
2534 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2535 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2536 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2538 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2540 //SV_LinkEdict_TouchAreaGrid(ent);
2544 ==============================================================================
2548 ==============================================================================
2553 SV_CheckWaterTransition
2557 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2560 prvm_prog_t *prog = SVVM_prog;
2561 // 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
2563 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2564 cont = Mod_Q1BSP_NativeContentsFromSuperContents(SV_PointSuperContents(entorigin));
2565 if (!PRVM_serveredictfloat(ent, watertype))
2567 // just spawned here
2568 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2570 PRVM_serveredictfloat(ent, watertype) = cont;
2571 PRVM_serveredictfloat(ent, waterlevel) = 1;
2575 // DRESK - Support for Entity Contents Transition Event
2576 // NOTE: Call here BEFORE updating the watertype below,
2577 // and suppress watersplash sound if a valid function
2578 // call was made to allow for custom "splash" sounds.
2579 else if( !SV_CheckContentsTransition(ent, cont) )
2580 { // Contents Transition Function Invalid; Potentially Play Water Sound
2581 // check if the entity crossed into or out of water
2582 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2583 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2586 if (cont <= CONTENTS_WATER)
2588 PRVM_serveredictfloat(ent, watertype) = cont;
2589 PRVM_serveredictfloat(ent, waterlevel) = 1;
2593 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2594 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2602 Toss, bounce, and fly movement. When onground, do nothing.
2606 void SV_Physics_Toss (prvm_edict_t *ent)
2608 prvm_prog_t *prog = SVVM_prog;
2613 prvm_edict_t *groundentity;
2614 float d, ent_gravity;
2618 // if onground, return without moving
2619 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2621 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2622 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2624 // don't stick to ground if onground and moving upward
2625 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2627 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2629 // we can trust FL_ONGROUND if groundentity is world because it never moves
2632 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2634 // if ent was supported by a brush model on previous frame,
2635 // and groundentity is now freed, set groundentity to 0 (world)
2636 // which leaves it suspended in the air
2637 PRVM_serveredictedict(ent, groundentity) = 0;
2638 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2641 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2643 // don't slide if still touching the groundentity
2647 ent->priv.server->suspendedinairflag = false;
2649 SV_CheckVelocity (ent);
2652 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2653 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2656 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2658 movetime = sv.frametime;
2659 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2662 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2663 if(!SV_PushEntity(&trace, ent, move, true))
2664 return; // teleported
2665 if (ent->priv.server->free)
2667 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2669 // try to unstick the entity
2670 SV_UnstickEntity(ent);
2671 if(!SV_PushEntity(&trace, ent, move, true))
2672 return; // teleported
2673 if (ent->priv.server->free)
2676 if (trace.fraction == 1)
2678 movetime *= 1 - min(1, trace.fraction);
2679 switch((int)PRVM_serveredictfloat(ent, movetype))
2681 case MOVETYPE_BOUNCEMISSILE:
2682 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2684 bouncefactor = 1.0f;
2686 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2687 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2688 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2691 case MOVETYPE_BOUNCE:
2692 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2694 bouncefactor = 0.5f;
2696 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2698 bouncestop = 60.0f / 800.0f;
2700 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2701 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2704 // LadyHavoc: fixed grenades not bouncing when fired down a slope
2705 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2706 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2708 d = PRVM_serveredictvector(ent, velocity)[2];
2709 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2711 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2712 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2713 VectorClear(PRVM_serveredictvector(ent, velocity));
2714 VectorClear(PRVM_serveredictvector(ent, avelocity));
2719 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2720 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2725 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2726 if (trace.plane.normal[2] > 0.7)
2728 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2729 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2730 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2731 ent->priv.server->suspendedinairflag = true;
2732 VectorClear (PRVM_serveredictvector(ent, velocity));
2733 VectorClear (PRVM_serveredictvector(ent, avelocity));
2738 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2739 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2746 // check for in water
2747 SV_CheckWaterTransition (ent);
2751 ===============================================================================
2755 ===============================================================================
2762 Monsters freefall when they don't have a ground entity, otherwise
2763 all movement is done with discrete steps.
2765 This is also used for objects that have become still on the ground, but
2766 will fall if the floor is pulled out from under them.
2769 static void SV_Physics_Step (prvm_edict_t *ent)
2771 prvm_prog_t *prog = SVVM_prog;
2772 int flags = (int)PRVM_serveredictfloat(ent, flags);
2775 // Backup Velocity in the event that movetypesteplandevent is called,
2776 // to provide a parameter with the entity's velocity at impact.
2777 vec3_t backupVelocity;
2778 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2779 // don't fall at all if fly/swim
2780 if (!(flags & (FL_FLY | FL_SWIM)))
2782 if (flags & FL_ONGROUND)
2784 // freefall if onground and moving upward
2785 // freefall if not standing on a world surface (it may be a lift or trap door)
2786 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2788 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2789 SV_CheckVelocity(ent);
2790 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2792 SV_LinkEdict_TouchAreaGrid(ent);
2793 ent->priv.server->waterposition_forceupdate = true;
2798 // freefall if not onground
2799 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2801 SV_CheckVelocity(ent);
2802 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2804 SV_LinkEdict_TouchAreaGrid(ent);
2807 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2809 // DRESK - Check for Entity Land Event Function
2810 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2811 { // Valid Function; Execute
2812 // Prepare Parameters
2813 // Assign Velocity at Impact
2814 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2815 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2816 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2818 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2820 PRVM_serverglobalfloat(time) = sv.time;
2821 // Execute VM Function
2822 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2825 // Check for Engine Landing Sound
2826 if(sv_sound_land.string)
2827 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2829 ent->priv.server->waterposition_forceupdate = true;
2834 //============================================================================
2836 static void SV_Physics_Entity (prvm_edict_t *ent)
2838 prvm_prog_t *prog = SVVM_prog;
2839 // don't run think/move on newly spawned projectiles as it messes up
2840 // movement interpolation and rocket trails, and is inconsistent with
2841 // respect to entities spawned in the same frame
2842 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2843 // but if it spawns a lower numbered ent, it doesn't - this never moves
2844 // ents in the first frame regardless)
2845 qboolean runmove = ent->priv.server->move;
2846 ent->priv.server->move = true;
2847 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2849 switch ((int) PRVM_serveredictfloat(ent, movetype))
2852 case MOVETYPE_FAKEPUSH:
2853 SV_Physics_Pusher (ent);
2856 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2857 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2860 case MOVETYPE_FOLLOW:
2861 if(SV_RunThink(ent))
2862 SV_Physics_Follow (ent);
2864 case MOVETYPE_NOCLIP:
2865 if (SV_RunThink(ent))
2868 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2869 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2874 SV_Physics_Step (ent);
2876 if (SV_RunThink(ent))
2877 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2879 ent->priv.server->waterposition_forceupdate = false;
2880 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2881 SV_CheckWaterTransition(ent);
2885 if (SV_RunThink (ent))
2889 case MOVETYPE_BOUNCE:
2890 case MOVETYPE_BOUNCEMISSILE:
2891 case MOVETYPE_FLYMISSILE:
2893 case MOVETYPE_FLY_WORLDONLY:
2895 if (SV_RunThink (ent))
2896 SV_Physics_Toss (ent);
2898 case MOVETYPE_PHYSICS:
2899 if (SV_RunThink(ent))
2902 SV_LinkEdict_TouchAreaGrid(ent);
2906 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2908 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2913 static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
2915 prvm_prog_t *prog = SVVM_prog;
2917 // don't run think at all, that is done during server frames
2918 // instead, call the movetypes directly so they match client input
2920 // This probably only makes sense for CSQC-networked (SendEntity field set) player entities
2921 switch ((int) PRVM_serveredictfloat(ent, movetype))
2924 case MOVETYPE_FAKEPUSH:
2925 // push physics relies heavily on think times and calls, and so cannot be predicted currently
2926 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2930 case MOVETYPE_FOLLOW:
2931 SV_Physics_Follow (ent);
2933 case MOVETYPE_NOCLIP:
2934 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2935 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2938 SV_Physics_Step (ent);
2944 case MOVETYPE_BOUNCE:
2945 case MOVETYPE_BOUNCEMISSILE:
2946 case MOVETYPE_FLYMISSILE:
2947 SV_Physics_Toss (ent);
2950 case MOVETYPE_FLY_WORLDONLY:
2953 case MOVETYPE_PHYSICS:
2956 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2958 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2963 void SV_Physics_ClientMove(void)
2965 prvm_prog_t *prog = SVVM_prog;
2967 ent = host_client->edict;
2969 // call player physics, this needs the proper frametime
2970 PRVM_serverglobalfloat(frametime) = sv.frametime;
2973 // call standard client pre-think, with frametime = 0
2974 PRVM_serverglobalfloat(time) = sv.time;
2975 PRVM_serverglobalfloat(frametime) = 0;
2976 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2977 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2978 PRVM_serverglobalfloat(frametime) = sv.frametime;
2980 // make sure the velocity is sane (not a NaN)
2981 SV_CheckVelocity(ent);
2983 // perform movetype behaviour
2984 // note: will always be MOVETYPE_WALK if disableclientprediction = 0
2985 SV_Physics_ClientEntity_NoThink (ent);
2987 // call standard player post-think, with frametime = 0
2988 PRVM_serverglobalfloat(time) = sv.time;
2989 PRVM_serverglobalfloat(frametime) = 0;
2990 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2991 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2992 PRVM_serverglobalfloat(frametime) = sv.frametime;
2994 if(PRVM_serveredictfloat(ent, fixangle))
2996 // angle fixing was requested by physics code...
2997 // so store the current angles for later use
2998 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2999 host_client->fixangle_angles_set = true;
3001 // and clear fixangle for the next frame
3002 PRVM_serveredictfloat(ent, fixangle) = 0;
3006 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
3008 prvm_prog_t *prog = SVVM_prog;
3009 // don't do physics on disconnected clients, FrikBot relies on this
3010 if (!host_client->begun)
3013 // make sure the velocity is sane (not a NaN)
3014 SV_CheckVelocity(ent);
3016 // don't run physics here if running asynchronously
3017 if (host_client->clmovement_inputtimeout <= 0)
3020 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
3023 // make sure the velocity is still sane (not a NaN)
3024 SV_CheckVelocity(ent);
3026 // call standard client pre-think
3027 PRVM_serverglobalfloat(time) = sv.time;
3028 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3029 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3031 // make sure the velocity is still sane (not a NaN)
3032 SV_CheckVelocity(ent);
3035 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3037 prvm_prog_t *prog = SVVM_prog;
3038 // don't do physics on disconnected clients, FrikBot relies on this
3039 if (!host_client->begun)
3042 // make sure the velocity is sane (not a NaN)
3043 SV_CheckVelocity(ent);
3045 // call standard player post-think
3046 PRVM_serverglobalfloat(time) = sv.time;
3047 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3048 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3050 // make sure the velocity is still sane (not a NaN)
3051 SV_CheckVelocity(ent);
3053 if(PRVM_serveredictfloat(ent, fixangle))
3055 // angle fixing was requested by physics code...
3056 // so store the current angles for later use
3057 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3058 host_client->fixangle_angles_set = true;
3060 // and clear fixangle for the next frame
3061 PRVM_serveredictfloat(ent, fixangle) = 0;
3064 // decrement the countdown variable used to decide when to go back to
3065 // synchronous physics
3066 if (host_client->clmovement_inputtimeout > sv.frametime)
3067 host_client->clmovement_inputtimeout -= sv.frametime;
3069 host_client->clmovement_inputtimeout = 0;
3072 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3074 prvm_prog_t *prog = SVVM_prog;
3075 // don't do physics on disconnected clients, FrikBot relies on this
3076 if (!host_client->begun)
3078 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3082 // make sure the velocity is sane (not a NaN)
3083 SV_CheckVelocity(ent);
3085 switch ((int) PRVM_serveredictfloat(ent, movetype))
3088 case MOVETYPE_FAKEPUSH:
3089 SV_Physics_Pusher (ent);
3092 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3093 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3096 case MOVETYPE_FOLLOW:
3098 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3099 SV_Physics_Follow (ent);
3101 case MOVETYPE_NOCLIP:
3103 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3106 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3107 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3111 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3112 SV_Physics_Step (ent);
3113 if (SV_RunThink(ent))
3114 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
3116 ent->priv.server->waterposition_forceupdate = false;
3117 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
3118 SV_CheckWaterTransition(ent);
3123 // don't run physics here if running asynchronously
3124 if (host_client->clmovement_inputtimeout <= 0)
3128 case MOVETYPE_BOUNCE:
3129 case MOVETYPE_BOUNCEMISSILE:
3130 case MOVETYPE_FLYMISSILE:
3133 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3134 SV_Physics_Toss (ent);
3137 case MOVETYPE_FLY_WORLDONLY:
3139 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3142 case MOVETYPE_PHYSICS:
3146 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
3148 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3152 SV_CheckVelocity (ent);
3155 SV_LinkEdict_TouchAreaGrid(ent);
3157 SV_CheckVelocity (ent);
3166 void SV_Physics (void)
3168 prvm_prog_t *prog = SVVM_prog;
3172 // free memory for resources that are no longer referenced
3173 PRVM_GarbageCollection(prog);
3175 // let the progs know that a new frame has started
3176 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3177 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3178 PRVM_serverglobalfloat(time) = sv.time;
3179 PRVM_serverglobalfloat(frametime) = sv.frametime;
3180 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3182 // run physics engine
3183 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3186 // treat each object in turn
3189 // if force_retouch, relink all the entities
3190 if (PRVM_serverglobalfloat(force_retouch) > 0)
3191 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3192 if (!ent->priv.server->free)
3193 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3195 if (sv_gameplayfix_consistentplayerprethink.integer)
3197 // run physics on the client entities in 3 stages
3198 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3199 if (!ent->priv.server->free)
3200 SV_Physics_ClientEntity_PreThink(ent);
3202 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3203 if (!ent->priv.server->free)
3204 SV_Physics_ClientEntity(ent);
3206 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3207 if (!ent->priv.server->free)
3208 SV_Physics_ClientEntity_PostThink(ent);
3212 // run physics on the client entities
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 if (!ent->priv.server->free)
3217 SV_Physics_ClientEntity_PreThink(ent);
3218 SV_Physics_ClientEntity(ent);
3219 SV_Physics_ClientEntity_PostThink(ent);
3224 // run physics on all the non-client entities
3225 if (!sv_freezenonclients.integer)
3227 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3228 if (!ent->priv.server->free)
3229 SV_Physics_Entity(ent);
3230 // make a second pass to see if any ents spawned this frame and make
3231 // sure they run their move/think
3232 if (sv_gameplayfix_delayprojectiles.integer < 0)
3233 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3234 if (!ent->priv.server->move && !ent->priv.server->free)
3235 SV_Physics_Entity(ent);
3238 if (PRVM_serverglobalfloat(force_retouch) > 0)
3239 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3241 // LadyHavoc: endframe support
3242 if (PRVM_serverfunction(EndFrame))
3244 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3245 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3246 PRVM_serverglobalfloat(time) = sv.time;
3247 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3250 // decrement prog->num_edicts if the highest number entities died
3251 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3253 if (!sv_freezenonclients.integer)
3254 sv.time += sv.frametime;