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
1115 Returns true if the push did not result in the entity being teleported by QC code.
1118 static qbool SV_Impact (prvm_edict_t *e1, trace_t *trace)
1120 prvm_prog_t *prog = SVVM_prog;
1121 int restorevm_tempstringsbuf_cursize;
1122 int old_self, old_other;
1123 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1125 e1->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1127 old_self = PRVM_serverglobaledict(self);
1128 old_other = PRVM_serverglobaledict(other);
1129 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1131 VM_SetTraceGlobals(prog, trace);
1133 if (!e1->free && !e2->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1135 PRVM_serverglobalfloat(time) = sv.time;
1136 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1137 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1138 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1141 if (!e1->free && !e2->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1143 PRVM_serverglobalfloat(time) = sv.time;
1144 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1145 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1146 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1147 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1148 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1149 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1150 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1151 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1152 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1153 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1154 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1157 PRVM_serverglobaledict(self) = old_self;
1158 PRVM_serverglobaledict(other) = old_other;
1159 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1161 if(e1->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1163 e1->priv.required->mark = 0;
1166 else if(e1->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1168 e1->priv.required->mark = 0;
1173 Con_Printf(CON_ERROR "The edict mark had been overwritten! Please debug this.\n");
1183 Slide off of the impacting object
1184 returns the blocked flags (1 = floor, 2 = step / wall)
1187 #define STOP_EPSILON 0.1
1188 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1193 backoff = -DotProduct (in, normal) * overbounce;
1194 VectorMA(in, backoff, normal, out);
1196 for (i = 0;i < 3;i++)
1197 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1206 The basic solid body movement clip that slides along multiple planes
1207 Returns the clipflags if the velocity was modified (hit something solid)
1211 8 = teleported by touch method
1212 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1215 static float SV_Gravity (prvm_edict_t *ent);
1216 static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dotouch, qbool checkstuck);
1217 #define MAX_CLIP_PLANES 5
1218 static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float stepheight)
1220 prvm_prog_t *prog = SVVM_prog;
1221 unsigned int i, j, numplanes, blocked, bumpcount;
1222 float d, time_left, gravity;
1223 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1224 prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
1233 VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1237 gravity = SV_Gravity(ent);
1239 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1241 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1242 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1244 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1249 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1250 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1253 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1255 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1258 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1259 if(!SV_PushEntity(&trace, ent, push, sv_gameplayfix_impactbeforeonground.integer, true))
1261 // we got teleported by a touch function
1262 // let's abort the move
1267 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
1268 // abort move if we're stuck in the world (and didn't make it out)
1269 if (trace.worldstartsolid && trace.allsolid)
1271 VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
1275 if (trace.fraction == 1)
1278 time_left *= 1 - trace.fraction;
1280 if (trace.plane.normal[2])
1282 if (trace.plane.normal[2] > 0.7)
1289 Con_Printf ("SV_FlyMove: !trace.ent");
1290 trace.ent = prog->edicts;
1293 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1294 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1297 else if (stepheight)
1299 // step - handle it immediately
1305 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1306 VectorSet(steppush, 0, 0, stepheight);
1307 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1308 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1309 if(!SV_PushEntity(&steptrace, ent, steppush, false, true))
1314 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1315 if(!SV_PushEntity(&steptrace2, ent, push, false, true))
1320 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1321 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1322 if(!SV_PushEntity(&steptrace3, ent, steppush, false, true))
1327 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1328 // accept the new position if it made some progress...
1329 // previously this checked if absolute distance >= 0.03125 which made stepping up unreliable
1330 if (PRVM_serveredictvector(ent, origin)[0] - org[0] || PRVM_serveredictvector(ent, origin)[1] - org[1])
1332 //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]);
1334 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1335 time_left *= 1 - trace.fraction;
1341 //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]);
1342 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1347 // step - return it to caller
1349 // save the trace for player extrafriction
1351 VectorCopy(trace.plane.normal, stepnormal);
1354 if (!sv_gameplayfix_impactbeforeonground.integer)
1356 // Unlike some other movetypes Quake's SV_FlyMove calls SV_Impact only after setting ONGROUND which id1 fiends rely on.
1357 // If we stepped up (sv_gameplayfix_stepmultipletimes) this will impact the steptrace2 plane instead of the original.
1358 if (PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace.ent)
1359 if (!SV_Impact(ent, &trace))
1365 return blocked; // removed by the impact function
1368 if (trace.fraction >= 0.001)
1370 // actually covered some distance
1371 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1375 // clipped to another plane
1376 if (numplanes >= MAX_CLIP_PLANES)
1378 // this shouldn't really happen
1379 VectorClear(PRVM_serveredictvector(ent, velocity));
1385 for (i = 0;i < numplanes;i++)
1386 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1390 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1395 VectorCopy(trace.plane.normal, planes[numplanes]);
1398 // modify original_velocity so it parallels all of the clip planes
1399 for (i = 0;i < numplanes;i++)
1401 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1402 for (j = 0;j < numplanes;j++)
1407 if (DotProduct(new_velocity, planes[j]) < 0)
1417 // go along this plane
1418 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1422 // go along the crease
1425 VectorClear(PRVM_serveredictvector(ent, velocity));
1429 CrossProduct(planes[0], planes[1], dir);
1430 // LadyHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1431 VectorNormalize(dir);
1432 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1433 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1436 // if current velocity is against the original velocity,
1437 // stop dead to avoid tiny occilations in sloping corners
1438 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1440 VectorClear(PRVM_serveredictvector(ent, velocity));
1445 //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]);
1448 if ((blocked & 1) == 0 && bumpcount > 1)
1450 // LadyHavoc: fix the 'fall to your death in a wedge corner' glitch
1451 // flag ONGROUND if there's ground under it
1452 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
1456 // LadyHavoc: this came from QW and allows you to get out of water more easily
1457 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1458 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1462 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1464 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1465 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1478 static float SV_Gravity (prvm_edict_t *ent)
1480 prvm_prog_t *prog = SVVM_prog;
1483 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1486 return ent_gravity * sv_gravity.value * sv.frametime;
1491 ===============================================================================
1495 ===============================================================================
1498 static qbool SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1500 prvm_prog_t *prog = SVVM_prog;
1504 vec3_t stuckmins, stuckmaxs;
1505 vec3_t goodmins, goodmaxs;
1509 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1510 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1511 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1512 VectorCopy(pivot, goodmins);
1513 VectorCopy(pivot, goodmaxs);
1514 for (bump = 0;bump < 6;bump++)
1516 int coord = 2-(bump >> 1);
1517 //int coord = (bump >> 1);
1518 int dir = (bump & 1);
1521 for(subbump = 0; ; ++subbump)
1523 VectorCopy(stuckorigin, testorigin);
1527 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1532 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1535 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1536 if (stucktrace.bmodelstartsolid)
1538 // BAD BAD, can't fix that
1542 if (stucktrace.fraction >= 1)
1547 // BAD BAD, can't fix that
1551 // we hit something... let's move out of it
1552 VectorSubtract(stucktrace.endpos, testorigin, move);
1553 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1554 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1558 Con_Printf("subbump: %d\n", subbump);
1564 goodmaxs[coord] = stuckmaxs[coord];
1569 goodmins[coord] = stuckmins[coord];
1574 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1583 Does not change the entities velocity at all
1584 The trace struct is filled with the trace that has been done.
1585 Returns true if the push did not result in the entity being teleported by QC code.
1588 static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dotouch, qbool checkstuck)
1590 prvm_prog_t *prog = SVVM_prog;
1598 solid = (int)PRVM_serveredictfloat(ent, solid);
1599 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1600 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1601 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1603 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1604 VectorAdd(start, push, end);
1606 if (movetype == MOVETYPE_FLYMISSILE)
1607 type = MOVE_MISSILE;
1608 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1609 type = MOVE_WORLDONLY;
1610 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1611 type = MOVE_NOMONSTERS; // only clip against bmodels
1615 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1616 if (trace->allsolid && checkstuck)
1618 if (SV_UnstickEntity(ent))
1620 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1621 VectorAdd(start, push, end);
1622 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1624 // abort move if we're stuck in the world (and didn't make it out)
1625 else if (trace->worldstartsolid)
1629 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1630 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, oldorigin)); // for SV_UnstickEntity()
1635 if(!trace->startsolid)
1636 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)
1638 Con_Printf("something eeeeevil happened\n");
1644 SV_LinkEdict_TouchAreaGrid(ent);
1646 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))))
1647 return SV_Impact (ent, trace);
1660 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1662 prvm_prog_t *prog = SVVM_prog;
1664 int pusherowner, pusherprog;
1667 float savesolid, movetime2, pushltime;
1668 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1670 int numcheckentities;
1671 static prvm_edict_t *checkentities[MAX_EDICTS];
1672 model_t *pushermodel;
1673 trace_t trace, trace2;
1674 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1675 static unsigned short moved_edicts[MAX_EDICTS];
1678 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])
1680 PRVM_serveredictfloat(pusher, ltime) += movetime;
1684 switch ((int) PRVM_serveredictfloat(pusher, solid))
1686 // LadyHavoc: valid pusher types
1689 case SOLID_SLIDEBOX:
1690 case SOLID_CORPSE: // LadyHavoc: this would be weird...
1692 // LadyHavoc: no collisions
1695 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1696 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1697 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1698 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1699 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1700 PRVM_serveredictfloat(pusher, ltime) += movetime;
1701 SV_LinkEdict(pusher);
1704 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1707 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1708 if (index < 1 || index >= MAX_MODELS)
1710 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1713 pushermodel = SV_GetModelByIndex(index);
1714 pusherowner = PRVM_serveredictedict(pusher, owner);
1715 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1717 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1719 movetime2 = movetime;
1720 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1721 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1722 if (moveangle[0] || moveangle[2])
1724 for (i = 0;i < 3;i++)
1728 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1729 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1733 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1734 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1738 else if (moveangle[1])
1740 for (i = 0;i < 3;i++)
1744 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1745 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1749 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1750 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1756 for (i = 0;i < 3;i++)
1760 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1761 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1765 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1766 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1771 VectorNegate (moveangle, a);
1772 AngleVectorsFLU (a, forward, left, up);
1774 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1775 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1776 pushltime = PRVM_serveredictfloat(pusher, ltime);
1778 // move the pusher to its final position
1780 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1781 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1782 PRVM_serveredictfloat(pusher, ltime) += movetime;
1783 SV_LinkEdict(pusher);
1785 pushermodel = SV_GetModelFromEdict(pusher);
1786 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);
1787 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1789 savesolid = PRVM_serveredictfloat(pusher, solid);
1791 // see if any solid entities are inside the final position
1794 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1795 numcheckentities = 0;
1796 else // MOVETYPE_PUSH
1797 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1798 for (e = 0;e < numcheckentities;e++)
1800 prvm_edict_t *check = checkentities[e];
1801 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1806 case MOVETYPE_FOLLOW:
1807 case MOVETYPE_NOCLIP:
1808 case MOVETYPE_FLY_WORLDONLY:
1814 if (PRVM_serveredictedict(check, owner) == pusherprog)
1817 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1820 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1822 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1823 check->priv.server->waterposition_forceupdate = true;
1825 checkcontents = SV_GenericHitSuperContentsMask(check);
1827 // if the entity is standing on the pusher, it will definitely be moved
1828 // if the entity is not standing on the pusher, but is in the pusher's
1829 // final position, move it
1830 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1832 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1833 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1834 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1835 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1836 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1837 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);
1838 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1839 if (!trace.startsolid)
1841 //Con_Printf("- not in solid\n");
1846 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1847 //VectorClear(pivot);
1852 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1853 VectorAdd (org, pivot, org);
1854 org2[0] = DotProduct (org, forward);
1855 org2[1] = DotProduct (org, left);
1856 org2[2] = DotProduct (org, up);
1857 VectorSubtract (org2, org, move);
1858 VectorAdd (move, move1, move);
1861 VectorCopy (move1, move);
1863 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1865 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1866 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1867 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1869 // physics objects need better collisions than this code can do
1870 if (movetype == MOVETYPE_PHYSICS)
1872 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1873 SV_LinkEdict(check);
1874 SV_LinkEdict_TouchAreaGrid(check);
1878 // try moving the contacted entity
1879 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1880 if(!SV_PushEntity(&trace, check, move, true, true))
1882 // entity "check" got teleported
1883 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1884 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1885 continue; // pushed enough
1887 // FIXME: turn players specially
1888 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1889 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1890 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1892 // this trace.fraction < 1 check causes items to fall off of pushers
1893 // if they pass under or through a wall
1894 // the groundentity check causes items to fall off of ledges
1895 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1896 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1898 // if it is still inside the pusher, block
1899 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1900 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1901 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1902 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1903 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1904 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);
1905 if (trace.startsolid)
1908 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1910 // hack to invoke all necessary movement triggers
1912 if(!SV_PushEntity(&trace2, check, move2, true, true))
1914 // entity "check" got teleported
1921 // still inside pusher, so it's really blocked
1924 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1926 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1929 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1930 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1934 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1935 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1936 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1937 SV_LinkEdict(pusher);
1939 // move back any entities we already moved
1940 for (i = 0;i < num_moved;i++)
1942 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1943 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1944 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1948 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1949 if (PRVM_serveredictfunction(pusher, blocked))
1951 PRVM_serverglobalfloat(time) = sv.time;
1952 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1953 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1954 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1959 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1960 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1961 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1970 static void SV_Physics_Pusher (prvm_edict_t *ent)
1972 prvm_prog_t *prog = SVVM_prog;
1973 prvm_vec_t thinktime, oldltime, movetime;
1975 oldltime = PRVM_serveredictfloat(ent, ltime);
1977 thinktime = PRVM_serveredictfloat(ent, nextthink);
1978 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1980 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1985 movetime = sv.frametime;
1988 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
1989 SV_PushMove (ent, movetime);
1991 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
1993 PRVM_serveredictfloat(ent, nextthink) = 0;
1994 PRVM_serverglobalfloat(time) = sv.time;
1995 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1996 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1997 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2003 ===============================================================================
2007 ===============================================================================
2010 static float unstickoffsets[] =
2012 // poutting -/+z changes first as they are least weird
2027 typedef enum unstickresult_e
2029 // matching the DP_QC_NUDGEOUTOFSOLID return values
2031 UNSTICK_GOOD = -1, ///< didn't need to be unstuck
2036 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2038 prvm_prog_t *prog = SVVM_prog;
2041 // if not stuck in a bmodel, just return
2042 if (!SV_TestEntityPosition(ent, vec3_origin))
2043 return UNSTICK_GOOD;
2045 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2047 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2049 VectorCopy(unstickoffsets + i, offset);
2050 return UNSTICK_UNSTUCK;
2054 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2055 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2057 for(i = 2; i <= maxunstick; ++i)
2059 VectorClear(offset);
2061 if (!SV_TestEntityPosition(ent, offset))
2062 return UNSTICK_UNSTUCK;
2064 if (!SV_TestEntityPosition(ent, offset))
2065 return UNSTICK_UNSTUCK;
2068 return UNSTICK_STUCK;
2075 This is a big hack to try and fix the rare case of getting stuck in the world
2079 qbool SV_UnstickEntity (prvm_edict_t *ent)
2081 prvm_prog_t *prog = SVVM_prog;
2084 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0
2085 && sv.worldmodel->TraceBox == Mod_CollisionBIH_TraceBox) // Mod_Q1BSP_TraceBox doesn't support startdepth
2087 VectorCopy(PRVM_serveredictvector(ent, origin), offset);
2088 switch (PHYS_NudgeOutOfSolid(prog, ent))
2092 case UNSTICK_UNSTUCK:
2093 VectorSubtract(PRVM_serveredictvector(ent, origin), offset, offset);
2094 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)), offset[0], offset[1], offset[2]);
2097 Con_DPrintf(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)));
2100 Con_Printf("NudgeOutOfSolid returned a value outside its enum.\n");
2105 if (!(PRVM_NUM_FOR_EDICT(ent) <= svs.maxclients ? sv_gameplayfix_unstickplayers : sv_gameplayfix_unstickentities).integer)
2108 switch(SV_UnstickEntityReturnOffset(ent, offset))
2112 case UNSTICK_UNSTUCK:
2113 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]);
2116 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2117 if (!SV_TestEntityPosition(ent, offset))
2119 Con_DPrintf("Unstuck entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2122 Con_DPrintf(CON_WARN "Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2125 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2136 static qbool SV_CheckWater (prvm_edict_t *ent)
2138 prvm_prog_t *prog = SVVM_prog;
2140 int nNativeContents;
2143 point[0] = PRVM_serveredictvector(ent, origin)[0];
2144 point[1] = PRVM_serveredictvector(ent, origin)[1];
2145 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2147 // DRESK - Support for Entity Contents Transition Event
2148 // NOTE: Some logic needed to be slightly re-ordered
2149 // to not affect performance and allow for the feature.
2151 // Acquire Super Contents Prior to Resets
2152 cont = SV_PointSuperContents(point);
2153 // Acquire Native Contents Here
2154 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(cont);
2156 // DRESK - Support for Entity Contents Transition Event
2157 if(PRVM_serveredictfloat(ent, watertype))
2158 // Entity did NOT Spawn; Check
2159 SV_CheckContentsTransition(ent, nNativeContents);
2162 PRVM_serveredictfloat(ent, waterlevel) = 0;
2163 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2164 cont = SV_PointSuperContents(point);
2165 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2167 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2168 PRVM_serveredictfloat(ent, waterlevel) = 1;
2169 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2170 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2172 PRVM_serveredictfloat(ent, waterlevel) = 2;
2173 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2174 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2175 PRVM_serveredictfloat(ent, waterlevel) = 3;
2179 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2188 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2190 prvm_prog_t *prog = SVVM_prog;
2192 vec3_t forward, into, side, v_angle;
2194 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2195 AngleVectors (v_angle, forward, NULL, NULL);
2196 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2198 // cut the tangential velocity
2199 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2200 VectorScale (stepnormal, i, into);
2201 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2202 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2203 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2209 =====================
2212 Player has come to a dead stop, possibly due to the problem with limited
2213 float precision at some angle joins in the BSP hull.
2215 Try fixing by pushing one pixel in each direction.
2217 This is a hack, but in the interest of good gameplay...
2218 ======================
2220 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2225 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2228 for (i=0 ; i<8 ; i++)
2230 // try pushing a little in an axial direction
2233 case 0: dir[0] = 2; dir[1] = 0; break;
2234 case 1: dir[0] = 0; dir[1] = 2; break;
2235 case 2: dir[0] = -2; dir[1] = 0; break;
2236 case 3: dir[0] = 0; dir[1] = -2; break;
2237 case 4: dir[0] = 2; dir[1] = 2; break;
2238 case 5: dir[0] = -2; dir[1] = 2; break;
2239 case 6: dir[0] = 2; dir[1] = -2; break;
2240 case 7: dir[0] = -2; dir[1] = -2; break;
2243 SV_PushEntity (&trace, ent, dir, false, true);
2245 // retry the original move
2246 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2247 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2248 PRVM_serveredictvector(ent, velocity)[2] = 0;
2249 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2251 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2252 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2254 Con_DPrint("TryUnstick - success.\n");
2258 // go back to the original pos and try again
2259 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2263 VectorClear (PRVM_serveredictvector(ent, velocity));
2264 Con_DPrint("TryUnstick - failure.\n");
2270 =====================
2273 Only used by players
2274 ======================
2276 static void SV_WalkMove (prvm_edict_t *ent)
2278 prvm_prog_t *prog = SVVM_prog;
2281 //int originalmove_clip;
2282 int originalmove_flags;
2283 int originalmove_groundentity;
2284 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2285 int skipsupercontentsmask = 0;
2286 int skipmaterialflagsmask = 0;
2288 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2289 trace_t downtrace, trace;
2292 // if frametime is 0 (due to client sending the same timestamp twice),
2294 if (sv.frametime <= 0)
2297 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2299 SV_CheckVelocity(ent);
2301 // do a regular slide move unless it looks like you ran into a step
2302 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2304 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2305 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2307 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2309 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2312 // only try this if there was no floor in the way in the trace (no,
2313 // this check seems to be not REALLY necessary, because if clip & 1,
2314 // our trace will hit that thing too)
2315 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2316 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2317 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2318 type = MOVE_MISSILE;
2319 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2320 type = MOVE_WORLDONLY;
2321 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2322 type = MOVE_NOMONSTERS; // only clip against bmodels
2325 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2326 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2327 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
2328 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2330 clip |= 1; // but we HAVE found a floor
2331 // set groundentity so we get carried when walking onto a mover with sv_gameplayfix_nogravityonground
2332 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2336 // if the move did not hit the ground at any point, we're not on ground
2338 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2340 SV_CheckVelocity(ent);
2342 SV_LinkEdict_TouchAreaGrid(ent);
2344 if(clip & 8) // teleport
2347 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2350 if (sv_nostep.integer)
2353 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2354 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2355 //originalmove_clip = clip;
2356 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2357 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2359 // if move didn't block on a step, return
2362 // if move was not trying to move into the step, return
2363 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2366 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2368 // return if gibbed by a trigger
2369 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2372 // return if attempting to jump while airborn (unless sv_jumpstep)
2373 if (!sv_jumpstep.integer)
2374 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2378 // try moving up and forward to go up a step
2379 // back to start pos
2380 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2381 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2384 VectorClear (upmove);
2385 upmove[2] = sv_stepheight.value;
2386 if(!SV_PushEntity(&trace, ent, upmove, true, true))
2388 // we got teleported when upstepping... must abort the move
2393 PRVM_serveredictvector(ent, velocity)[2] = 0;
2394 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 0);
2395 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2398 // we got teleported when upstepping... must abort the move
2399 // note that z velocity handling may not be what QC expects here, but we cannot help it
2403 SV_CheckVelocity(ent);
2405 SV_LinkEdict_TouchAreaGrid(ent);
2407 // check for stuckness, possibly due to the limited precision of floats
2408 // in the clipping hulls
2410 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2411 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2413 //Con_Printf("wall\n");
2414 // stepping up didn't make any progress, revert to original move
2415 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2416 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2417 //clip = originalmove_clip;
2418 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2419 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2420 // now try to unstick if needed
2421 //clip = SV_TryUnstick (ent, oldvel);
2425 //Con_Printf("step - ");
2427 // extra friction based on view angle
2428 if (clip & 2 && sv_wallfriction.integer)
2429 SV_WallFriction (ent, stepnormal);
2431 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2432 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))
2436 VectorClear (downmove);
2437 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2438 if(!SV_PushEntity(&downtrace, ent, downmove, true, true))
2440 // we got teleported when downstepping... must abort the move
2444 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2446 // this has been disabled so that you can't jump when you are stepping
2447 // up while already jumping (also known as the Quake2 double jump bug)
2449 // LadyHavoc: disabled this check so you can walk on monsters/players
2450 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2452 //Con_Printf("onground\n");
2453 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2454 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2460 //Con_Printf("slope\n");
2461 // if the push down didn't end up on good ground, use the move without
2462 // the step up. This happens near wall / slope combinations, and can
2463 // cause the player to hop up higher on a slope too steep to climb
2464 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2465 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2466 //clip = originalmove_clip;
2467 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2468 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2471 SV_CheckVelocity(ent);
2473 SV_LinkEdict_TouchAreaGrid(ent);
2476 //============================================================================
2482 Entities that are "stuck" to another entity
2485 static void SV_Physics_Follow (prvm_edict_t *ent)
2487 prvm_prog_t *prog = SVVM_prog;
2488 vec3_t vf, vr, vu, angles, v;
2491 // LadyHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2492 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2493 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])
2495 // quick case for no rotation
2496 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2500 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2501 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2502 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2503 AngleVectors (angles, vf, vr, vu);
2504 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];
2505 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];
2506 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];
2507 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2508 angles[1] = PRVM_serveredictvector(e, angles)[1];
2509 angles[2] = PRVM_serveredictvector(e, angles)[2];
2510 AngleVectors (angles, vf, vr, vu);
2511 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2512 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2513 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2515 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2517 //SV_LinkEdict_TouchAreaGrid(ent);
2521 ==============================================================================
2525 ==============================================================================
2530 SV_CheckWaterTransition
2534 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2537 prvm_prog_t *prog = SVVM_prog;
2538 // 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
2540 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2541 cont = Mod_Q1BSP_NativeContentsFromSuperContents(SV_PointSuperContents(entorigin));
2542 if (!PRVM_serveredictfloat(ent, watertype))
2544 // just spawned here
2545 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2547 PRVM_serveredictfloat(ent, watertype) = cont;
2548 PRVM_serveredictfloat(ent, waterlevel) = 1;
2552 // DRESK - Support for Entity Contents Transition Event
2553 // NOTE: Call here BEFORE updating the watertype below,
2554 // and suppress watersplash sound if a valid function
2555 // call was made to allow for custom "splash" sounds.
2556 else if( !SV_CheckContentsTransition(ent, cont) )
2557 { // Contents Transition Function Invalid; Potentially Play Water Sound
2558 // check if the entity crossed into or out of water
2559 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2560 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2563 if (cont <= CONTENTS_WATER)
2565 PRVM_serveredictfloat(ent, watertype) = cont;
2566 PRVM_serveredictfloat(ent, waterlevel) = 1;
2570 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2571 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2579 Toss, bounce, and fly movement. When onground, do nothing.
2583 void SV_Physics_Toss (prvm_edict_t *ent)
2585 prvm_prog_t *prog = SVVM_prog;
2590 prvm_edict_t *groundentity;
2591 float d, ent_gravity;
2595 // if onground, return without moving
2596 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2598 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2599 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2601 // don't stick to ground if onground and moving upward
2602 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2604 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2606 // we can trust FL_ONGROUND if groundentity is world because it never moves
2609 else if (ent->priv.server->suspendedinairflag && groundentity->free)
2611 // if ent was supported by a brush model on previous frame,
2612 // and groundentity is now freed, set groundentity to 0 (world)
2613 // which leaves it suspended in the air
2614 PRVM_serveredictedict(ent, groundentity) = 0;
2615 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2618 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2620 // don't slide if still touching the groundentity
2624 ent->priv.server->suspendedinairflag = false;
2626 SV_CheckVelocity (ent);
2629 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2630 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2633 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2635 movetime = sv.frametime;
2636 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2639 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2640 // The buzzsaw traps in r2m6 and r2m7 use MOVETYPE_FLY and rely on moving while stuck in the world.
2641 // Quake movetypes checked allsolid only in SV_FlyMove().
2642 if(!SV_PushEntity(&trace, ent, move, true, PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY))
2643 return; // teleported
2646 if (trace.fraction == 1)
2648 movetime *= 1 - min(1, trace.fraction);
2649 switch((int)PRVM_serveredictfloat(ent, movetype))
2651 case MOVETYPE_BOUNCEMISSILE:
2652 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2654 bouncefactor = 1.0f;
2656 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2657 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2658 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2661 case MOVETYPE_BOUNCE:
2662 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2664 bouncefactor = 0.5f;
2666 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2668 bouncestop = 60.0f / 800.0f;
2670 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2671 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2674 // LadyHavoc: fixed grenades not bouncing when fired down a slope
2675 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2676 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2678 d = PRVM_serveredictvector(ent, velocity)[2];
2679 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2681 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2682 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2683 VectorClear(PRVM_serveredictvector(ent, velocity));
2684 VectorClear(PRVM_serveredictvector(ent, avelocity));
2689 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2690 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2695 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2696 if (trace.plane.normal[2] > 0.7)
2698 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2699 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2700 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2701 ent->priv.server->suspendedinairflag = true;
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)
2716 // check for in water
2717 SV_CheckWaterTransition (ent);
2721 ===============================================================================
2725 ===============================================================================
2732 Monsters freefall when they don't have a ground entity, otherwise
2733 all movement is done with discrete steps.
2735 This is also used for objects that have become still on the ground, but
2736 will fall if the floor is pulled out from under them.
2739 static void SV_Physics_Step (prvm_edict_t *ent)
2741 prvm_prog_t *prog = SVVM_prog;
2742 int flags = (int)PRVM_serveredictfloat(ent, flags);
2745 // Backup Velocity in the event that movetypesteplandevent is called,
2746 // to provide a parameter with the entity's velocity at impact.
2747 vec3_t backupVelocity;
2748 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2749 // don't fall at all if fly/swim
2750 if (!(flags & (FL_FLY | FL_SWIM)))
2752 if (flags & FL_ONGROUND)
2754 // freefall if onground and moving upward
2755 // freefall if not standing on a world surface (it may be a lift or trap door)
2756 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2758 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2759 SV_CheckVelocity(ent);
2760 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2762 SV_LinkEdict_TouchAreaGrid(ent);
2763 ent->priv.server->waterposition_forceupdate = true;
2768 // freefall if not onground
2769 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2771 SV_CheckVelocity(ent);
2772 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2774 SV_LinkEdict_TouchAreaGrid(ent);
2777 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2779 // DRESK - Check for Entity Land Event Function
2780 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2781 { // Valid Function; Execute
2782 // Prepare Parameters
2783 // Assign Velocity at Impact
2784 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2785 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2786 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2788 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2790 PRVM_serverglobalfloat(time) = sv.time;
2791 // Execute VM Function
2792 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2795 // Check for Engine Landing Sound
2796 if(sv_sound_land.string)
2797 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2799 ent->priv.server->waterposition_forceupdate = true;
2804 //============================================================================
2806 static void SV_Physics_Entity (prvm_edict_t *ent)
2808 prvm_prog_t *prog = SVVM_prog;
2809 // don't run think/move on newly spawned projectiles as it messes up
2810 // movement interpolation and rocket trails, and is inconsistent with
2811 // respect to entities spawned in the same frame
2812 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2813 // but if it spawns a lower numbered ent, it doesn't - this never moves
2814 // ents in the first frame regardless)
2815 qbool runmove = ent->priv.server->move;
2816 ent->priv.server->move = true;
2817 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2819 switch ((int) PRVM_serveredictfloat(ent, movetype))
2822 case MOVETYPE_FAKEPUSH:
2823 SV_Physics_Pusher (ent);
2826 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2827 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2830 case MOVETYPE_FOLLOW:
2831 if(SV_RunThink(ent))
2832 SV_Physics_Follow (ent);
2834 case MOVETYPE_NOCLIP:
2835 if (SV_RunThink(ent))
2838 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2839 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2844 SV_Physics_Step (ent);
2846 if (SV_RunThink(ent))
2847 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2849 ent->priv.server->waterposition_forceupdate = false;
2850 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2851 SV_CheckWaterTransition(ent);
2855 if (SV_RunThink (ent))
2859 case MOVETYPE_BOUNCE:
2860 case MOVETYPE_BOUNCEMISSILE:
2861 case MOVETYPE_FLYMISSILE:
2863 case MOVETYPE_FLY_WORLDONLY:
2865 if (SV_RunThink (ent))
2866 SV_Physics_Toss (ent);
2868 case MOVETYPE_PHYSICS:
2869 if (SV_RunThink(ent))
2872 SV_LinkEdict_TouchAreaGrid(ent);
2876 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2878 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2883 static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
2885 prvm_prog_t *prog = SVVM_prog;
2887 // don't run think at all, that is done during server frames
2888 // instead, call the movetypes directly so they match client input
2890 // This probably only makes sense for CSQC-networked (SendEntity field set) player entities
2891 switch ((int) PRVM_serveredictfloat(ent, movetype))
2894 case MOVETYPE_FAKEPUSH:
2895 // push physics relies heavily on think times and calls, and so cannot be predicted currently
2896 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2900 case MOVETYPE_FOLLOW:
2901 SV_Physics_Follow (ent);
2903 case MOVETYPE_NOCLIP:
2904 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2905 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2908 SV_Physics_Step (ent);
2914 case MOVETYPE_BOUNCE:
2915 case MOVETYPE_BOUNCEMISSILE:
2916 case MOVETYPE_FLYMISSILE:
2917 SV_Physics_Toss (ent);
2920 case MOVETYPE_FLY_WORLDONLY:
2923 case MOVETYPE_PHYSICS:
2926 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2928 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2933 // asynchronous path
2934 void SV_Physics_ClientMove(void)
2936 prvm_prog_t *prog = SVVM_prog;
2938 ent = host_client->edict;
2940 // call player physics, this needs the proper frametime
2941 PRVM_serverglobalfloat(frametime) = sv.frametime;
2944 // call standard client pre-think, with frametime = 0
2945 PRVM_serverglobalfloat(time) = sv.time;
2946 PRVM_serverglobalfloat(frametime) = 0;
2947 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2948 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2949 PRVM_serverglobalfloat(frametime) = sv.frametime;
2951 // make sure the velocity is sane (not a NaN)
2952 SV_CheckVelocity(ent);
2954 // perform movetype behaviour
2955 // note: will always be MOVETYPE_WALK if disableclientprediction = 0
2956 SV_Physics_ClientEntity_NoThink (ent);
2958 // call standard player post-think, with frametime = 0
2959 PRVM_serverglobalfloat(time) = sv.time;
2960 PRVM_serverglobalfloat(frametime) = 0;
2961 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2962 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2963 PRVM_serverglobalfloat(frametime) = sv.frametime;
2965 if(PRVM_serveredictfloat(ent, fixangle))
2967 // angle fixing was requested by physics code...
2968 // so store the current angles for later use
2969 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2970 host_client->fixangle_angles_set = true;
2972 // and clear fixangle for the next frame
2973 PRVM_serveredictfloat(ent, fixangle) = 0;
2977 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2979 prvm_prog_t *prog = SVVM_prog;
2980 // don't do physics on disconnected clients, FrikBot relies on this
2981 if (!host_client->begun)
2984 // make sure the velocity is sane (not a NaN)
2985 SV_CheckVelocity(ent);
2987 // don't run physics here if running asynchronously
2988 if (host_client->clmovement_inputtimeout <= 0)
2991 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2994 // make sure the velocity is still sane (not a NaN)
2995 SV_CheckVelocity(ent);
2997 // call standard client pre-think
2998 PRVM_serverglobalfloat(time) = sv.time;
2999 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3000 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3002 // make sure the velocity is still sane (not a NaN)
3003 SV_CheckVelocity(ent);
3006 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3008 prvm_prog_t *prog = SVVM_prog;
3009 // don't do physics on disconnected clients, FrikBot relies on this
3010 if (!host_client->begun)
3013 // make sure the velocity is sane (not a NaN)
3014 SV_CheckVelocity(ent);
3016 // call standard player post-think
3017 PRVM_serverglobalfloat(time) = sv.time;
3018 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3019 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3021 // make sure the velocity is still sane (not a NaN)
3022 SV_CheckVelocity(ent);
3024 if(PRVM_serveredictfloat(ent, fixangle))
3026 // angle fixing was requested by physics code...
3027 // so store the current angles for later use
3028 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3029 host_client->fixangle_angles_set = true;
3031 // and clear fixangle for the next frame
3032 PRVM_serveredictfloat(ent, fixangle) = 0;
3035 // decrement the countdown variable used to decide when to go back to
3036 // synchronous physics
3037 if (host_client->clmovement_inputtimeout > sv.frametime)
3038 host_client->clmovement_inputtimeout -= sv.frametime;
3040 host_client->clmovement_inputtimeout = 0;
3043 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3045 prvm_prog_t *prog = SVVM_prog;
3046 // don't do physics on disconnected clients, FrikBot relies on this
3047 if (!host_client->begun)
3049 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3053 // make sure the velocity is sane (not a NaN)
3054 SV_CheckVelocity(ent);
3056 switch ((int) PRVM_serveredictfloat(ent, movetype))
3059 case MOVETYPE_FAKEPUSH:
3060 SV_Physics_Pusher (ent);
3063 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3064 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3067 case MOVETYPE_FOLLOW:
3069 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3070 SV_Physics_Follow (ent);
3072 case MOVETYPE_NOCLIP:
3074 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3077 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3078 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3082 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3083 SV_Physics_Step (ent);
3084 if (SV_RunThink(ent))
3085 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
3087 ent->priv.server->waterposition_forceupdate = false;
3088 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
3089 SV_CheckWaterTransition(ent);
3094 // don't run physics here if running asynchronously
3095 if (host_client->clmovement_inputtimeout <= 0)
3099 case MOVETYPE_BOUNCE:
3100 case MOVETYPE_BOUNCEMISSILE:
3101 case MOVETYPE_FLYMISSILE:
3104 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3105 SV_Physics_Toss (ent);
3108 case MOVETYPE_FLY_WORLDONLY:
3110 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3113 case MOVETYPE_PHYSICS:
3117 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
3119 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3123 SV_CheckVelocity (ent);
3126 SV_LinkEdict_TouchAreaGrid(ent);
3128 SV_CheckVelocity (ent);
3137 void SV_Physics (void)
3139 prvm_prog_t *prog = SVVM_prog;
3143 // free memory for resources that are no longer referenced
3144 PRVM_GarbageCollection(prog);
3146 // let the progs know that a new frame has started
3147 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3148 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3149 PRVM_serverglobalfloat(time) = sv.time;
3150 PRVM_serverglobalfloat(frametime) = sv.frametime;
3151 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3154 // run physics engine
3155 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3159 // treat each object in turn
3162 // if force_retouch, relink all the entities
3163 if (PRVM_serverglobalfloat(force_retouch) > 0)
3164 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3166 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3168 if (sv_gameplayfix_consistentplayerprethink.integer)
3170 // run physics on the client entities in 3 stages
3171 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3173 SV_Physics_ClientEntity_PreThink(ent);
3175 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3177 SV_Physics_ClientEntity(ent);
3179 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3181 SV_Physics_ClientEntity_PostThink(ent);
3185 // run physics on the client entities
3186 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3190 SV_Physics_ClientEntity_PreThink(ent);
3191 SV_Physics_ClientEntity(ent);
3192 SV_Physics_ClientEntity_PostThink(ent);
3197 // run physics on all the non-client entities
3198 if (!sv_freezenonclients.integer)
3200 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3202 SV_Physics_Entity(ent);
3203 // make a second pass to see if any ents spawned this frame and make
3204 // sure they run their move/think
3205 if (sv_gameplayfix_delayprojectiles.integer < 0)
3206 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3207 if (!ent->priv.server->move && !ent->free)
3208 SV_Physics_Entity(ent);
3211 if (PRVM_serverglobalfloat(force_retouch) > 0)
3212 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3214 // LadyHavoc: endframe support
3215 if (PRVM_serverfunction(EndFrame))
3217 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3218 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3219 PRVM_serverglobalfloat(time) = sv.time;
3220 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3223 // decrement prog->num_edicts if the highest number entities died
3224 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3226 if (!sv_freezenonclients.integer)
3227 sv.time += sv.frametime;