2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "prvm_cmds.h"
28 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
30 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
32 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
33 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
34 corpses are SOLID_NOT and MOVETYPE_TOSS
35 crates are SOLID_BBOX and MOVETYPE_TOSS
36 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
37 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
39 solid_edge items only clip against bsp models.
43 #define MOVE_EPSILON 0.01
45 void SV_Physics_Toss (prvm_edict_t *ent);
47 int SV_GetPitchSign(prvm_prog_t *prog, prvm_edict_t *ent)
51 (model = SV_GetModelFromEdict(ent))
53 model->type == mod_alias
56 (((unsigned char)PRVM_serveredictfloat(ent, pflags)) & PFLAGS_FULLDYNAMIC)
58 ((gamemode == GAME_TENEBRAE) && ((unsigned int)PRVM_serveredictfloat(ent, effects) & (16 | 32)))
66 ===============================================================================
70 ===============================================================================
73 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
75 prvm_prog_t *prog = SVVM_prog;
78 int dphitcontentsmask = (int)PRVM_serveredictfloat(passedict, dphitcontentsmask);
79 if (dphitcontentsmask)
80 return dphitcontentsmask;
81 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_SLIDEBOX)
83 if ((int)PRVM_serveredictfloat(passedict, flags) & FL_MONSTER)
84 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
86 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
88 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_CORPSE)
89 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
90 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_TRIGGER)
91 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
93 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
96 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
104 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask)
106 prvm_prog_t *prog = SVVM_prog;
107 int i, bodysupercontents;
110 prvm_edict_t *traceowner, *touch;
112 // temporary storage because prvm_vec_t may differ from vec_t
113 vec3_t touchmins, touchmaxs;
114 // bounding box of entire move area
115 vec3_t clipboxmins, clipboxmaxs;
116 // size when clipping against monsters
117 vec3_t clipmins2, clipmaxs2;
118 // start and end origin of move
122 // matrices to transform into/out of other entity's space
123 matrix4x4_t matrix, imatrix;
124 // model of other entity
126 // list of entities to test for collisions
128 static prvm_edict_t *touchedicts[MAX_EDICTS];
131 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
133 VectorCopy(start, clipstart);
134 VectorClear(clipmins2);
135 VectorClear(clipmaxs2);
136 #if COLLISIONPARANOID >= 3
137 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
141 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
142 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
143 if (cliptrace.startsolid || cliptrace.fraction < 1)
144 cliptrace.ent = prog->edicts;
145 if (type == MOVE_WORLDONLY)
148 if (type == MOVE_MISSILE)
150 // LadyHavoc: modified this, was = -15, now -= 15
151 for (i = 0;i < 3;i++)
158 // create the bounding box of the entire move
159 for (i = 0;i < 3;i++)
161 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
162 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
165 // debug override to test against everything
166 if (sv_debugmove.integer)
168 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = (vec_t)-999999999;
169 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = (vec_t)999999999;
172 // if the passedict is world, make it NULL (to avoid two checks each time)
173 if (passedict == prog->edicts)
175 // precalculate prog value for passedict for comparisons
176 passedictprog = PRVM_EDICT_TO_PROG(passedict);
177 // precalculate passedict's owner edict pointer for comparisons
178 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
180 clipgroup = passedict ? (int)PRVM_serveredictfloat(passedict, clipgroup) : 0;
183 // because this uses World_EntitiestoBox, we know all entity boxes overlap
184 // the clip region, so we can skip culling checks in the loop below
185 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
186 if (numtouchedicts > MAX_EDICTS)
188 // this never happens
189 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
190 numtouchedicts = MAX_EDICTS;
192 for (i = 0;i < numtouchedicts;i++)
194 touch = touchedicts[i];
196 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
198 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
203 // don't clip against self
204 if (passedict == touch)
206 // don't clip owned entities against owner
207 if (traceowner == touch)
209 // don't clip owner against owned entities
210 if (passedictprog == PRVM_serveredictedict(touch, owner))
212 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
213 if (clipgroup && clipgroup == (int)PRVM_serveredictfloat(touch, clipgroup))
215 // don't clip points against points (they can't collide)
216 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
220 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
222 // might interact, so do an exact clip
224 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
226 model = SV_GetModelFromEdict(touch);
227 pitchsign = SV_GetPitchSign(prog, touch);
230 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
232 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
233 Matrix4x4_Invert_Simple(&imatrix, &matrix);
234 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
235 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
236 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
237 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
238 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
239 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
240 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 0.0f);
242 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
244 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
256 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
258 prvm_prog_t *prog = SVVM_prog;
259 int i, bodysupercontents;
262 prvm_edict_t *traceowner, *touch;
264 // temporary storage because prvm_vec_t may differ from vec_t
265 vec3_t touchmins, touchmaxs;
266 // bounding box of entire move area
267 vec3_t clipboxmins, clipboxmaxs;
268 // size when clipping against monsters
269 vec3_t clipmins2, clipmaxs2;
270 // start and end origin of move
271 vec3_t clipstart, clipend;
274 // matrices to transform into/out of other entity's space
275 matrix4x4_t matrix, imatrix;
276 // model of other entity
278 // list of entities to test for collisions
280 static prvm_edict_t *touchedicts[MAX_EDICTS];
282 if (VectorCompare(start, end))
283 return SV_TracePoint(start, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
285 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
287 VectorCopy(start, clipstart);
288 VectorCopy(end, clipend);
289 VectorClear(clipmins2);
290 VectorClear(clipmaxs2);
291 #if COLLISIONPARANOID >= 3
292 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
296 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, false);
297 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
298 if (cliptrace.startsolid || cliptrace.fraction < 1)
299 cliptrace.ent = prog->edicts;
300 if (type == MOVE_WORLDONLY)
303 if (type == MOVE_MISSILE)
305 // LadyHavoc: modified this, was = -15, now -= 15
306 for (i = 0;i < 3;i++)
313 // create the bounding box of the entire move
314 for (i = 0;i < 3;i++)
316 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
317 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
320 // debug override to test against everything
321 if (sv_debugmove.integer)
323 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = (vec_t)-999999999;
324 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = (vec_t)999999999;
327 // if the passedict is world, make it NULL (to avoid two checks each time)
328 if (passedict == prog->edicts)
330 // precalculate prog value for passedict for comparisons
331 passedictprog = PRVM_EDICT_TO_PROG(passedict);
332 // precalculate passedict's owner edict pointer for comparisons
333 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
335 clipgroup = passedict ? (int)PRVM_serveredictfloat(passedict, clipgroup) : 0;
338 // because this uses World_EntitiestoBox, we know all entity boxes overlap
339 // the clip region, so we can skip culling checks in the loop below
340 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
341 if (numtouchedicts > MAX_EDICTS)
343 // this never happens
344 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
345 numtouchedicts = MAX_EDICTS;
347 for (i = 0;i < numtouchedicts;i++)
349 touch = touchedicts[i];
351 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
353 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
358 // don't clip against self
359 if (passedict == touch)
361 // don't clip owned entities against owner
362 if (traceowner == touch)
364 // don't clip owner against owned entities
365 if (passedictprog == PRVM_serveredictedict(touch, owner))
367 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
368 if (clipgroup && clipgroup == (int)PRVM_serveredictfloat(touch, clipgroup))
370 // don't clip points against points (they can't collide)
371 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
375 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
377 // might interact, so do an exact clip
379 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
381 model = SV_GetModelFromEdict(touch);
382 pitchsign = SV_GetPitchSign(prog, touch);
385 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
387 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
388 Matrix4x4_Invert_Simple(&imatrix, &matrix);
389 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
390 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
391 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
392 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
393 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
394 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
395 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
397 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, false);
399 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
411 #if COLLISIONPARANOID >= 1
412 static trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
414 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
417 prvm_prog_t *prog = SVVM_prog;
418 vec3_t hullmins, hullmaxs;
419 int i, bodysupercontents;
423 prvm_edict_t *traceowner, *touch;
425 // temporary storage because prvm_vec_t may differ from vec_t
426 vec3_t touchmins, touchmaxs;
427 // bounding box of entire move area
428 vec3_t clipboxmins, clipboxmaxs;
429 // size of the moving object
430 vec3_t clipmins, clipmaxs;
431 // size when clipping against monsters
432 vec3_t clipmins2, clipmaxs2;
433 // start and end origin of move
434 vec3_t clipstart, clipend;
437 // matrices to transform into/out of other entity's space
438 matrix4x4_t matrix, imatrix;
439 // model of other entity
441 // list of entities to test for collisions
443 static prvm_edict_t *touchedicts[MAX_EDICTS];
445 if (VectorCompare(mins, maxs))
447 vec3_t shiftstart, shiftend;
448 VectorAdd(start, mins, shiftstart);
449 VectorAdd(end, mins, shiftend);
450 if (VectorCompare(start, end))
451 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
453 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
454 VectorSubtract(trace.endpos, mins, trace.endpos);
458 VectorCopy(start, clipstart);
459 VectorCopy(end, clipend);
460 VectorCopy(mins, clipmins);
461 VectorCopy(maxs, clipmaxs);
462 VectorCopy(mins, clipmins2);
463 VectorCopy(maxs, clipmaxs2);
464 #if COLLISIONPARANOID >= 3
465 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
469 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
470 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
471 if (cliptrace.startsolid || cliptrace.fraction < 1)
472 cliptrace.ent = prog->edicts;
473 if (type == MOVE_WORLDONLY)
476 if (type == MOVE_MISSILE)
478 // LadyHavoc: modified this, was = -15, now -= 15
479 for (i = 0;i < 3;i++)
486 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
487 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
488 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
491 VectorCopy(clipmins, hullmins);
492 VectorCopy(clipmaxs, hullmaxs);
495 // create the bounding box of the entire move
496 for (i = 0;i < 3;i++)
498 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
499 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
502 // debug override to test against everything
503 if (sv_debugmove.integer)
505 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = (vec_t)-999999999;
506 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = (vec_t)999999999;
509 // if the passedict is world, make it NULL (to avoid two checks each time)
510 if (passedict == prog->edicts)
512 // precalculate prog value for passedict for comparisons
513 passedictprog = PRVM_EDICT_TO_PROG(passedict);
514 // figure out whether this is a point trace for comparisons
515 pointtrace = VectorCompare(clipmins, clipmaxs);
516 // precalculate passedict's owner edict pointer for comparisons
517 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
519 clipgroup = passedict ? (int)PRVM_serveredictfloat(passedict, clipgroup) : 0;
522 // because this uses World_EntitiestoBox, we know all entity boxes overlap
523 // the clip region, so we can skip culling checks in the loop below
524 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
525 if (numtouchedicts > MAX_EDICTS)
527 // this never happens
528 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
529 numtouchedicts = MAX_EDICTS;
531 for (i = 0;i < numtouchedicts;i++)
533 touch = touchedicts[i];
535 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
537 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
542 // don't clip against self
543 if (passedict == touch)
545 // don't clip owned entities against owner
546 if (traceowner == touch)
548 // don't clip owner against owned entities
549 if (passedictprog == PRVM_serveredictedict(touch, owner))
551 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
552 if (clipgroup && clipgroup == (int)PRVM_serveredictfloat(touch, clipgroup))
554 // don't clip points against points (they can't collide)
555 if (pointtrace && VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
559 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
561 // might interact, so do an exact clip
563 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
565 model = SV_GetModelFromEdict(touch);
566 pitchsign = SV_GetPitchSign(prog, touch);
569 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
571 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
572 Matrix4x4_Invert_Simple(&imatrix, &matrix);
573 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
574 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
575 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
576 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
577 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
578 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
579 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
581 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
583 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
590 #if COLLISIONPARANOID >= 1
591 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
593 prvm_prog_t *prog = SVVM_prog;
597 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
600 VectorCopy(trace.endpos, temp);
601 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend).startsolid;
602 #if COLLISIONPARANOID < 3
603 if (trace.startsolid || endstuck)
605 Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "^3" : "", passedict ? (int)(passedict - prog->edicts) : -1, PRVM_serveredictvector(passedict, origin)[0], PRVM_serveredictvector(passedict, origin)[1], PRVM_serveredictvector(passedict, origin)[2], end[0] - PRVM_serveredictvector(passedict, origin)[0], end[1] - PRVM_serveredictvector(passedict, origin)[1], end[2] - PRVM_serveredictvector(passedict, origin)[2], trace.fraction, trace.endpos[0] - PRVM_serveredictvector(passedict, origin)[0], trace.endpos[1] - PRVM_serveredictvector(passedict, origin)[1], trace.endpos[2] - PRVM_serveredictvector(passedict, origin)[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
611 int SV_PointSuperContents(const vec3_t point)
613 prvm_prog_t *prog = SVVM_prog;
614 int supercontents = 0;
618 // matrices to transform into/out of other entity's space
619 matrix4x4_t matrix, imatrix;
620 // model of other entity
623 // list of entities to test for collisions
625 static prvm_edict_t *touchedicts[MAX_EDICTS];
627 // get world supercontents at this point
628 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
629 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
631 // if sv_gameplayfix_swiminbmodels is off we're done
632 if (!sv_gameplayfix_swiminbmodels.integer)
633 return supercontents;
635 // get list of entities at this point
636 numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
637 if (numtouchedicts > MAX_EDICTS)
639 // this never happens
640 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
641 numtouchedicts = MAX_EDICTS;
643 for (i = 0;i < numtouchedicts;i++)
645 touch = touchedicts[i];
647 // we only care about SOLID_BSP for pointcontents
648 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
651 // might interact, so do an exact clip
652 model = SV_GetModelFromEdict(touch);
653 if (!model || !model->PointSuperContents)
655 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
656 Matrix4x4_Invert_Simple(&imatrix, &matrix);
657 Matrix4x4_Transform(&imatrix, point, transformed);
658 frame = (int)PRVM_serveredictfloat(touch, frame);
659 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
662 return supercontents;
666 ===============================================================================
668 Linking entities into the world culling system
670 ===============================================================================
673 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
675 prvm_prog_t *prog = SVVM_prog;
676 vec3_t paddedmins, paddedmaxs;
677 if (maxedicts < 1 || resultedicts == NULL)
679 // LadyHavoc: discovered this actually causes its own bugs (dm6 teleporters being too close to info_teleport_destination)
680 //VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
681 //VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
682 VectorCopy(mins, paddedmins);
683 VectorCopy(maxs, paddedmaxs);
684 if (sv_areadebug.integer)
686 int numresultedicts = 0;
689 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
691 ed = PRVM_EDICT_NUM(edictindex);
692 if (!ed->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
694 resultedicts[numresultedicts++] = ed;
695 if (numresultedicts == maxedicts)
699 return numresultedicts;
702 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
705 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
707 prvm_prog_t *prog = SVVM_prog;
708 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
709 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
710 PRVM_serverglobalfloat(time) = sv.time;
711 PRVM_serverglobalfloat(trace_allsolid) = false;
712 PRVM_serverglobalfloat(trace_startsolid) = false;
713 PRVM_serverglobalfloat(trace_fraction) = 1;
714 PRVM_serverglobalfloat(trace_inwater) = false;
715 PRVM_serverglobalfloat(trace_inopen) = true;
716 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
717 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
718 PRVM_serverglobalfloat(trace_plane_dist) = 0;
719 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
720 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
721 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
722 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
723 PRVM_serverglobalstring(trace_dphittexturename) = 0;
724 prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
727 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
729 prvm_prog_t *prog = SVVM_prog;
730 int i, numtouchedicts, old_self, old_other;
732 static prvm_edict_t *touchedicts[MAX_EDICTS];
734 if (ent == prog->edicts)
735 return; // don't add the world
740 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
743 // build a list of edicts to touch, because the link loop can be corrupted
744 // by IncreaseEdicts called during touch functions
745 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
746 if (numtouchedicts > MAX_EDICTS)
748 // this never happens
749 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
750 numtouchedicts = MAX_EDICTS;
753 old_self = PRVM_serverglobaledict(self);
754 old_other = PRVM_serverglobaledict(other);
755 for (i = 0;i < numtouchedicts;i++)
757 touch = touchedicts[i];
758 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
760 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
763 PRVM_serverglobaledict(self) = old_self;
764 PRVM_serverglobaledict(other) = old_other;
767 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
771 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
773 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
774 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
775 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
776 if(rotatedmins[0] > u[0]) { rotatedmins[0] = u[0]; } if(rotatedmins[1] > u[1]) { rotatedmins[1] = u[1]; } if(rotatedmins[2] > u[2]) { rotatedmins[2] = u[2]; }
777 if(rotatedmaxs[0] < u[0]) { rotatedmaxs[0] = u[0]; } if(rotatedmaxs[1] < u[1]) { rotatedmaxs[1] = u[1]; } if(rotatedmaxs[2] < u[2]) { rotatedmaxs[2] = u[2]; }
778 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
779 if(rotatedmins[0] > u[0]) { rotatedmins[0] = u[0]; } if(rotatedmins[1] > u[1]) { rotatedmins[1] = u[1]; } if(rotatedmins[2] > u[2]) { rotatedmins[2] = u[2]; }
780 if(rotatedmaxs[0] < u[0]) { rotatedmaxs[0] = u[0]; } if(rotatedmaxs[1] < u[1]) { rotatedmaxs[1] = u[1]; } if(rotatedmaxs[2] < u[2]) { rotatedmaxs[2] = u[2]; }
781 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
782 if(rotatedmins[0] > u[0]) { rotatedmins[0] = u[0]; } if(rotatedmins[1] > u[1]) { rotatedmins[1] = u[1]; } if(rotatedmins[2] > u[2]) { rotatedmins[2] = u[2]; }
783 if(rotatedmaxs[0] < u[0]) { rotatedmaxs[0] = u[0]; } if(rotatedmaxs[1] < u[1]) { rotatedmaxs[1] = u[1]; } if(rotatedmaxs[2] < u[2]) { rotatedmaxs[2] = u[2]; }
784 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
785 if(rotatedmins[0] > u[0]) { rotatedmins[0] = u[0]; } if(rotatedmins[1] > u[1]) { rotatedmins[1] = u[1]; } if(rotatedmins[2] > u[2]) { rotatedmins[2] = u[2]; }
786 if(rotatedmaxs[0] < u[0]) { rotatedmaxs[0] = u[0]; } if(rotatedmaxs[1] < u[1]) { rotatedmaxs[1] = u[1]; } if(rotatedmaxs[2] < u[2]) { rotatedmaxs[2] = u[2]; }
787 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
788 if(rotatedmins[0] > u[0]) { rotatedmins[0] = u[0]; } if(rotatedmins[1] > u[1]) { rotatedmins[1] = u[1]; } if(rotatedmins[2] > u[2]) { rotatedmins[2] = u[2]; }
789 if(rotatedmaxs[0] < u[0]) { rotatedmaxs[0] = u[0]; } if(rotatedmaxs[1] < u[1]) { rotatedmaxs[1] = u[1]; } if(rotatedmaxs[2] < u[2]) { rotatedmaxs[2] = u[2]; }
790 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
791 if(rotatedmins[0] > u[0]) { rotatedmins[0] = u[0]; } if(rotatedmins[1] > u[1]) { rotatedmins[1] = u[1]; } if(rotatedmins[2] > u[2]) { rotatedmins[2] = u[2]; }
792 if(rotatedmaxs[0] < u[0]) { rotatedmaxs[0] = u[0]; } if(rotatedmaxs[1] < u[1]) { rotatedmaxs[1] = u[1]; } if(rotatedmaxs[2] < u[2]) { rotatedmaxs[2] = u[2]; }
793 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
794 if(rotatedmins[0] > u[0]) { rotatedmins[0] = u[0]; } if(rotatedmins[1] > u[1]) { rotatedmins[1] = u[1]; } if(rotatedmins[2] > u[2]) { rotatedmins[2] = u[2]; }
795 if(rotatedmaxs[0] < u[0]) { rotatedmaxs[0] = u[0]; } if(rotatedmaxs[1] < u[1]) { rotatedmaxs[1] = u[1]; } if(rotatedmaxs[2] < u[2]) { rotatedmaxs[2] = u[2]; }
804 void SV_LinkEdict (prvm_edict_t *ent)
806 prvm_prog_t *prog = SVVM_prog;
808 vec3_t mins, maxs, entmins, entmaxs, entangles;
811 if (ent == prog->edicts)
812 return; // don't add the world
817 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
818 if (modelindex < 0 || modelindex >= MAX_MODELS)
820 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
823 model = SV_GetModelByIndex(modelindex);
825 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
826 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
827 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
831 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
833 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
834 // TODO special handling for spheres?
835 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
836 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
837 VectorCopy(PRVM_serveredictvector(ent, angles), entangles);
838 RotateBBox(entmins, entmaxs, entangles, mins, maxs);
839 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
840 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
842 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
846 if (!model->TraceBox)
847 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
849 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
851 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
852 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
854 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
856 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
857 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
861 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
862 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
867 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
868 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
869 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
874 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
875 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
878 if (sv_legacy_bbox_expand.integer)
880 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
882 // to make items easier to pick up and allow them to be grabbed off
883 // of shelves, the abs sizes are expanded
893 // because movement is clipped an epsilon away from an actual edge,
894 // we must fully check even when bounding boxes don't quite touch
904 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
905 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
907 World_LinkEdict(&sv.world, ent, mins, maxs, sv_areagrid_link_SOLID_NOT.integer);
911 ===============================================================================
915 ===============================================================================
920 SV_TestEntityPosition
922 returns true if the entity is in solid currently
925 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
927 prvm_prog_t *prog = SVVM_prog;
928 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
929 int skipsupercontentsmask = 0;
930 int skipmaterialflagsmask = 0;
931 vec3_t org, entorigin, entmins, entmaxs;
933 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
934 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
935 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
936 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
937 trace = SV_TraceBox(org, entmins, entmaxs, entorigin, ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
938 if (trace.startsupercontents & hitsupercontentsmask)
942 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
944 // q1bsp/hlbsp use hulls and if the entity does not exactly match
945 // a hull size it is incorrectly tested, so this code tries to
946 // 'fix' it slightly...
947 // FIXME: this breaks entities larger than the hull size
950 VectorAdd(org, entmins, m1);
951 VectorAdd(org, entmaxs, m2);
952 VectorSubtract(m2, m1, s);
953 #define EPSILON (1.0f / 32.0f)
954 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
955 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
956 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
957 for (i = 0;i < 8;i++)
959 v[0] = (i & 1) ? m2[0] : m1[0];
960 v[1] = (i & 2) ? m2[1] : m1[1];
961 v[2] = (i & 4) ? m2[2] : m1[2];
962 if (SV_PointSuperContents(v) & hitsupercontentsmask)
967 // if the trace found a better position for the entity, move it there
968 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
971 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
972 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
974 // verify if the endpos is REALLY outside solid
975 VectorCopy(trace.endpos, org);
976 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
978 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
980 VectorCopy(org, PRVM_serveredictvector(ent, origin));
986 // DRESK - Support for Entity Contents Transition Event
989 SV_CheckContentsTransition
991 returns true if entity had a valid contentstransition function call
994 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
996 prvm_prog_t *prog = SVVM_prog;
997 int bValidFunctionCall;
999 // Default Valid Function Call to False
1000 bValidFunctionCall = false;
1002 if(PRVM_serveredictfloat(ent, watertype) != nContents)
1003 { // Changed Contents
1004 // Acquire Contents Transition Function from QC
1005 if(PRVM_serveredictfunction(ent, contentstransition))
1006 { // Valid Function; Execute
1007 // Assign Valid Function
1008 bValidFunctionCall = true;
1009 // Prepare Parameters (Original Contents, New Contents)
1010 // Original Contents
1011 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
1013 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1015 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1017 PRVM_serverglobalfloat(time) = sv.time;
1018 // Execute VM Function
1019 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1023 // Return if Function Call was Valid
1024 return bValidFunctionCall;
1033 void SV_CheckVelocity (prvm_edict_t *ent)
1035 prvm_prog_t *prog = SVVM_prog;
1042 for (i=0 ; i<3 ; i++)
1044 if (isnan(PRVM_serveredictvector(ent, velocity)[i]))
1046 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1047 PRVM_serveredictvector(ent, velocity)[i] = 0;
1049 if (isnan(PRVM_serveredictvector(ent, origin)[i]))
1051 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1052 PRVM_serveredictvector(ent, origin)[i] = 0;
1056 // LadyHavoc: a hack to ensure that the (rather silly) id1 quakec
1057 // player_run/player_stand1 does not horribly malfunction if the
1058 // velocity becomes a denormalized float
1059 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0000001)
1060 VectorClear(PRVM_serveredictvector(ent, velocity));
1062 // LadyHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1063 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1064 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1066 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1067 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1068 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1069 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1077 Runs thinking code if time. There is some play in the exact time the think
1078 function will be called, because it is called before any movement is done
1079 in a frame. Not used for pushmove objects, because they must be exact.
1080 Returns false if the entity removed itself.
1083 static qbool SV_RunThink (prvm_edict_t *ent)
1085 prvm_prog_t *prog = SVVM_prog;
1088 // don't let things stay in the past.
1089 // it is possible to start that way by a trigger with a local time.
1090 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1093 for (iterations = 0;iterations < 128 && !ent->free;iterations++)
1095 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1096 PRVM_serveredictfloat(ent, nextthink) = 0;
1097 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1098 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1099 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1100 // mods often set nextthink to time to cause a think every frame,
1101 // we don't want to loop in that case, so exit if the new nextthink is
1102 // <= the time the qc was told, also exit if it is past the end of the
1104 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1114 Two entities have touched, so run their touch functions
1117 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1119 prvm_prog_t *prog = SVVM_prog;
1120 int restorevm_tempstringsbuf_cursize;
1121 int old_self, old_other;
1122 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1124 old_self = PRVM_serverglobaledict(self);
1125 old_other = PRVM_serverglobaledict(other);
1126 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1128 VM_SetTraceGlobals(prog, trace);
1130 if (!e1->free && !e2->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1132 PRVM_serverglobalfloat(time) = sv.time;
1133 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1134 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1135 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1138 if (!e1->free && !e2->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1140 PRVM_serverglobalfloat(time) = sv.time;
1141 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1142 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1143 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1144 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1145 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1146 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1147 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1148 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1149 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1150 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1151 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1154 PRVM_serverglobaledict(self) = old_self;
1155 PRVM_serverglobaledict(other) = old_other;
1156 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1164 Slide off of the impacting object
1165 returns the blocked flags (1 = floor, 2 = step / wall)
1168 #define STOP_EPSILON 0.1
1169 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1174 backoff = -DotProduct (in, normal) * overbounce;
1175 VectorMA(in, backoff, normal, out);
1177 for (i = 0;i < 3;i++)
1178 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1187 The basic solid body movement clip that slides along multiple planes
1188 Returns the clipflags if the velocity was modified (hit something solid)
1192 8 = teleported by touch method
1193 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1196 static float SV_Gravity (prvm_edict_t *ent);
1197 static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink, qbool checkstuck);
1198 #define MAX_CLIP_PLANES 5
1199 static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float stepheight)
1201 prvm_prog_t *prog = SVVM_prog;
1202 unsigned int i, j, numplanes, blocked, bumpcount;
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;
1214 VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1218 gravity = SV_Gravity(ent);
1220 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1222 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1223 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1225 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1230 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1231 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1234 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1236 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1239 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1240 if(!SV_PushEntity(&trace, ent, push, false, true))
1242 // we got teleported by a touch function
1243 // let's abort the move
1248 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
1249 // abort move if we're stuck in the world (and didn't make it out)
1250 if (trace.worldstartsolid && trace.allsolid && trace.startdepth < 0)
1252 VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
1256 if (trace.fraction == 1)
1259 time_left *= 1 - trace.fraction;
1261 if (trace.plane.normal[2])
1263 if (trace.plane.normal[2] > 0.7)
1270 Con_Printf ("SV_FlyMove: !trace.ent");
1271 trace.ent = prog->edicts;
1274 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1275 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1278 else if (stepheight)
1280 // step - handle it immediately
1286 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1287 VectorSet(steppush, 0, 0, stepheight);
1288 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1289 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1290 if(!SV_PushEntity(&steptrace, ent, steppush, false, true))
1295 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1296 if(!SV_PushEntity(&steptrace2, ent, push, false, true))
1301 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1302 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1303 if(!SV_PushEntity(&steptrace3, ent, steppush, false, true))
1308 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1309 // accept the new position if it made some progress...
1310 // previously this checked if absolute distance >= 0.03125 which made stepping up unreliable
1311 if (PRVM_serveredictvector(ent, origin)[0] - org[0] || PRVM_serveredictvector(ent, origin)[1] - org[1])
1313 //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]);
1315 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1316 time_left *= 1 - trace.fraction;
1322 //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]);
1323 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1328 // step - return it to caller
1330 // save the trace for player extrafriction
1332 VectorCopy(trace.plane.normal, stepnormal);
1335 // Unlike some other movetypes Quake's SV_FlyMove calls SV_Impact only after setting ONGROUND which id1 fiends rely on.
1336 // If we stepped up (sv_gameplayfix_stepmultipletimes) this will impact the steptrace2 plane instead of the original.
1337 if (PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace.ent)
1338 SV_Impact(ent, &trace);
1340 return blocked; // removed by the impact function
1342 if (trace.fraction >= 0.001)
1344 // actually covered some distance
1345 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1349 // clipped to another plane
1350 if (numplanes >= MAX_CLIP_PLANES)
1352 // this shouldn't really happen
1353 VectorClear(PRVM_serveredictvector(ent, velocity));
1359 for (i = 0;i < numplanes;i++)
1360 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1364 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1369 VectorCopy(trace.plane.normal, planes[numplanes]);
1372 // modify original_velocity so it parallels all of the clip planes
1373 for (i = 0;i < numplanes;i++)
1375 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1376 for (j = 0;j < numplanes;j++)
1381 if (DotProduct(new_velocity, planes[j]) < 0)
1391 // go along this plane
1392 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1396 // go along the crease
1399 VectorClear(PRVM_serveredictvector(ent, velocity));
1403 CrossProduct(planes[0], planes[1], dir);
1404 // LadyHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1405 VectorNormalize(dir);
1406 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1407 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1410 // if current velocity is against the original velocity,
1411 // stop dead to avoid tiny occilations in sloping corners
1412 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1414 VectorClear(PRVM_serveredictvector(ent, velocity));
1419 //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]);
1422 if ((blocked & 1) == 0 && bumpcount > 1)
1424 // LadyHavoc: fix the 'fall to your death in a wedge corner' glitch
1425 // flag ONGROUND if there's ground under it
1426 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
1430 // LadyHavoc: this came from QW and allows you to get out of water more easily
1431 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1432 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1436 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1438 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1439 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1452 static float SV_Gravity (prvm_edict_t *ent)
1454 prvm_prog_t *prog = SVVM_prog;
1457 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1460 return ent_gravity * sv_gravity.value * sv.frametime;
1465 ===============================================================================
1469 ===============================================================================
1472 static qbool SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1474 prvm_prog_t *prog = SVVM_prog;
1478 vec3_t stuckmins, stuckmaxs;
1479 vec3_t goodmins, goodmaxs;
1483 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1484 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1485 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1486 VectorCopy(pivot, goodmins);
1487 VectorCopy(pivot, goodmaxs);
1488 for (bump = 0;bump < 6;bump++)
1490 int coord = 2-(bump >> 1);
1491 //int coord = (bump >> 1);
1492 int dir = (bump & 1);
1495 for(subbump = 0; ; ++subbump)
1497 VectorCopy(stuckorigin, testorigin);
1501 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1506 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1509 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1510 if (stucktrace.bmodelstartsolid)
1512 // BAD BAD, can't fix that
1516 if (stucktrace.fraction >= 1)
1521 // BAD BAD, can't fix that
1525 // we hit something... let's move out of it
1526 VectorSubtract(stucktrace.endpos, testorigin, move);
1527 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1528 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1532 Con_Printf("subbump: %d\n", subbump);
1538 goodmaxs[coord] = stuckmaxs[coord];
1543 goodmins[coord] = stuckmins[coord];
1548 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1557 Does not change the entities velocity at all
1558 The trace struct is filled with the trace that has been done.
1559 Returns true if the push did not result in the entity being teleported by QC code.
1562 static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink, qbool checkstuck)
1564 prvm_prog_t *prog = SVVM_prog;
1572 solid = (int)PRVM_serveredictfloat(ent, solid);
1573 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1574 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1575 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1577 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1578 VectorAdd(start, push, end);
1580 if (movetype == MOVETYPE_FLYMISSILE)
1581 type = MOVE_MISSILE;
1582 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1583 type = MOVE_WORLDONLY;
1584 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1585 type = MOVE_NOMONSTERS; // only clip against bmodels
1589 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1590 // abort move if we're stuck in the world (and didn't make it out)
1591 if (trace->worldstartsolid && trace->allsolid && trace->startdepth < 0 && checkstuck)
1593 // checking startdepth eliminates many false positives on Q1BSP with mod_q1bsp_polygoncollisions 0
1594 // but it's still not guaranteed that we're stuck in a bmodel at this point
1595 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1597 switch (PHYS_NudgeOutOfSolid(prog, ent))
1600 Con_Printf(CON_WARN "NudgeOutOfSolid couldn't fix stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1601 return true; // definitely stuck in a bmodel
1603 Con_DPrintf("NudgeOutOfSolid fixed stuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), PRVM_serveredictvector(ent, origin)[0] - start[0], PRVM_serveredictvector(ent, origin)[1] - start[1], PRVM_serveredictvector(ent, origin)[2] - start[2]);
1604 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1605 VectorAdd(start, push, end);
1606 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1608 // definitely not stuck in a bmodel, move may proceed
1611 else if (sv_gameplayfix_unstickentities.integer && SV_UnstickEntity(ent))
1613 // bones_was_here: pretty sure we can deprecate sv_gameplayfix_unstickentities, sv_gameplayfix_nudgeoutofsolid is much nicer
1614 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1615 VectorAdd(start, push, end);
1616 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1619 return true; // assuming stuck, bones_was_here TODO: always use PHYS_NudgeOutOfSolid (remove sv_gameplayfix_nudgeoutofsolid)?
1622 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1624 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1629 if(!trace->startsolid)
1630 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)
1632 Con_Printf("something eeeeevil happened\n");
1638 SV_LinkEdict_TouchAreaGrid(ent);
1640 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))))
1641 SV_Impact (ent, trace);
1644 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1646 ent->priv.required->mark = 0;
1649 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1651 ent->priv.required->mark = 0;
1656 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1668 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1670 prvm_prog_t *prog = SVVM_prog;
1672 int pusherowner, pusherprog;
1675 float savesolid, movetime2, pushltime;
1676 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1678 int numcheckentities;
1679 static prvm_edict_t *checkentities[MAX_EDICTS];
1680 model_t *pushermodel;
1681 trace_t trace, trace2;
1682 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1683 static unsigned short moved_edicts[MAX_EDICTS];
1686 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])
1688 PRVM_serveredictfloat(pusher, ltime) += movetime;
1692 switch ((int) PRVM_serveredictfloat(pusher, solid))
1694 // LadyHavoc: valid pusher types
1697 case SOLID_SLIDEBOX:
1698 case SOLID_CORPSE: // LadyHavoc: this would be weird...
1700 // LadyHavoc: no collisions
1703 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1704 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1705 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1706 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1707 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1708 PRVM_serveredictfloat(pusher, ltime) += movetime;
1709 SV_LinkEdict(pusher);
1712 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1715 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1716 if (index < 1 || index >= MAX_MODELS)
1718 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1721 pushermodel = SV_GetModelByIndex(index);
1722 pusherowner = PRVM_serveredictedict(pusher, owner);
1723 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1725 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1727 movetime2 = movetime;
1728 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1729 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1730 if (moveangle[0] || moveangle[2])
1732 for (i = 0;i < 3;i++)
1736 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1737 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1741 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1742 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1746 else if (moveangle[1])
1748 for (i = 0;i < 3;i++)
1752 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1753 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1757 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1758 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1764 for (i = 0;i < 3;i++)
1768 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1769 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1773 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1774 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1779 VectorNegate (moveangle, a);
1780 AngleVectorsFLU (a, forward, left, up);
1782 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1783 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1784 pushltime = PRVM_serveredictfloat(pusher, ltime);
1786 // move the pusher to its final position
1788 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1789 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1790 PRVM_serveredictfloat(pusher, ltime) += movetime;
1791 SV_LinkEdict(pusher);
1793 pushermodel = SV_GetModelFromEdict(pusher);
1794 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);
1795 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1797 savesolid = PRVM_serveredictfloat(pusher, solid);
1799 // see if any solid entities are inside the final position
1802 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1803 numcheckentities = 0;
1804 else // MOVETYPE_PUSH
1805 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1806 for (e = 0;e < numcheckentities;e++)
1808 prvm_edict_t *check = checkentities[e];
1809 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1814 case MOVETYPE_FOLLOW:
1815 case MOVETYPE_NOCLIP:
1816 case MOVETYPE_FLY_WORLDONLY:
1822 if (PRVM_serveredictedict(check, owner) == pusherprog)
1825 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1828 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1830 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1831 check->priv.server->waterposition_forceupdate = true;
1833 checkcontents = SV_GenericHitSuperContentsMask(check);
1835 // if the entity is standing on the pusher, it will definitely be moved
1836 // if the entity is not standing on the pusher, but is in the pusher's
1837 // final position, move it
1838 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1840 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1841 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1842 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1843 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1844 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1845 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);
1846 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1847 if (!trace.startsolid)
1849 //Con_Printf("- not in solid\n");
1854 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1855 //VectorClear(pivot);
1860 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1861 VectorAdd (org, pivot, org);
1862 org2[0] = DotProduct (org, forward);
1863 org2[1] = DotProduct (org, left);
1864 org2[2] = DotProduct (org, up);
1865 VectorSubtract (org2, org, move);
1866 VectorAdd (move, move1, move);
1869 VectorCopy (move1, move);
1871 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1873 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1874 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1875 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1877 // physics objects need better collisions than this code can do
1878 if (movetype == MOVETYPE_PHYSICS)
1880 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1881 SV_LinkEdict(check);
1882 SV_LinkEdict_TouchAreaGrid(check);
1886 // try moving the contacted entity
1887 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1888 if(!SV_PushEntity(&trace, check, move, true, true))
1890 // entity "check" got teleported
1891 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1892 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1893 continue; // pushed enough
1895 // FIXME: turn players specially
1896 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1897 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1898 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1900 // this trace.fraction < 1 check causes items to fall off of pushers
1901 // if they pass under or through a wall
1902 // the groundentity check causes items to fall off of ledges
1903 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1904 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1906 // if it is still inside the pusher, block
1907 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1908 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1909 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1910 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1911 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1912 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);
1913 if (trace.startsolid)
1916 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1918 // hack to invoke all necessary movement triggers
1920 if(!SV_PushEntity(&trace2, check, move2, true, true))
1922 // entity "check" got teleported
1929 // still inside pusher, so it's really blocked
1932 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1934 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1937 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1938 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1942 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1943 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1944 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1945 SV_LinkEdict(pusher);
1947 // move back any entities we already moved
1948 for (i = 0;i < num_moved;i++)
1950 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1951 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1952 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1956 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1957 if (PRVM_serveredictfunction(pusher, blocked))
1959 PRVM_serverglobalfloat(time) = sv.time;
1960 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1961 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1962 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1967 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1968 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1969 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1978 static void SV_Physics_Pusher (prvm_edict_t *ent)
1980 prvm_prog_t *prog = SVVM_prog;
1981 prvm_vec_t thinktime, oldltime, movetime;
1983 oldltime = PRVM_serveredictfloat(ent, ltime);
1985 thinktime = PRVM_serveredictfloat(ent, nextthink);
1986 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1988 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1993 movetime = sv.frametime;
1996 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
1997 SV_PushMove (ent, movetime);
1999 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
2001 PRVM_serveredictfloat(ent, nextthink) = 0;
2002 PRVM_serverglobalfloat(time) = sv.time;
2003 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2004 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
2005 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2011 ===============================================================================
2015 ===============================================================================
2018 static float unstickoffsets[] =
2020 // poutting -/+z changes first as they are least weird
2035 typedef enum unstickresult_e
2043 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2045 prvm_prog_t *prog = SVVM_prog;
2048 // if not stuck in a bmodel, just return
2049 if (!SV_TestEntityPosition(ent, vec3_origin))
2050 return UNSTICK_GOOD;
2052 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2054 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2056 VectorCopy(unstickoffsets + i, offset);
2058 //SV_LinkEdict_TouchAreaGrid(ent);
2059 return UNSTICK_UNSTUCK;
2063 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2064 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2066 for(i = 2; i <= maxunstick; ++i)
2068 VectorClear(offset);
2070 if (!SV_TestEntityPosition(ent, offset))
2073 //SV_LinkEdict_TouchAreaGrid(ent);
2074 return UNSTICK_UNSTUCK;
2077 if (!SV_TestEntityPosition(ent, offset))
2080 //SV_LinkEdict_TouchAreaGrid(ent);
2081 return UNSTICK_UNSTUCK;
2085 return UNSTICK_STUCK;
2088 qbool SV_UnstickEntity (prvm_edict_t *ent)
2090 prvm_prog_t *prog = SVVM_prog;
2092 switch(SV_UnstickEntityReturnOffset(ent, offset))
2096 case UNSTICK_UNSTUCK:
2097 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]);
2100 if (developer_extra.integer)
2101 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2104 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2113 This is a big hack to try and fix the rare case of getting stuck in the world
2117 static void SV_CheckStuck (prvm_edict_t *ent)
2119 prvm_prog_t *prog = SVVM_prog;
2122 switch(SV_UnstickEntityReturnOffset(ent, offset))
2125 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2127 case UNSTICK_UNSTUCK:
2128 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]);
2131 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2132 if (!SV_TestEntityPosition(ent, offset))
2134 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)));
2136 //SV_LinkEdict_TouchAreaGrid(ent);
2139 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2142 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2152 static qbool SV_CheckWater (prvm_edict_t *ent)
2154 prvm_prog_t *prog = SVVM_prog;
2156 int nNativeContents;
2159 point[0] = PRVM_serveredictvector(ent, origin)[0];
2160 point[1] = PRVM_serveredictvector(ent, origin)[1];
2161 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2163 // DRESK - Support for Entity Contents Transition Event
2164 // NOTE: Some logic needed to be slightly re-ordered
2165 // to not affect performance and allow for the feature.
2167 // Acquire Super Contents Prior to Resets
2168 cont = SV_PointSuperContents(point);
2169 // Acquire Native Contents Here
2170 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(cont);
2172 // DRESK - Support for Entity Contents Transition Event
2173 if(PRVM_serveredictfloat(ent, watertype))
2174 // Entity did NOT Spawn; Check
2175 SV_CheckContentsTransition(ent, nNativeContents);
2178 PRVM_serveredictfloat(ent, waterlevel) = 0;
2179 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2180 cont = SV_PointSuperContents(point);
2181 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2183 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2184 PRVM_serveredictfloat(ent, waterlevel) = 1;
2185 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2186 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2188 PRVM_serveredictfloat(ent, waterlevel) = 2;
2189 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2190 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2191 PRVM_serveredictfloat(ent, waterlevel) = 3;
2195 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2204 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2206 prvm_prog_t *prog = SVVM_prog;
2208 vec3_t forward, into, side, v_angle;
2210 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2211 AngleVectors (v_angle, forward, NULL, NULL);
2212 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2214 // cut the tangential velocity
2215 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2216 VectorScale (stepnormal, i, into);
2217 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2218 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2219 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2225 =====================
2228 Player has come to a dead stop, possibly due to the problem with limited
2229 float precision at some angle joins in the BSP hull.
2231 Try fixing by pushing one pixel in each direction.
2233 This is a hack, but in the interest of good gameplay...
2234 ======================
2236 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2241 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2244 for (i=0 ; i<8 ; i++)
2246 // try pushing a little in an axial direction
2249 case 0: dir[0] = 2; dir[1] = 0; break;
2250 case 1: dir[0] = 0; dir[1] = 2; break;
2251 case 2: dir[0] = -2; dir[1] = 0; break;
2252 case 3: dir[0] = 0; dir[1] = -2; break;
2253 case 4: dir[0] = 2; dir[1] = 2; break;
2254 case 5: dir[0] = -2; dir[1] = 2; break;
2255 case 6: dir[0] = 2; dir[1] = -2; break;
2256 case 7: dir[0] = -2; dir[1] = -2; break;
2259 SV_PushEntity (&trace, ent, dir, false, true);
2261 // retry the original move
2262 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2263 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2264 PRVM_serveredictvector(ent, velocity)[2] = 0;
2265 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2267 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2268 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2270 Con_DPrint("TryUnstick - success.\n");
2274 // go back to the original pos and try again
2275 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2279 VectorClear (PRVM_serveredictvector(ent, velocity));
2280 Con_DPrint("TryUnstick - failure.\n");
2286 =====================
2289 Only used by players
2290 ======================
2292 static void SV_WalkMove (prvm_edict_t *ent)
2294 prvm_prog_t *prog = SVVM_prog;
2297 //int originalmove_clip;
2298 int originalmove_flags;
2299 int originalmove_groundentity;
2300 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2301 int skipsupercontentsmask = 0;
2302 int skipmaterialflagsmask = 0;
2304 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2305 trace_t downtrace, trace;
2308 // if frametime is 0 (due to client sending the same timestamp twice),
2310 if (sv.frametime <= 0)
2313 if (sv_gameplayfix_unstickplayers.integer)
2314 SV_CheckStuck (ent);
2316 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2318 SV_CheckVelocity(ent);
2320 // do a regular slide move unless it looks like you ran into a step
2321 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2323 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2324 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2326 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2328 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2331 // only try this if there was no floor in the way in the trace (no,
2332 // this check seems to be not REALLY necessary, because if clip & 1,
2333 // our trace will hit that thing too)
2334 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2335 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2336 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2337 type = MOVE_MISSILE;
2338 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2339 type = MOVE_WORLDONLY;
2340 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2341 type = MOVE_NOMONSTERS; // only clip against bmodels
2344 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2345 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2346 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
2347 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2349 clip |= 1; // but we HAVE found a floor
2350 // set groundentity so we get carried when walking onto a mover with sv_gameplayfix_nogravityonground
2351 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2355 // if the move did not hit the ground at any point, we're not on ground
2357 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2359 SV_CheckVelocity(ent);
2361 SV_LinkEdict_TouchAreaGrid(ent);
2363 if(clip & 8) // teleport
2366 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2369 if (sv_nostep.integer)
2372 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2373 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2374 //originalmove_clip = clip;
2375 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2376 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2378 // if move didn't block on a step, return
2381 // if move was not trying to move into the step, return
2382 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2385 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2387 // return if gibbed by a trigger
2388 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2391 // return if attempting to jump while airborn (unless sv_jumpstep)
2392 if (!sv_jumpstep.integer)
2393 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2397 // try moving up and forward to go up a step
2398 // back to start pos
2399 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2400 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2403 VectorClear (upmove);
2404 upmove[2] = sv_stepheight.value;
2405 if(!SV_PushEntity(&trace, ent, upmove, true, true))
2407 // we got teleported when upstepping... must abort the move
2412 PRVM_serveredictvector(ent, velocity)[2] = 0;
2413 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 0);
2414 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2417 // we got teleported when upstepping... must abort the move
2418 // note that z velocity handling may not be what QC expects here, but we cannot help it
2422 SV_CheckVelocity(ent);
2424 SV_LinkEdict_TouchAreaGrid(ent);
2426 // check for stuckness, possibly due to the limited precision of floats
2427 // in the clipping hulls
2429 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2430 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2432 //Con_Printf("wall\n");
2433 // stepping up didn't make any progress, revert to original move
2434 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2435 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2436 //clip = originalmove_clip;
2437 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2438 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2439 // now try to unstick if needed
2440 //clip = SV_TryUnstick (ent, oldvel);
2444 //Con_Printf("step - ");
2446 // extra friction based on view angle
2447 if (clip & 2 && sv_wallfriction.integer)
2448 SV_WallFriction (ent, stepnormal);
2450 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2451 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))
2455 VectorClear (downmove);
2456 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2457 if(!SV_PushEntity(&downtrace, ent, downmove, true, true))
2459 // we got teleported when downstepping... must abort the move
2463 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2465 // this has been disabled so that you can't jump when you are stepping
2466 // up while already jumping (also known as the Quake2 double jump bug)
2468 // LadyHavoc: disabled this check so you can walk on monsters/players
2469 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2471 //Con_Printf("onground\n");
2472 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2473 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2479 //Con_Printf("slope\n");
2480 // if the push down didn't end up on good ground, use the move without
2481 // the step up. This happens near wall / slope combinations, and can
2482 // cause the player to hop up higher on a slope too steep to climb
2483 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2484 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2485 //clip = originalmove_clip;
2486 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2487 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2490 SV_CheckVelocity(ent);
2492 SV_LinkEdict_TouchAreaGrid(ent);
2495 //============================================================================
2501 Entities that are "stuck" to another entity
2504 static void SV_Physics_Follow (prvm_edict_t *ent)
2506 prvm_prog_t *prog = SVVM_prog;
2507 vec3_t vf, vr, vu, angles, v;
2510 // LadyHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2511 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2512 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])
2514 // quick case for no rotation
2515 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2519 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2520 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2521 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2522 AngleVectors (angles, vf, vr, vu);
2523 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];
2524 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];
2525 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];
2526 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2527 angles[1] = PRVM_serveredictvector(e, angles)[1];
2528 angles[2] = PRVM_serveredictvector(e, angles)[2];
2529 AngleVectors (angles, vf, vr, vu);
2530 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2531 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2532 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2534 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2536 //SV_LinkEdict_TouchAreaGrid(ent);
2540 ==============================================================================
2544 ==============================================================================
2549 SV_CheckWaterTransition
2553 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2556 prvm_prog_t *prog = SVVM_prog;
2557 // 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
2559 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2560 cont = Mod_Q1BSP_NativeContentsFromSuperContents(SV_PointSuperContents(entorigin));
2561 if (!PRVM_serveredictfloat(ent, watertype))
2563 // just spawned here
2564 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2566 PRVM_serveredictfloat(ent, watertype) = cont;
2567 PRVM_serveredictfloat(ent, waterlevel) = 1;
2571 // DRESK - Support for Entity Contents Transition Event
2572 // NOTE: Call here BEFORE updating the watertype below,
2573 // and suppress watersplash sound if a valid function
2574 // call was made to allow for custom "splash" sounds.
2575 else if( !SV_CheckContentsTransition(ent, cont) )
2576 { // Contents Transition Function Invalid; Potentially Play Water Sound
2577 // check if the entity crossed into or out of water
2578 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2579 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2582 if (cont <= CONTENTS_WATER)
2584 PRVM_serveredictfloat(ent, watertype) = cont;
2585 PRVM_serveredictfloat(ent, waterlevel) = 1;
2589 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2590 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2598 Toss, bounce, and fly movement. When onground, do nothing.
2602 void SV_Physics_Toss (prvm_edict_t *ent)
2604 prvm_prog_t *prog = SVVM_prog;
2609 prvm_edict_t *groundentity;
2610 float d, ent_gravity;
2614 // if onground, return without moving
2615 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2617 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2618 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2620 // don't stick to ground if onground and moving upward
2621 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2623 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2625 // we can trust FL_ONGROUND if groundentity is world because it never moves
2628 else if (ent->priv.server->suspendedinairflag && groundentity->free)
2630 // if ent was supported by a brush model on previous frame,
2631 // and groundentity is now freed, set groundentity to 0 (world)
2632 // which leaves it suspended in the air
2633 PRVM_serveredictedict(ent, groundentity) = 0;
2634 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2637 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2639 // don't slide if still touching the groundentity
2643 ent->priv.server->suspendedinairflag = false;
2645 SV_CheckVelocity (ent);
2648 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2649 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2652 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2654 movetime = sv.frametime;
2655 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2658 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2659 // The buzzsaw traps in r2m6 and r2m7 use MOVETYPE_FLY and rely on moving while stuck in the world.
2660 // Quake movetypes checked allsolid only in SV_FlyMove().
2661 if(!SV_PushEntity(&trace, ent, move, true, PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY))
2662 return; // teleported
2665 if (trace.fraction == 1)
2667 movetime *= 1 - min(1, trace.fraction);
2668 switch((int)PRVM_serveredictfloat(ent, movetype))
2670 case MOVETYPE_BOUNCEMISSILE:
2671 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2673 bouncefactor = 1.0f;
2675 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2676 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2677 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2680 case MOVETYPE_BOUNCE:
2681 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2683 bouncefactor = 0.5f;
2685 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2687 bouncestop = 60.0f / 800.0f;
2689 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2690 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2693 // LadyHavoc: fixed grenades not bouncing when fired down a slope
2694 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2695 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2697 d = PRVM_serveredictvector(ent, velocity)[2];
2698 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2700 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2701 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2702 VectorClear(PRVM_serveredictvector(ent, velocity));
2703 VectorClear(PRVM_serveredictvector(ent, avelocity));
2708 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2709 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2714 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2715 if (trace.plane.normal[2] > 0.7)
2717 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2718 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2719 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2720 ent->priv.server->suspendedinairflag = true;
2721 VectorClear (PRVM_serveredictvector(ent, velocity));
2722 VectorClear (PRVM_serveredictvector(ent, avelocity));
2727 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2728 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2735 // check for in water
2736 SV_CheckWaterTransition (ent);
2740 ===============================================================================
2744 ===============================================================================
2751 Monsters freefall when they don't have a ground entity, otherwise
2752 all movement is done with discrete steps.
2754 This is also used for objects that have become still on the ground, but
2755 will fall if the floor is pulled out from under them.
2758 static void SV_Physics_Step (prvm_edict_t *ent)
2760 prvm_prog_t *prog = SVVM_prog;
2761 int flags = (int)PRVM_serveredictfloat(ent, flags);
2764 // Backup Velocity in the event that movetypesteplandevent is called,
2765 // to provide a parameter with the entity's velocity at impact.
2766 vec3_t backupVelocity;
2767 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2768 // don't fall at all if fly/swim
2769 if (!(flags & (FL_FLY | FL_SWIM)))
2771 if (flags & FL_ONGROUND)
2773 // freefall if onground and moving upward
2774 // freefall if not standing on a world surface (it may be a lift or trap door)
2775 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2777 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2778 SV_CheckVelocity(ent);
2779 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2781 SV_LinkEdict_TouchAreaGrid(ent);
2782 ent->priv.server->waterposition_forceupdate = true;
2787 // freefall if not onground
2788 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2790 SV_CheckVelocity(ent);
2791 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2793 SV_LinkEdict_TouchAreaGrid(ent);
2796 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2798 // DRESK - Check for Entity Land Event Function
2799 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2800 { // Valid Function; Execute
2801 // Prepare Parameters
2802 // Assign Velocity at Impact
2803 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2804 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2805 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2807 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2809 PRVM_serverglobalfloat(time) = sv.time;
2810 // Execute VM Function
2811 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2814 // Check for Engine Landing Sound
2815 if(sv_sound_land.string)
2816 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2818 ent->priv.server->waterposition_forceupdate = true;
2823 //============================================================================
2825 static void SV_Physics_Entity (prvm_edict_t *ent)
2827 prvm_prog_t *prog = SVVM_prog;
2828 // don't run think/move on newly spawned projectiles as it messes up
2829 // movement interpolation and rocket trails, and is inconsistent with
2830 // respect to entities spawned in the same frame
2831 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2832 // but if it spawns a lower numbered ent, it doesn't - this never moves
2833 // ents in the first frame regardless)
2834 qbool runmove = ent->priv.server->move;
2835 ent->priv.server->move = true;
2836 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2838 switch ((int) PRVM_serveredictfloat(ent, movetype))
2841 case MOVETYPE_FAKEPUSH:
2842 SV_Physics_Pusher (ent);
2845 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2846 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2849 case MOVETYPE_FOLLOW:
2850 if(SV_RunThink(ent))
2851 SV_Physics_Follow (ent);
2853 case MOVETYPE_NOCLIP:
2854 if (SV_RunThink(ent))
2857 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2858 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2863 SV_Physics_Step (ent);
2865 if (SV_RunThink(ent))
2866 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2868 ent->priv.server->waterposition_forceupdate = false;
2869 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2870 SV_CheckWaterTransition(ent);
2874 if (SV_RunThink (ent))
2878 case MOVETYPE_BOUNCE:
2879 case MOVETYPE_BOUNCEMISSILE:
2880 case MOVETYPE_FLYMISSILE:
2882 case MOVETYPE_FLY_WORLDONLY:
2884 if (SV_RunThink (ent))
2885 SV_Physics_Toss (ent);
2887 case MOVETYPE_PHYSICS:
2888 if (SV_RunThink(ent))
2891 SV_LinkEdict_TouchAreaGrid(ent);
2895 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2897 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2902 static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
2904 prvm_prog_t *prog = SVVM_prog;
2906 // don't run think at all, that is done during server frames
2907 // instead, call the movetypes directly so they match client input
2909 // This probably only makes sense for CSQC-networked (SendEntity field set) player entities
2910 switch ((int) PRVM_serveredictfloat(ent, movetype))
2913 case MOVETYPE_FAKEPUSH:
2914 // push physics relies heavily on think times and calls, and so cannot be predicted currently
2915 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2919 case MOVETYPE_FOLLOW:
2920 SV_Physics_Follow (ent);
2922 case MOVETYPE_NOCLIP:
2923 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2924 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2927 SV_Physics_Step (ent);
2933 case MOVETYPE_BOUNCE:
2934 case MOVETYPE_BOUNCEMISSILE:
2935 case MOVETYPE_FLYMISSILE:
2936 SV_Physics_Toss (ent);
2939 case MOVETYPE_FLY_WORLDONLY:
2942 case MOVETYPE_PHYSICS:
2945 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2947 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2952 // asynchronous path
2953 void SV_Physics_ClientMove(void)
2955 prvm_prog_t *prog = SVVM_prog;
2957 ent = host_client->edict;
2959 // call player physics, this needs the proper frametime
2960 PRVM_serverglobalfloat(frametime) = sv.frametime;
2963 // call standard client pre-think, with frametime = 0
2964 PRVM_serverglobalfloat(time) = sv.time;
2965 PRVM_serverglobalfloat(frametime) = 0;
2966 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2967 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2968 PRVM_serverglobalfloat(frametime) = sv.frametime;
2970 // make sure the velocity is sane (not a NaN)
2971 SV_CheckVelocity(ent);
2973 // perform movetype behaviour
2974 // note: will always be MOVETYPE_WALK if disableclientprediction = 0
2975 SV_Physics_ClientEntity_NoThink (ent);
2977 // call standard player post-think, with frametime = 0
2978 PRVM_serverglobalfloat(time) = sv.time;
2979 PRVM_serverglobalfloat(frametime) = 0;
2980 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2981 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2982 PRVM_serverglobalfloat(frametime) = sv.frametime;
2984 if(PRVM_serveredictfloat(ent, fixangle))
2986 // angle fixing was requested by physics code...
2987 // so store the current angles for later use
2988 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2989 host_client->fixangle_angles_set = true;
2991 // and clear fixangle for the next frame
2992 PRVM_serveredictfloat(ent, fixangle) = 0;
2996 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2998 prvm_prog_t *prog = SVVM_prog;
2999 // don't do physics on disconnected clients, FrikBot relies on this
3000 if (!host_client->begun)
3003 // make sure the velocity is sane (not a NaN)
3004 SV_CheckVelocity(ent);
3006 // don't run physics here if running asynchronously
3007 if (host_client->clmovement_inputtimeout <= 0)
3010 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
3013 // make sure the velocity is still sane (not a NaN)
3014 SV_CheckVelocity(ent);
3016 // call standard client pre-think
3017 PRVM_serverglobalfloat(time) = sv.time;
3018 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3019 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3021 // make sure the velocity is still sane (not a NaN)
3022 SV_CheckVelocity(ent);
3025 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3027 prvm_prog_t *prog = SVVM_prog;
3028 // don't do physics on disconnected clients, FrikBot relies on this
3029 if (!host_client->begun)
3032 // make sure the velocity is sane (not a NaN)
3033 SV_CheckVelocity(ent);
3035 // call standard player post-think
3036 PRVM_serverglobalfloat(time) = sv.time;
3037 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3038 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3040 // make sure the velocity is still sane (not a NaN)
3041 SV_CheckVelocity(ent);
3043 if(PRVM_serveredictfloat(ent, fixangle))
3045 // angle fixing was requested by physics code...
3046 // so store the current angles for later use
3047 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3048 host_client->fixangle_angles_set = true;
3050 // and clear fixangle for the next frame
3051 PRVM_serveredictfloat(ent, fixangle) = 0;
3054 // decrement the countdown variable used to decide when to go back to
3055 // synchronous physics
3056 if (host_client->clmovement_inputtimeout > sv.frametime)
3057 host_client->clmovement_inputtimeout -= sv.frametime;
3059 host_client->clmovement_inputtimeout = 0;
3062 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3064 prvm_prog_t *prog = SVVM_prog;
3065 // don't do physics on disconnected clients, FrikBot relies on this
3066 if (!host_client->begun)
3068 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3072 // make sure the velocity is sane (not a NaN)
3073 SV_CheckVelocity(ent);
3075 switch ((int) PRVM_serveredictfloat(ent, movetype))
3078 case MOVETYPE_FAKEPUSH:
3079 SV_Physics_Pusher (ent);
3082 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3083 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3086 case MOVETYPE_FOLLOW:
3088 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3089 SV_Physics_Follow (ent);
3091 case MOVETYPE_NOCLIP:
3093 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3096 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3097 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3101 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3102 SV_Physics_Step (ent);
3103 if (SV_RunThink(ent))
3104 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
3106 ent->priv.server->waterposition_forceupdate = false;
3107 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
3108 SV_CheckWaterTransition(ent);
3113 // don't run physics here if running asynchronously
3114 if (host_client->clmovement_inputtimeout <= 0)
3118 case MOVETYPE_BOUNCE:
3119 case MOVETYPE_BOUNCEMISSILE:
3120 case MOVETYPE_FLYMISSILE:
3123 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3124 SV_Physics_Toss (ent);
3127 case MOVETYPE_FLY_WORLDONLY:
3129 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3132 case MOVETYPE_PHYSICS:
3136 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
3138 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3142 SV_CheckVelocity (ent);
3145 SV_LinkEdict_TouchAreaGrid(ent);
3147 SV_CheckVelocity (ent);
3156 void SV_Physics (void)
3158 prvm_prog_t *prog = SVVM_prog;
3162 // free memory for resources that are no longer referenced
3163 PRVM_GarbageCollection(prog);
3165 // let the progs know that a new frame has started
3166 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3167 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3168 PRVM_serverglobalfloat(time) = sv.time;
3169 PRVM_serverglobalfloat(frametime) = sv.frametime;
3170 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3173 // run physics engine
3174 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3178 // treat each object in turn
3181 // if force_retouch, relink all the entities
3182 if (PRVM_serverglobalfloat(force_retouch) > 0)
3183 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3185 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3187 if (sv_gameplayfix_consistentplayerprethink.integer)
3189 // run physics on the client entities in 3 stages
3190 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3192 SV_Physics_ClientEntity_PreThink(ent);
3194 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3196 SV_Physics_ClientEntity(ent);
3198 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3200 SV_Physics_ClientEntity_PostThink(ent);
3204 // run physics on the client entities
3205 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3209 SV_Physics_ClientEntity_PreThink(ent);
3210 SV_Physics_ClientEntity(ent);
3211 SV_Physics_ClientEntity_PostThink(ent);
3216 // run physics on all the non-client entities
3217 if (!sv_freezenonclients.integer)
3219 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3221 SV_Physics_Entity(ent);
3222 // make a second pass to see if any ents spawned this frame and make
3223 // sure they run their move/think
3224 if (sv_gameplayfix_delayprojectiles.integer < 0)
3225 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3226 if (!ent->priv.server->move && !ent->free)
3227 SV_Physics_Entity(ent);
3230 if (PRVM_serverglobalfloat(force_retouch) > 0)
3231 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3233 // LadyHavoc: endframe support
3234 if (PRVM_serverfunction(EndFrame))
3236 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3237 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3238 PRVM_serverglobalfloat(time) = sv.time;
3239 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3242 // decrement prog->num_edicts if the highest number entities died
3243 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3245 if (!sv_freezenonclients.integer)
3246 sv.time += sv.frametime;