2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "prvm_cmds.h"
28 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
30 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
32 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
33 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
34 corpses are SOLID_NOT and MOVETYPE_TOSS
35 crates are SOLID_BBOX and MOVETYPE_TOSS
36 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
37 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
39 solid_edge items only clip against bsp models.
43 #define MOVE_EPSILON 0.01
45 void SV_Physics_Toss (prvm_edict_t *ent);
47 int SV_GetPitchSign(prvm_prog_t *prog, prvm_edict_t *ent)
51 (model = SV_GetModelFromEdict(ent))
53 model->type == mod_alias
56 (((unsigned char)PRVM_serveredictfloat(ent, pflags)) & PFLAGS_FULLDYNAMIC)
58 ((gamemode == GAME_TENEBRAE) && ((unsigned int)PRVM_serveredictfloat(ent, effects) & (16 | 32)))
66 ===============================================================================
70 ===============================================================================
73 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
75 prvm_prog_t *prog = SVVM_prog;
78 int dphitcontentsmask = (int)PRVM_serveredictfloat(passedict, dphitcontentsmask);
79 if (dphitcontentsmask)
80 return dphitcontentsmask;
81 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_SLIDEBOX)
83 if ((int)PRVM_serveredictfloat(passedict, flags) & FL_MONSTER)
84 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
86 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
88 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_CORPSE)
89 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
90 else if (PRVM_serveredictfloat(passedict, solid) == SOLID_TRIGGER)
91 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
93 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
96 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
104 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask)
106 prvm_prog_t *prog = SVVM_prog;
107 int i, bodysupercontents;
110 prvm_edict_t *traceowner, *touch;
112 // temporary storage because prvm_vec_t may differ from vec_t
113 vec3_t touchmins, touchmaxs;
114 // bounding box of entire move area
115 vec3_t clipboxmins, clipboxmaxs;
116 // size when clipping against monsters
117 vec3_t clipmins2, clipmaxs2;
118 // start and end origin of move
122 // matrices to transform into/out of other entity's space
123 matrix4x4_t matrix, imatrix;
124 // model of other entity
126 // list of entities to test for collisions
128 static prvm_edict_t *touchedicts[MAX_EDICTS];
131 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
133 VectorCopy(start, clipstart);
134 VectorClear(clipmins2);
135 VectorClear(clipmaxs2);
136 #if COLLISIONPARANOID >= 3
137 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
141 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
142 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
143 if (cliptrace.startsolid || cliptrace.fraction < 1)
144 cliptrace.ent = prog->edicts;
145 if (type == MOVE_WORLDONLY)
148 if (type == MOVE_MISSILE)
150 // LadyHavoc: modified this, was = -15, now -= 15
151 for (i = 0;i < 3;i++)
158 // create the bounding box of the entire move
159 for (i = 0;i < 3;i++)
161 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
162 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
165 // debug override to test against everything
166 if (sv_debugmove.integer)
168 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
169 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
172 // if the passedict is world, make it NULL (to avoid two checks each time)
173 if (passedict == prog->edicts)
175 // precalculate prog value for passedict for comparisons
176 passedictprog = PRVM_EDICT_TO_PROG(passedict);
177 // precalculate passedict's owner edict pointer for comparisons
178 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
180 clipgroup = passedict ? (int)PRVM_serveredictfloat(passedict, clipgroup) : 0;
183 // because this uses World_EntitiestoBox, we know all entity boxes overlap
184 // the clip region, so we can skip culling checks in the loop below
185 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
186 if (numtouchedicts > MAX_EDICTS)
188 // this never happens
189 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
190 numtouchedicts = MAX_EDICTS;
192 for (i = 0;i < numtouchedicts;i++)
194 touch = touchedicts[i];
196 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
198 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
203 // don't clip against self
204 if (passedict == touch)
206 // don't clip owned entities against owner
207 if (traceowner == touch)
209 // don't clip owner against owned entities
210 if (passedictprog == PRVM_serveredictedict(touch, owner))
212 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
213 if (clipgroup && clipgroup == (int)PRVM_serveredictfloat(touch, clipgroup))
215 // don't clip points against points (they can't collide)
216 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
220 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
222 // might interact, so do an exact clip
224 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
226 model = SV_GetModelFromEdict(touch);
227 pitchsign = SV_GetPitchSign(prog, touch);
230 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
232 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
233 Matrix4x4_Invert_Simple(&imatrix, &matrix);
234 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
235 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
236 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
237 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
238 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
239 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
240 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 0.0f);
242 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
244 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
256 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
258 prvm_prog_t *prog = SVVM_prog;
259 int i, bodysupercontents;
262 prvm_edict_t *traceowner, *touch;
264 // temporary storage because prvm_vec_t may differ from vec_t
265 vec3_t touchmins, touchmaxs;
266 // bounding box of entire move area
267 vec3_t clipboxmins, clipboxmaxs;
268 // size when clipping against monsters
269 vec3_t clipmins2, clipmaxs2;
270 // start and end origin of move
271 vec3_t clipstart, clipend;
274 // matrices to transform into/out of other entity's space
275 matrix4x4_t matrix, imatrix;
276 // model of other entity
278 // list of entities to test for collisions
280 static prvm_edict_t *touchedicts[MAX_EDICTS];
282 if (VectorCompare(start, end))
283 return SV_TracePoint(start, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
285 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
287 VectorCopy(start, clipstart);
288 VectorCopy(end, clipend);
289 VectorClear(clipmins2);
290 VectorClear(clipmaxs2);
291 #if COLLISIONPARANOID >= 3
292 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
296 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, false);
297 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
298 if (cliptrace.startsolid || cliptrace.fraction < 1)
299 cliptrace.ent = prog->edicts;
300 if (type == MOVE_WORLDONLY)
303 if (type == MOVE_MISSILE)
305 // LadyHavoc: modified this, was = -15, now -= 15
306 for (i = 0;i < 3;i++)
313 // create the bounding box of the entire move
314 for (i = 0;i < 3;i++)
316 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
317 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
320 // debug override to test against everything
321 if (sv_debugmove.integer)
323 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
324 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
327 // if the passedict is world, make it NULL (to avoid two checks each time)
328 if (passedict == prog->edicts)
330 // precalculate prog value for passedict for comparisons
331 passedictprog = PRVM_EDICT_TO_PROG(passedict);
332 // precalculate passedict's owner edict pointer for comparisons
333 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
335 clipgroup = passedict ? (int)PRVM_serveredictfloat(passedict, clipgroup) : 0;
338 // because this uses World_EntitiestoBox, we know all entity boxes overlap
339 // the clip region, so we can skip culling checks in the loop below
340 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
341 if (numtouchedicts > MAX_EDICTS)
343 // this never happens
344 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
345 numtouchedicts = MAX_EDICTS;
347 for (i = 0;i < numtouchedicts;i++)
349 touch = touchedicts[i];
351 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
353 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
358 // don't clip against self
359 if (passedict == touch)
361 // don't clip owned entities against owner
362 if (traceowner == touch)
364 // don't clip owner against owned entities
365 if (passedictprog == PRVM_serveredictedict(touch, owner))
367 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
368 if (clipgroup && clipgroup == (int)PRVM_serveredictfloat(touch, clipgroup))
370 // don't clip points against points (they can't collide)
371 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
375 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
377 // might interact, so do an exact clip
379 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
381 model = SV_GetModelFromEdict(touch);
382 pitchsign = SV_GetPitchSign(prog, touch);
385 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
387 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
388 Matrix4x4_Invert_Simple(&imatrix, &matrix);
389 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
390 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
391 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
392 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
393 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
394 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
395 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
397 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, false);
399 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
411 #if COLLISIONPARANOID >= 1
412 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
414 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend)
417 prvm_prog_t *prog = SVVM_prog;
418 vec3_t hullmins, hullmaxs;
419 int i, bodysupercontents;
423 prvm_edict_t *traceowner, *touch;
425 // temporary storage because prvm_vec_t may differ from vec_t
426 vec3_t touchmins, touchmaxs;
427 // bounding box of entire move area
428 vec3_t clipboxmins, clipboxmaxs;
429 // size of the moving object
430 vec3_t clipmins, clipmaxs;
431 // size when clipping against monsters
432 vec3_t clipmins2, clipmaxs2;
433 // start and end origin of move
434 vec3_t clipstart, clipend;
437 // matrices to transform into/out of other entity's space
438 matrix4x4_t matrix, imatrix;
439 // model of other entity
441 // list of entities to test for collisions
443 static prvm_edict_t *touchedicts[MAX_EDICTS];
445 if (VectorCompare(mins, maxs))
447 vec3_t shiftstart, shiftend;
448 VectorAdd(start, mins, shiftstart);
449 VectorAdd(end, mins, shiftend);
450 if (VectorCompare(start, end))
451 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
453 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
454 VectorSubtract(trace.endpos, mins, trace.endpos);
458 VectorCopy(start, clipstart);
459 VectorCopy(end, clipend);
460 VectorCopy(mins, clipmins);
461 VectorCopy(maxs, clipmaxs);
462 VectorCopy(mins, clipmins2);
463 VectorCopy(maxs, clipmaxs2);
464 #if COLLISIONPARANOID >= 3
465 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
469 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
470 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
471 if (cliptrace.startsolid || cliptrace.fraction < 1)
472 cliptrace.ent = prog->edicts;
473 if (type == MOVE_WORLDONLY)
476 if (type == MOVE_MISSILE)
478 // LadyHavoc: modified this, was = -15, now -= 15
479 for (i = 0;i < 3;i++)
486 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
487 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
488 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
491 VectorCopy(clipmins, hullmins);
492 VectorCopy(clipmaxs, hullmaxs);
495 // create the bounding box of the entire move
496 for (i = 0;i < 3;i++)
498 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
499 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
502 // debug override to test against everything
503 if (sv_debugmove.integer)
505 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
506 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
509 // if the passedict is world, make it NULL (to avoid two checks each time)
510 if (passedict == prog->edicts)
512 // precalculate prog value for passedict for comparisons
513 passedictprog = PRVM_EDICT_TO_PROG(passedict);
514 // figure out whether this is a point trace for comparisons
515 pointtrace = VectorCompare(clipmins, clipmaxs);
516 // precalculate passedict's owner edict pointer for comparisons
517 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
519 clipgroup = passedict ? (int)PRVM_serveredictfloat(passedict, clipgroup) : 0;
522 // because this uses World_EntitiestoBox, we know all entity boxes overlap
523 // the clip region, so we can skip culling checks in the loop below
524 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
525 if (numtouchedicts > MAX_EDICTS)
527 // this never happens
528 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
529 numtouchedicts = MAX_EDICTS;
531 for (i = 0;i < numtouchedicts;i++)
533 touch = touchedicts[i];
535 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
537 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
542 // don't clip against self
543 if (passedict == touch)
545 // don't clip owned entities against owner
546 if (traceowner == touch)
548 // don't clip owner against owned entities
549 if (passedictprog == PRVM_serveredictedict(touch, owner))
551 // don't clip against any entities in the same clipgroup (DP_RM_CLIPGROUP)
552 if (clipgroup && clipgroup == (int)PRVM_serveredictfloat(touch, clipgroup))
554 // don't clip points against points (they can't collide)
555 if (pointtrace && VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
559 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
561 // might interact, so do an exact clip
563 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
565 model = SV_GetModelFromEdict(touch);
566 pitchsign = SV_GetPitchSign(prog, touch);
569 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], pitchsign * PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
571 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
572 Matrix4x4_Invert_Simple(&imatrix, &matrix);
573 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
574 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
575 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
576 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
577 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
578 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
579 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
581 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
583 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
590 #if COLLISIONPARANOID >= 1
591 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask)
593 prvm_prog_t *prog = SVVM_prog;
597 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
600 VectorCopy(trace.endpos, temp);
601 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask).startsolid;
602 #if COLLISIONPARANOID < 3
603 if (trace.startsolid || endstuck)
605 Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "^3" : "", passedict ? (int)(passedict - prog->edicts) : -1, PRVM_serveredictvector(passedict, origin)[0], PRVM_serveredictvector(passedict, origin)[1], PRVM_serveredictvector(passedict, origin)[2], end[0] - PRVM_serveredictvector(passedict, origin)[0], end[1] - PRVM_serveredictvector(passedict, origin)[1], end[2] - PRVM_serveredictvector(passedict, origin)[2], trace.fraction, trace.endpos[0] - PRVM_serveredictvector(passedict, origin)[0], trace.endpos[1] - PRVM_serveredictvector(passedict, origin)[1], trace.endpos[2] - PRVM_serveredictvector(passedict, origin)[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
611 int SV_PointSuperContents(const vec3_t point)
613 prvm_prog_t *prog = SVVM_prog;
614 int supercontents = 0;
618 // matrices to transform into/out of other entity's space
619 matrix4x4_t matrix, imatrix;
620 // model of other entity
623 // list of entities to test for collisions
625 static prvm_edict_t *touchedicts[MAX_EDICTS];
627 // get world supercontents at this point
628 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
629 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
631 // if sv_gameplayfix_swiminbmodels is off we're done
632 if (!sv_gameplayfix_swiminbmodels.integer)
633 return supercontents;
635 // get list of entities at this point
636 numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
637 if (numtouchedicts > MAX_EDICTS)
639 // this never happens
640 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
641 numtouchedicts = MAX_EDICTS;
643 for (i = 0;i < numtouchedicts;i++)
645 touch = touchedicts[i];
647 // we only care about SOLID_BSP for pointcontents
648 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
651 // might interact, so do an exact clip
652 model = SV_GetModelFromEdict(touch);
653 if (!model || !model->PointSuperContents)
655 Matrix4x4_CreateFromQuakeEntity(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2], PRVM_serveredictvector(touch, angles)[0], PRVM_serveredictvector(touch, angles)[1], PRVM_serveredictvector(touch, angles)[2], 1);
656 Matrix4x4_Invert_Simple(&imatrix, &matrix);
657 Matrix4x4_Transform(&imatrix, point, transformed);
658 frame = (int)PRVM_serveredictfloat(touch, frame);
659 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
662 return supercontents;
666 ===============================================================================
668 Linking entities into the world culling system
670 ===============================================================================
673 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
675 prvm_prog_t *prog = SVVM_prog;
676 vec3_t paddedmins, paddedmaxs;
677 if (maxedicts < 1 || resultedicts == NULL)
679 // LadyHavoc: discovered this actually causes its own bugs (dm6 teleporters being too close to info_teleport_destination)
680 //VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
681 //VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
682 VectorCopy(mins, paddedmins);
683 VectorCopy(maxs, paddedmaxs);
684 if (sv_areadebug.integer)
686 int numresultedicts = 0;
689 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
691 ed = PRVM_EDICT_NUM(edictindex);
692 if (!ed->priv.required->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
694 resultedicts[numresultedicts++] = ed;
695 if (numresultedicts == maxedicts)
699 return numresultedicts;
702 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
705 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
707 prvm_prog_t *prog = SVVM_prog;
708 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
709 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
710 PRVM_serverglobalfloat(time) = sv.time;
711 PRVM_serverglobalfloat(trace_allsolid) = false;
712 PRVM_serverglobalfloat(trace_startsolid) = false;
713 PRVM_serverglobalfloat(trace_fraction) = 1;
714 PRVM_serverglobalfloat(trace_inwater) = false;
715 PRVM_serverglobalfloat(trace_inopen) = true;
716 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
717 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
718 PRVM_serverglobalfloat(trace_plane_dist) = 0;
719 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
720 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
721 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
722 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
723 PRVM_serverglobalstring(trace_dphittexturename) = 0;
724 // optional entity parameter for self (EXT_ENTITYPARAM)
725 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(touch);
726 // optional entity parameter for other (EXT_ENTITYPARAM)
727 PRVM_G_INT(OFS_PARM1) = PRVM_EDICT_TO_PROG(ent);
728 prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
731 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
733 prvm_prog_t *prog = SVVM_prog;
734 int i, numtouchedicts, old_self, old_other;
736 static prvm_edict_t *touchedicts[MAX_EDICTS];
738 if (ent == prog->edicts)
739 return; // don't add the world
741 if (ent->priv.server->free)
744 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
747 // build a list of edicts to touch, because the link loop can be corrupted
748 // by IncreaseEdicts called during touch functions
749 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
750 if (numtouchedicts > MAX_EDICTS)
752 // this never happens
753 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
754 numtouchedicts = MAX_EDICTS;
757 old_self = PRVM_serverglobaledict(self);
758 old_other = PRVM_serverglobaledict(other);
759 for (i = 0;i < numtouchedicts;i++)
761 touch = touchedicts[i];
762 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
764 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
767 PRVM_serverglobaledict(self) = old_self;
768 PRVM_serverglobaledict(other) = old_other;
771 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
775 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
777 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
778 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
779 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
780 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]; }
781 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]; }
782 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
783 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]; }
784 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]; }
785 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
786 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]; }
787 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]; }
788 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
789 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]; }
790 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]; }
791 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
792 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]; }
793 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]; }
794 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
795 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]; }
796 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]; }
797 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
798 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]; }
799 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]; }
808 void SV_LinkEdict (prvm_edict_t *ent)
810 prvm_prog_t *prog = SVVM_prog;
812 vec3_t mins, maxs, entmins, entmaxs, entangles;
815 if (ent == prog->edicts)
816 return; // don't add the world
818 if (ent->priv.server->free)
821 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
822 if (modelindex < 0 || modelindex >= MAX_MODELS)
824 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
827 model = SV_GetModelByIndex(modelindex);
829 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
830 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
831 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
835 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
837 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
838 // TODO special handling for spheres?
839 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
840 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
841 VectorCopy(PRVM_serveredictvector(ent, angles), entangles);
842 RotateBBox(entmins, entmaxs, entangles, mins, maxs);
843 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
844 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
846 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
850 if (!model->TraceBox)
851 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
853 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
855 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
856 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
858 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
860 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
861 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
865 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
866 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
871 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
872 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
873 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
878 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
879 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
883 // to make items easier to pick up and allow them to be grabbed off
884 // of shelves, the abs sizes are expanded
886 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
897 // because movement is clipped an epsilon away from an actual edge,
898 // we must fully check even when bounding boxes don't quite touch
907 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
908 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
910 World_LinkEdict(&sv.world, ent, mins, maxs);
914 ===============================================================================
918 ===============================================================================
923 SV_TestEntityPosition
925 returns true if the entity is in solid currently
928 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
930 prvm_prog_t *prog = SVVM_prog;
931 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
932 int skipsupercontentsmask = 0;
933 int skipmaterialflagsmask = 0;
934 vec3_t org, entorigin, entmins, entmaxs;
936 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
937 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
938 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
939 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
940 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);
941 if (trace.startsupercontents & hitsupercontentsmask)
945 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
947 // q1bsp/hlbsp use hulls and if the entity does not exactly match
948 // a hull size it is incorrectly tested, so this code tries to
949 // 'fix' it slightly...
950 // FIXME: this breaks entities larger than the hull size
953 VectorAdd(org, entmins, m1);
954 VectorAdd(org, entmaxs, m2);
955 VectorSubtract(m2, m1, s);
956 #define EPSILON (1.0f / 32.0f)
957 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
958 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
959 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
960 for (i = 0;i < 8;i++)
962 v[0] = (i & 1) ? m2[0] : m1[0];
963 v[1] = (i & 2) ? m2[1] : m1[1];
964 v[2] = (i & 4) ? m2[2] : m1[2];
965 if (SV_PointSuperContents(v) & hitsupercontentsmask)
970 // if the trace found a better position for the entity, move it there
971 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
974 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
975 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
977 // verify if the endpos is REALLY outside solid
978 VectorCopy(trace.endpos, org);
979 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
981 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
983 VectorCopy(org, PRVM_serveredictvector(ent, origin));
989 // DRESK - Support for Entity Contents Transition Event
992 SV_CheckContentsTransition
994 returns true if entity had a valid contentstransition function call
997 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
999 prvm_prog_t *prog = SVVM_prog;
1000 int bValidFunctionCall;
1002 // Default Valid Function Call to False
1003 bValidFunctionCall = false;
1005 if(PRVM_serveredictfloat(ent, watertype) != nContents)
1006 { // Changed Contents
1007 // Acquire Contents Transition Function from QC
1008 if(PRVM_serveredictfunction(ent, contentstransition))
1009 { // Valid Function; Execute
1010 // Assign Valid Function
1011 bValidFunctionCall = true;
1012 // Prepare Parameters (Original Contents, New Contents)
1013 // Original Contents
1014 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
1016 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1018 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1020 PRVM_serverglobalfloat(time) = sv.time;
1021 // Execute VM Function
1022 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1026 // Return if Function Call was Valid
1027 return bValidFunctionCall;
1036 void SV_CheckVelocity (prvm_edict_t *ent)
1038 prvm_prog_t *prog = SVVM_prog;
1045 for (i=0 ; i<3 ; i++)
1047 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1049 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1050 PRVM_serveredictvector(ent, velocity)[i] = 0;
1052 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1054 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1055 PRVM_serveredictvector(ent, origin)[i] = 0;
1059 // LadyHavoc: a hack to ensure that the (rather silly) id1 quakec
1060 // player_run/player_stand1 does not horribly malfunction if the
1061 // velocity becomes a denormalized float
1062 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0000001)
1063 VectorClear(PRVM_serveredictvector(ent, velocity));
1065 // LadyHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1066 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1067 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1069 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1070 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1071 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1072 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1080 Runs thinking code if time. There is some play in the exact time the think
1081 function will be called, because it is called before any movement is done
1082 in a frame. Not used for pushmove objects, because they must be exact.
1083 Returns false if the entity removed itself.
1086 static qboolean SV_RunThink (prvm_edict_t *ent)
1088 prvm_prog_t *prog = SVVM_prog;
1091 // don't let things stay in the past.
1092 // it is possible to start that way by a trigger with a local time.
1093 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1096 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1098 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1099 PRVM_serveredictfloat(ent, nextthink) = 0;
1100 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1101 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1102 // optional entity parameter for self (EXT_ENTITYPARAM)
1103 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(ent);
1104 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1105 // mods often set nextthink to time to cause a think every frame,
1106 // we don't want to loop in that case, so exit if the new nextthink is
1107 // <= the time the qc was told, also exit if it is past the end of the
1109 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1112 return !ent->priv.server->free;
1119 Two entities have touched, so run their touch functions
1122 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1124 prvm_prog_t *prog = SVVM_prog;
1125 int restorevm_tempstringsbuf_cursize;
1126 int old_self, old_other;
1127 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1129 old_self = PRVM_serverglobaledict(self);
1130 old_other = PRVM_serverglobaledict(other);
1131 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1133 VM_SetTraceGlobals(prog, trace);
1135 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1137 PRVM_serverglobalfloat(time) = sv.time;
1138 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1139 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1140 // optional entity parameter for self (EXT_ENTITYPARAM)
1141 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(e1);
1142 // optional entity parameter for other (EXT_ENTITYPARAM)
1143 PRVM_G_INT(OFS_PARM1) = PRVM_EDICT_TO_PROG(e2);
1144 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1147 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1149 PRVM_serverglobalfloat(time) = sv.time;
1150 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1151 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1152 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1153 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1154 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1155 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1156 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1157 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1158 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1159 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1160 // optional entity parameter for self (EXT_ENTITYPARAM)
1161 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(e2);
1162 // optional entity parameter for other (EXT_ENTITYPARAM)
1163 PRVM_G_INT(OFS_PARM1) = PRVM_EDICT_TO_PROG(e1);
1164 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1167 PRVM_serverglobaledict(self) = old_self;
1168 PRVM_serverglobaledict(other) = old_other;
1169 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1177 Slide off of the impacting object
1178 returns the blocked flags (1 = floor, 2 = step / wall)
1181 #define STOP_EPSILON 0.1
1182 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1187 backoff = -DotProduct (in, normal) * overbounce;
1188 VectorMA(in, backoff, normal, out);
1190 for (i = 0;i < 3;i++)
1191 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1200 The basic solid body movement clip that slides along multiple planes
1201 Returns the clipflags if the velocity was modified (hit something solid)
1205 8 = teleported by touch method
1206 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1209 static float SV_Gravity (prvm_edict_t *ent);
1210 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink);
1211 #define MAX_CLIP_PLANES 5
1212 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float stepheight)
1214 prvm_prog_t *prog = SVVM_prog;
1215 int blocked, bumpcount;
1216 int i, j, numplanes;
1217 float d, time_left, gravity;
1218 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1219 prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
1228 VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1232 gravity = SV_Gravity(ent);
1234 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1236 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1237 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1239 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1244 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1245 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1248 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1250 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1253 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1254 if(!SV_PushEntity(&trace, ent, push, false))
1256 // we got teleported by a touch function
1257 // let's abort the move
1262 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
1263 // abort move if we're stuck in the world (and didn't make it out)
1264 if (trace.worldstartsolid && trace.allsolid)
1266 VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
1270 if (trace.fraction == 1)
1272 if (trace.plane.normal[2])
1274 if (trace.plane.normal[2] > 0.7)
1281 Con_Printf ("SV_FlyMove: !trace.ent");
1282 trace.ent = prog->edicts;
1285 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1286 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1289 else if (stepheight)
1291 // step - handle it immediately
1297 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1298 VectorSet(steppush, 0, 0, stepheight);
1299 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1300 if(!SV_PushEntity(&steptrace, ent, steppush, false))
1305 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1306 if(!SV_PushEntity(&steptrace2, ent, push, false))
1311 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1312 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1313 if(!SV_PushEntity(&steptrace3, ent, steppush, false))
1318 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1319 // accept the new position if it made some progress...
1320 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1322 //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]);
1324 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1325 time_left *= 1 - trace.fraction;
1331 //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]);
1332 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1337 // step - return it to caller
1339 // save the trace for player extrafriction
1341 VectorCopy(trace.plane.normal, stepnormal);
1343 if (trace.fraction >= 0.001)
1345 // actually covered some distance
1346 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1350 time_left *= 1 - trace.fraction;
1352 // clipped to another plane
1353 if (numplanes >= MAX_CLIP_PLANES)
1355 // this shouldn't really happen
1356 VectorClear(PRVM_serveredictvector(ent, velocity));
1362 for (i = 0;i < numplanes;i++)
1363 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1367 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1372 VectorCopy(trace.plane.normal, planes[numplanes]);
1375 // modify original_velocity so it parallels all of the clip planes
1376 for (i = 0;i < numplanes;i++)
1378 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1379 for (j = 0;j < numplanes;j++)
1384 if (DotProduct(new_velocity, planes[j]) < 0)
1394 // go along this plane
1395 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1399 // go along the crease
1402 VectorClear(PRVM_serveredictvector(ent, velocity));
1406 CrossProduct(planes[0], planes[1], dir);
1407 // LadyHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1408 VectorNormalize(dir);
1409 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1410 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1413 // if current velocity is against the original velocity,
1414 // stop dead to avoid tiny occilations in sloping corners
1415 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1417 VectorClear(PRVM_serveredictvector(ent, velocity));
1422 //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]);
1425 if ((blocked & 1) == 0 && bumpcount > 1)
1427 // LadyHavoc: fix the 'fall to your death in a wedge corner' glitch
1428 // flag ONGROUND if there's ground under it
1429 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
1433 // LadyHavoc: this came from QW and allows you to get out of water more easily
1434 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1435 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1439 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1441 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1442 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1455 static float SV_Gravity (prvm_edict_t *ent)
1457 prvm_prog_t *prog = SVVM_prog;
1460 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1463 return ent_gravity * sv_gravity.value * sv.frametime;
1468 ===============================================================================
1472 ===============================================================================
1475 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1477 prvm_prog_t *prog = SVVM_prog;
1481 vec3_t stuckmins, stuckmaxs;
1482 vec3_t goodmins, goodmaxs;
1486 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1487 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1488 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1489 VectorCopy(pivot, goodmins);
1490 VectorCopy(pivot, goodmaxs);
1491 for (bump = 0;bump < 6;bump++)
1493 int coord = 2-(bump >> 1);
1494 //int coord = (bump >> 1);
1495 int dir = (bump & 1);
1498 for(subbump = 0; ; ++subbump)
1500 VectorCopy(stuckorigin, testorigin);
1504 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1509 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1512 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1513 if (stucktrace.bmodelstartsolid)
1515 // BAD BAD, can't fix that
1519 if (stucktrace.fraction >= 1)
1524 // BAD BAD, can't fix that
1528 // we hit something... let's move out of it
1529 VectorSubtract(stucktrace.endpos, testorigin, move);
1530 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1531 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1535 Con_Printf("subbump: %d\n", subbump);
1541 goodmaxs[coord] = stuckmaxs[coord];
1546 goodmins[coord] = stuckmins[coord];
1551 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1556 qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1558 prvm_prog_t *prog = SVVM_prog;
1562 vec3_t stuckmins, stuckmaxs;
1564 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1565 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1566 separation = 0.0f; // when using hulls, it can not be enlarged
1567 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1568 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1569 stuckmins[0] -= separation;
1570 stuckmins[1] -= separation;
1571 stuckmins[2] -= separation;
1572 stuckmaxs[0] += separation;
1573 stuckmaxs[1] += separation;
1574 stuckmaxs[2] += separation;
1575 // first pass we try to get it out of brush entities
1576 // second pass we try to get it out of world only (can't win them all)
1577 for (pass = 0;pass < 2;pass++)
1579 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1580 for (bump = 0;bump < 10;bump++)
1582 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1583 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1585 // found a good location, use it
1586 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1589 nudge = -stucktrace.startdepth;
1590 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1600 Does not change the entities velocity at all
1601 The trace struct is filled with the trace that has been done.
1602 Returns true if the push did not result in the entity being teleported by QC code.
1605 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink)
1607 prvm_prog_t *prog = SVVM_prog;
1615 solid = (int)PRVM_serveredictfloat(ent, solid);
1616 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1617 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1618 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1620 // move start position out of solids
1621 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1623 SV_NudgeOutOfSolid(ent);
1626 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1627 VectorAdd(start, push, end);
1629 if (movetype == MOVETYPE_FLYMISSILE)
1630 type = MOVE_MISSILE;
1631 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1632 type = MOVE_WORLDONLY;
1633 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1634 type = MOVE_NOMONSTERS; // only clip against bmodels
1638 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1639 // fail the move if stuck in world
1640 if (trace->worldstartsolid)
1643 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1645 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1650 if(!trace->startsolid)
1651 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)
1653 Con_Printf("something eeeeevil happened\n");
1658 SV_LinkEdict_TouchAreaGrid(ent);
1660 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))))
1661 SV_Impact (ent, trace);
1663 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1665 ent->priv.required->mark = 0;
1668 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1670 ent->priv.required->mark = 0;
1675 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1687 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1689 prvm_prog_t *prog = SVVM_prog;
1691 int pusherowner, pusherprog;
1694 float savesolid, movetime2, pushltime;
1695 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1697 int numcheckentities;
1698 static prvm_edict_t *checkentities[MAX_EDICTS];
1699 dp_model_t *pushermodel;
1700 trace_t trace, trace2;
1701 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1702 static unsigned short moved_edicts[MAX_EDICTS];
1705 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])
1707 PRVM_serveredictfloat(pusher, ltime) += movetime;
1711 switch ((int) PRVM_serveredictfloat(pusher, solid))
1713 // LadyHavoc: valid pusher types
1716 case SOLID_SLIDEBOX:
1717 case SOLID_CORPSE: // LadyHavoc: this would be weird...
1719 // LadyHavoc: no collisions
1722 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1723 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1724 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1725 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1726 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1727 PRVM_serveredictfloat(pusher, ltime) += movetime;
1728 SV_LinkEdict(pusher);
1731 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1734 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1735 if (index < 1 || index >= MAX_MODELS)
1737 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1740 pushermodel = SV_GetModelByIndex(index);
1741 pusherowner = PRVM_serveredictedict(pusher, owner);
1742 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1744 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1746 movetime2 = movetime;
1747 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1748 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1749 if (moveangle[0] || moveangle[2])
1751 for (i = 0;i < 3;i++)
1755 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1756 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1760 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1761 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1765 else if (moveangle[1])
1767 for (i = 0;i < 3;i++)
1771 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1772 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1776 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1777 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1783 for (i = 0;i < 3;i++)
1787 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1788 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1792 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1793 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1798 VectorNegate (moveangle, a);
1799 AngleVectorsFLU (a, forward, left, up);
1801 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1802 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1803 pushltime = PRVM_serveredictfloat(pusher, ltime);
1805 // move the pusher to its final position
1807 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1808 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1809 PRVM_serveredictfloat(pusher, ltime) += movetime;
1810 SV_LinkEdict(pusher);
1812 pushermodel = SV_GetModelFromEdict(pusher);
1813 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);
1814 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1816 savesolid = PRVM_serveredictfloat(pusher, solid);
1818 // see if any solid entities are inside the final position
1821 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1822 numcheckentities = 0;
1823 else // MOVETYPE_PUSH
1824 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1825 for (e = 0;e < numcheckentities;e++)
1827 prvm_edict_t *check = checkentities[e];
1828 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1833 case MOVETYPE_FOLLOW:
1834 case MOVETYPE_NOCLIP:
1835 case MOVETYPE_FLY_WORLDONLY:
1841 if (PRVM_serveredictedict(check, owner) == pusherprog)
1844 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1847 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1849 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1850 check->priv.server->waterposition_forceupdate = true;
1852 checkcontents = SV_GenericHitSuperContentsMask(check);
1854 // if the entity is standing on the pusher, it will definitely be moved
1855 // if the entity is not standing on the pusher, but is in the pusher's
1856 // final position, move it
1857 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1859 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1860 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1861 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1862 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1863 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1864 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);
1865 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1866 if (!trace.startsolid)
1868 //Con_Printf("- not in solid\n");
1873 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1874 //VectorClear(pivot);
1879 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1880 VectorAdd (org, pivot, org);
1881 org2[0] = DotProduct (org, forward);
1882 org2[1] = DotProduct (org, left);
1883 org2[2] = DotProduct (org, up);
1884 VectorSubtract (org2, org, move);
1885 VectorAdd (move, move1, move);
1888 VectorCopy (move1, move);
1890 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1892 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1893 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1894 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1896 // physics objects need better collisions than this code can do
1897 if (movetype == MOVETYPE_PHYSICS)
1899 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1900 SV_LinkEdict(check);
1901 SV_LinkEdict_TouchAreaGrid(check);
1905 // try moving the contacted entity
1906 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1907 if(!SV_PushEntity (&trace, check, move, true))
1909 // entity "check" got teleported
1910 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1911 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1912 continue; // pushed enough
1914 // FIXME: turn players specially
1915 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1916 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1917 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1919 // this trace.fraction < 1 check causes items to fall off of pushers
1920 // if they pass under or through a wall
1921 // the groundentity check causes items to fall off of ledges
1922 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1923 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1925 // if it is still inside the pusher, block
1926 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1927 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1928 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1929 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1930 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1931 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);
1932 if (trace.startsolid)
1935 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1937 // hack to invoke all necessary movement triggers
1939 if(!SV_PushEntity(&trace2, check, move2, true))
1941 // entity "check" got teleported
1948 // still inside pusher, so it's really blocked
1951 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1953 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1956 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1957 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1961 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1962 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1963 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1964 SV_LinkEdict(pusher);
1966 // move back any entities we already moved
1967 for (i = 0;i < num_moved;i++)
1969 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1970 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1971 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1975 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1976 if (PRVM_serveredictfunction(pusher, blocked))
1978 PRVM_serverglobalfloat(time) = sv.time;
1979 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1980 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1981 // optional entity parameter for self (EXT_ENTITYPARAM)
1982 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(pusher);
1983 // optional entity parameter for other (EXT_ENTITYPARAM)
1984 PRVM_G_INT(OFS_PARM1) = PRVM_EDICT_TO_PROG(check);
1985 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1990 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1991 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1992 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
2001 static void SV_Physics_Pusher (prvm_edict_t *ent)
2003 prvm_prog_t *prog = SVVM_prog;
2004 double thinktime, oldltime, movetime;
2006 oldltime = PRVM_serveredictfloat(ent, ltime);
2008 thinktime = PRVM_serveredictfloat(ent, nextthink);
2009 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
2011 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
2016 movetime = sv.frametime;
2019 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
2020 SV_PushMove (ent, movetime);
2022 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
2024 PRVM_serveredictfloat(ent, nextthink) = 0;
2025 PRVM_serverglobalfloat(time) = sv.time;
2026 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2027 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
2028 // optional entity parameter for self (EXT_ENTITYPARAM)
2029 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(ent);
2030 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2036 ===============================================================================
2040 ===============================================================================
2043 static float unstickoffsets[] =
2045 // poutting -/+z changes first as they are least weird
2060 typedef enum unstickresult_e
2068 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2070 prvm_prog_t *prog = SVVM_prog;
2073 // if not stuck in a bmodel, just return
2074 if (!SV_TestEntityPosition(ent, vec3_origin))
2075 return UNSTICK_GOOD;
2077 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2079 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2081 VectorCopy(unstickoffsets + i, offset);
2083 //SV_LinkEdict_TouchAreaGrid(ent);
2084 return UNSTICK_UNSTUCK;
2088 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2089 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2091 for(i = 2; i <= maxunstick; ++i)
2093 VectorClear(offset);
2095 if (!SV_TestEntityPosition(ent, offset))
2098 //SV_LinkEdict_TouchAreaGrid(ent);
2099 return UNSTICK_UNSTUCK;
2102 if (!SV_TestEntityPosition(ent, offset))
2105 //SV_LinkEdict_TouchAreaGrid(ent);
2106 return UNSTICK_UNSTUCK;
2110 return UNSTICK_STUCK;
2113 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2115 prvm_prog_t *prog = SVVM_prog;
2117 switch(SV_UnstickEntityReturnOffset(ent, offset))
2121 case UNSTICK_UNSTUCK:
2122 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]);
2125 if (developer_extra.integer)
2126 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2129 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2138 This is a big hack to try and fix the rare case of getting stuck in the world
2142 static void SV_CheckStuck (prvm_edict_t *ent)
2144 prvm_prog_t *prog = SVVM_prog;
2147 switch(SV_UnstickEntityReturnOffset(ent, offset))
2150 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2152 case UNSTICK_UNSTUCK:
2153 Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), offset[0], offset[1], offset[2]);
2156 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2157 if (!SV_TestEntityPosition(ent, offset))
2159 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2161 //SV_LinkEdict_TouchAreaGrid(ent);
2164 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2167 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2177 static qboolean SV_CheckWater (prvm_edict_t *ent)
2179 prvm_prog_t *prog = SVVM_prog;
2181 int nNativeContents;
2184 point[0] = PRVM_serveredictvector(ent, origin)[0];
2185 point[1] = PRVM_serveredictvector(ent, origin)[1];
2186 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2188 // DRESK - Support for Entity Contents Transition Event
2189 // NOTE: Some logic needed to be slightly re-ordered
2190 // to not affect performance and allow for the feature.
2192 // Acquire Super Contents Prior to Resets
2193 cont = SV_PointSuperContents(point);
2194 // Acquire Native Contents Here
2195 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(cont);
2197 // DRESK - Support for Entity Contents Transition Event
2198 if(PRVM_serveredictfloat(ent, watertype))
2199 // Entity did NOT Spawn; Check
2200 SV_CheckContentsTransition(ent, nNativeContents);
2203 PRVM_serveredictfloat(ent, waterlevel) = 0;
2204 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2205 cont = SV_PointSuperContents(point);
2206 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2208 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2209 PRVM_serveredictfloat(ent, waterlevel) = 1;
2210 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2211 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2213 PRVM_serveredictfloat(ent, waterlevel) = 2;
2214 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2215 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2216 PRVM_serveredictfloat(ent, waterlevel) = 3;
2220 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2229 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2231 prvm_prog_t *prog = SVVM_prog;
2233 vec3_t forward, into, side, v_angle;
2235 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2236 AngleVectors (v_angle, forward, NULL, NULL);
2237 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2239 // cut the tangential velocity
2240 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2241 VectorScale (stepnormal, i, into);
2242 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2243 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2244 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2250 =====================
2253 Player has come to a dead stop, possibly due to the problem with limited
2254 float precision at some angle joins in the BSP hull.
2256 Try fixing by pushing one pixel in each direction.
2258 This is a hack, but in the interest of good gameplay...
2259 ======================
2261 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2266 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2269 for (i=0 ; i<8 ; i++)
2271 // try pushing a little in an axial direction
2274 case 0: dir[0] = 2; dir[1] = 0; break;
2275 case 1: dir[0] = 0; dir[1] = 2; break;
2276 case 2: dir[0] = -2; dir[1] = 0; break;
2277 case 3: dir[0] = 0; dir[1] = -2; break;
2278 case 4: dir[0] = 2; dir[1] = 2; break;
2279 case 5: dir[0] = -2; dir[1] = 2; break;
2280 case 6: dir[0] = 2; dir[1] = -2; break;
2281 case 7: dir[0] = -2; dir[1] = -2; break;
2284 SV_PushEntity (&trace, ent, dir, false, true);
2286 // retry the original move
2287 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2288 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2289 PRVM_serveredictvector(ent, velocity)[2] = 0;
2290 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2292 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2293 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2295 Con_DPrint("TryUnstick - success.\n");
2299 // go back to the original pos and try again
2300 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2304 VectorClear (PRVM_serveredictvector(ent, velocity));
2305 Con_DPrint("TryUnstick - failure.\n");
2311 =====================
2314 Only used by players
2315 ======================
2317 static void SV_WalkMove (prvm_edict_t *ent)
2319 prvm_prog_t *prog = SVVM_prog;
2322 //int originalmove_clip;
2323 int originalmove_flags;
2324 int originalmove_groundentity;
2325 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2326 int skipsupercontentsmask = 0;
2327 int skipmaterialflagsmask = 0;
2329 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2330 trace_t downtrace, trace;
2331 qboolean applygravity;
2333 // if frametime is 0 (due to client sending the same timestamp twice),
2335 if (sv.frametime <= 0)
2338 if (sv_gameplayfix_unstickplayers.integer)
2339 SV_CheckStuck (ent);
2341 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2343 SV_CheckVelocity(ent);
2345 // do a regular slide move unless it looks like you ran into a step
2346 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2348 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2349 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2351 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2353 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2356 // only try this if there was no floor in the way in the trace (no,
2357 // this check seems to be not REALLY necessary, because if clip & 1,
2358 // our trace will hit that thing too)
2359 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2360 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2361 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2362 type = MOVE_MISSILE;
2363 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2364 type = MOVE_WORLDONLY;
2365 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2366 type = MOVE_NOMONSTERS; // only clip against bmodels
2369 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2370 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2371 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
2372 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2373 clip |= 1; // but we HAVE found a floor
2376 // if the move did not hit the ground at any point, we're not on ground
2378 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2380 SV_CheckVelocity(ent);
2382 SV_LinkEdict_TouchAreaGrid(ent);
2384 if(clip & 8) // teleport
2387 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2390 if (sv_nostep.integer)
2393 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2394 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2395 //originalmove_clip = clip;
2396 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2397 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2399 // if move didn't block on a step, return
2402 // if move was not trying to move into the step, return
2403 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2406 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2408 // return if gibbed by a trigger
2409 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2412 // return if attempting to jump while airborn (unless sv_jumpstep)
2413 if (!sv_jumpstep.integer)
2414 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2418 // try moving up and forward to go up a step
2419 // back to start pos
2420 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2421 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2424 VectorClear (upmove);
2425 upmove[2] = sv_stepheight.value;
2426 if(!SV_PushEntity(&trace, ent, upmove, true))
2428 // we got teleported when upstepping... must abort the move
2433 PRVM_serveredictvector(ent, velocity)[2] = 0;
2434 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 0);
2435 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2438 // we got teleported when upstepping... must abort the move
2439 // note that z velocity handling may not be what QC expects here, but we cannot help it
2443 SV_CheckVelocity(ent);
2445 SV_LinkEdict_TouchAreaGrid(ent);
2447 // check for stuckness, possibly due to the limited precision of floats
2448 // in the clipping hulls
2450 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2451 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2453 //Con_Printf("wall\n");
2454 // stepping up didn't make any progress, revert to original move
2455 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2456 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2457 //clip = originalmove_clip;
2458 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2459 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2460 // now try to unstick if needed
2461 //clip = SV_TryUnstick (ent, oldvel);
2465 //Con_Printf("step - ");
2467 // extra friction based on view angle
2468 if (clip & 2 && sv_wallfriction.integer)
2469 SV_WallFriction (ent, stepnormal);
2471 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2472 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))
2476 VectorClear (downmove);
2477 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2478 if(!SV_PushEntity (&downtrace, ent, downmove, true))
2480 // we got teleported when downstepping... must abort the move
2484 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2486 // this has been disabled so that you can't jump when you are stepping
2487 // up while already jumping (also known as the Quake2 double jump bug)
2489 // LadyHavoc: disabled this check so you can walk on monsters/players
2490 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2492 //Con_Printf("onground\n");
2493 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2494 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2500 //Con_Printf("slope\n");
2501 // if the push down didn't end up on good ground, use the move without
2502 // the step up. This happens near wall / slope combinations, and can
2503 // cause the player to hop up higher on a slope too steep to climb
2504 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2505 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2506 //clip = originalmove_clip;
2507 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2508 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2511 SV_CheckVelocity(ent);
2513 SV_LinkEdict_TouchAreaGrid(ent);
2516 //============================================================================
2522 Entities that are "stuck" to another entity
2525 static void SV_Physics_Follow (prvm_edict_t *ent)
2527 prvm_prog_t *prog = SVVM_prog;
2528 vec3_t vf, vr, vu, angles, v;
2531 // LadyHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2532 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2533 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])
2535 // quick case for no rotation
2536 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2540 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2541 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2542 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2543 AngleVectors (angles, vf, vr, vu);
2544 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];
2545 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];
2546 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];
2547 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2548 angles[1] = PRVM_serveredictvector(e, angles)[1];
2549 angles[2] = PRVM_serveredictvector(e, angles)[2];
2550 AngleVectors (angles, vf, vr, vu);
2551 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2552 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2553 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2555 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2557 //SV_LinkEdict_TouchAreaGrid(ent);
2561 ==============================================================================
2565 ==============================================================================
2570 SV_CheckWaterTransition
2574 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2577 prvm_prog_t *prog = SVVM_prog;
2578 // 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
2580 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2581 cont = Mod_Q1BSP_NativeContentsFromSuperContents(SV_PointSuperContents(entorigin));
2582 if (!PRVM_serveredictfloat(ent, watertype))
2584 // just spawned here
2585 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2587 PRVM_serveredictfloat(ent, watertype) = cont;
2588 PRVM_serveredictfloat(ent, waterlevel) = 1;
2592 // DRESK - Support for Entity Contents Transition Event
2593 // NOTE: Call here BEFORE updating the watertype below,
2594 // and suppress watersplash sound if a valid function
2595 // call was made to allow for custom "splash" sounds.
2596 else if( !SV_CheckContentsTransition(ent, cont) )
2597 { // Contents Transition Function Invalid; Potentially Play Water Sound
2598 // check if the entity crossed into or out of water
2599 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2600 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2603 if (cont <= CONTENTS_WATER)
2605 PRVM_serveredictfloat(ent, watertype) = cont;
2606 PRVM_serveredictfloat(ent, waterlevel) = 1;
2610 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2611 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2619 Toss, bounce, and fly movement. When onground, do nothing.
2623 void SV_Physics_Toss (prvm_edict_t *ent)
2625 prvm_prog_t *prog = SVVM_prog;
2630 prvm_edict_t *groundentity;
2631 float d, ent_gravity;
2635 // if onground, return without moving
2636 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2638 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2639 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2641 // don't stick to ground if onground and moving upward
2642 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2644 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2646 // we can trust FL_ONGROUND if groundentity is world because it never moves
2649 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2651 // if ent was supported by a brush model on previous frame,
2652 // and groundentity is now freed, set groundentity to 0 (world)
2653 // which leaves it suspended in the air
2654 PRVM_serveredictedict(ent, groundentity) = 0;
2655 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2658 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2660 // don't slide if still touching the groundentity
2664 ent->priv.server->suspendedinairflag = false;
2666 SV_CheckVelocity (ent);
2669 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2670 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2673 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2675 movetime = sv.frametime;
2676 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2679 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2680 if(!SV_PushEntity(&trace, ent, move, true))
2681 return; // teleported
2682 if (ent->priv.server->free)
2684 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2686 // try to unstick the entity
2687 SV_UnstickEntity(ent);
2688 if(!SV_PushEntity(&trace, ent, move, true))
2689 return; // teleported
2690 if (ent->priv.server->free)
2693 if (trace.fraction == 1)
2695 movetime *= 1 - min(1, trace.fraction);
2696 switch((int)PRVM_serveredictfloat(ent, movetype))
2698 case MOVETYPE_BOUNCEMISSILE:
2699 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2701 bouncefactor = 1.0f;
2703 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2704 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2705 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2708 case MOVETYPE_BOUNCE:
2709 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2711 bouncefactor = 0.5f;
2713 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2715 bouncestop = 60.0f / 800.0f;
2717 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2718 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2721 // LadyHavoc: fixed grenades not bouncing when fired down a slope
2722 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2723 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2725 d = PRVM_serveredictvector(ent, velocity)[2];
2726 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2728 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2729 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2730 VectorClear(PRVM_serveredictvector(ent, velocity));
2731 VectorClear(PRVM_serveredictvector(ent, avelocity));
2736 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2737 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2742 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2743 if (trace.plane.normal[2] > 0.7)
2745 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2746 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2747 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2748 ent->priv.server->suspendedinairflag = true;
2749 VectorClear (PRVM_serveredictvector(ent, velocity));
2750 VectorClear (PRVM_serveredictvector(ent, avelocity));
2755 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2756 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2763 // check for in water
2764 SV_CheckWaterTransition (ent);
2768 ===============================================================================
2772 ===============================================================================
2779 Monsters freefall when they don't have a ground entity, otherwise
2780 all movement is done with discrete steps.
2782 This is also used for objects that have become still on the ground, but
2783 will fall if the floor is pulled out from under them.
2786 static void SV_Physics_Step (prvm_edict_t *ent)
2788 prvm_prog_t *prog = SVVM_prog;
2789 int flags = (int)PRVM_serveredictfloat(ent, flags);
2792 // Backup Velocity in the event that movetypesteplandevent is called,
2793 // to provide a parameter with the entity's velocity at impact.
2794 vec3_t backupVelocity;
2795 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2796 // don't fall at all if fly/swim
2797 if (!(flags & (FL_FLY | FL_SWIM)))
2799 if (flags & FL_ONGROUND)
2801 // freefall if onground and moving upward
2802 // freefall if not standing on a world surface (it may be a lift or trap door)
2803 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2805 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2806 SV_CheckVelocity(ent);
2807 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2809 SV_LinkEdict_TouchAreaGrid(ent);
2810 ent->priv.server->waterposition_forceupdate = true;
2815 // freefall if not onground
2816 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2818 SV_CheckVelocity(ent);
2819 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2821 SV_LinkEdict_TouchAreaGrid(ent);
2824 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2826 // DRESK - Check for Entity Land Event Function
2827 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2828 { // Valid Function; Execute
2829 // Prepare Parameters
2830 // Assign Velocity at Impact
2831 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2832 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2833 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2835 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2837 PRVM_serverglobalfloat(time) = sv.time;
2838 // Execute VM Function
2839 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2842 // Check for Engine Landing Sound
2843 if(sv_sound_land.string)
2844 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2846 ent->priv.server->waterposition_forceupdate = true;
2851 //============================================================================
2853 static void SV_Physics_Entity (prvm_edict_t *ent)
2855 prvm_prog_t *prog = SVVM_prog;
2856 // don't run think/move on newly spawned projectiles as it messes up
2857 // movement interpolation and rocket trails, and is inconsistent with
2858 // respect to entities spawned in the same frame
2859 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2860 // but if it spawns a lower numbered ent, it doesn't - this never moves
2861 // ents in the first frame regardless)
2862 qboolean runmove = ent->priv.server->move;
2863 ent->priv.server->move = true;
2864 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2866 switch ((int) PRVM_serveredictfloat(ent, movetype))
2869 case MOVETYPE_FAKEPUSH:
2870 SV_Physics_Pusher (ent);
2873 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2874 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2877 case MOVETYPE_FOLLOW:
2878 if(SV_RunThink(ent))
2879 SV_Physics_Follow (ent);
2881 case MOVETYPE_NOCLIP:
2882 if (SV_RunThink(ent))
2885 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2886 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2891 SV_Physics_Step (ent);
2893 if (SV_RunThink(ent))
2894 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2896 ent->priv.server->waterposition_forceupdate = false;
2897 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2898 SV_CheckWaterTransition(ent);
2902 if (SV_RunThink (ent))
2906 case MOVETYPE_BOUNCE:
2907 case MOVETYPE_BOUNCEMISSILE:
2908 case MOVETYPE_FLYMISSILE:
2910 case MOVETYPE_FLY_WORLDONLY:
2912 if (SV_RunThink (ent))
2913 SV_Physics_Toss (ent);
2915 case MOVETYPE_PHYSICS:
2916 if (SV_RunThink(ent))
2919 SV_LinkEdict_TouchAreaGrid(ent);
2923 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2925 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2930 static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
2932 prvm_prog_t *prog = SVVM_prog;
2934 // don't run think at all, that is done during server frames
2935 // instead, call the movetypes directly so they match client input
2937 // This probably only makes sense for CSQC-networked (SendEntity field set) player entities
2938 switch ((int) PRVM_serveredictfloat(ent, movetype))
2941 case MOVETYPE_FAKEPUSH:
2942 // push physics relies heavily on think times and calls, and so cannot be predicted currently
2943 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2947 case MOVETYPE_FOLLOW:
2948 SV_Physics_Follow (ent);
2950 case MOVETYPE_NOCLIP:
2951 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2952 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2955 SV_Physics_Step (ent);
2961 case MOVETYPE_BOUNCE:
2962 case MOVETYPE_BOUNCEMISSILE:
2963 case MOVETYPE_FLYMISSILE:
2964 SV_Physics_Toss (ent);
2967 case MOVETYPE_FLY_WORLDONLY:
2970 case MOVETYPE_PHYSICS:
2973 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2975 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2980 void SV_Physics_ClientMove(void)
2982 prvm_prog_t *prog = SVVM_prog;
2984 ent = host_client->edict;
2986 // call player physics, this needs the proper frametime
2987 PRVM_serverglobalfloat(frametime) = sv.frametime;
2990 // call standard client pre-think, with frametime = 0
2991 PRVM_serverglobalfloat(time) = sv.time;
2992 PRVM_serverglobalfloat(frametime) = 0;
2993 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2994 // optional entity parameter for self (EXT_ENTITYPARAM)
2995 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(ent);
2996 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2997 PRVM_serverglobalfloat(frametime) = sv.frametime;
2999 // make sure the velocity is sane (not a NaN)
3000 SV_CheckVelocity(ent);
3002 // perform movetype behaviour
3003 // note: will always be MOVETYPE_WALK if disableclientprediction = 0
3004 SV_Physics_ClientEntity_NoThink (ent);
3006 // call standard player post-think, with frametime = 0
3007 PRVM_serverglobalfloat(time) = sv.time;
3008 PRVM_serverglobalfloat(frametime) = 0;
3009 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3010 // optional entity parameter for self (EXT_ENTITYPARAM)
3011 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(ent);
3012 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3013 PRVM_serverglobalfloat(frametime) = sv.frametime;
3015 if(PRVM_serveredictfloat(ent, fixangle))
3017 // angle fixing was requested by physics code...
3018 // so store the current angles for later use
3019 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3020 host_client->fixangle_angles_set = true;
3022 // and clear fixangle for the next frame
3023 PRVM_serveredictfloat(ent, fixangle) = 0;
3027 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
3029 prvm_prog_t *prog = SVVM_prog;
3030 // don't do physics on disconnected clients, FrikBot relies on this
3031 if (!host_client->begun)
3034 // make sure the velocity is sane (not a NaN)
3035 SV_CheckVelocity(ent);
3037 // don't run physics here if running asynchronously
3038 if (host_client->clmovement_inputtimeout <= 0)
3041 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
3044 // make sure the velocity is still sane (not a NaN)
3045 SV_CheckVelocity(ent);
3047 // call standard client pre-think
3048 PRVM_serverglobalfloat(time) = sv.time;
3049 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3050 // optional entity parameter for self (EXT_ENTITYPARAM)
3051 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(ent);
3052 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3054 // make sure the velocity is still sane (not a NaN)
3055 SV_CheckVelocity(ent);
3058 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3060 prvm_prog_t *prog = SVVM_prog;
3061 // don't do physics on disconnected clients, FrikBot relies on this
3062 if (!host_client->begun)
3065 // make sure the velocity is sane (not a NaN)
3066 SV_CheckVelocity(ent);
3068 // call standard player post-think
3069 PRVM_serverglobalfloat(time) = sv.time;
3070 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3071 // optional entity parameter for self (EXT_ENTITYPARAM)
3072 PRVM_G_INT(OFS_PARM0) = PRVM_EDICT_TO_PROG(ent);
3073 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3075 // make sure the velocity is still sane (not a NaN)
3076 SV_CheckVelocity(ent);
3078 if(PRVM_serveredictfloat(ent, fixangle))
3080 // angle fixing was requested by physics code...
3081 // so store the current angles for later use
3082 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3083 host_client->fixangle_angles_set = true;
3085 // and clear fixangle for the next frame
3086 PRVM_serveredictfloat(ent, fixangle) = 0;
3089 // decrement the countdown variable used to decide when to go back to
3090 // synchronous physics
3091 if (host_client->clmovement_inputtimeout > sv.frametime)
3092 host_client->clmovement_inputtimeout -= sv.frametime;
3094 host_client->clmovement_inputtimeout = 0;
3097 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3099 prvm_prog_t *prog = SVVM_prog;
3100 // don't do physics on disconnected clients, FrikBot relies on this
3101 if (!host_client->begun)
3103 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3107 // make sure the velocity is sane (not a NaN)
3108 SV_CheckVelocity(ent);
3110 switch ((int) PRVM_serveredictfloat(ent, movetype))
3113 case MOVETYPE_FAKEPUSH:
3114 SV_Physics_Pusher (ent);
3117 // LadyHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3118 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3121 case MOVETYPE_FOLLOW:
3123 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3124 SV_Physics_Follow (ent);
3126 case MOVETYPE_NOCLIP:
3128 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3131 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3132 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3136 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3137 SV_Physics_Step (ent);
3138 if (SV_RunThink(ent))
3139 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
3141 ent->priv.server->waterposition_forceupdate = false;
3142 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
3143 SV_CheckWaterTransition(ent);
3148 // don't run physics here if running asynchronously
3149 if (host_client->clmovement_inputtimeout <= 0)
3153 case MOVETYPE_BOUNCE:
3154 case MOVETYPE_BOUNCEMISSILE:
3155 case MOVETYPE_FLYMISSILE:
3158 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3159 SV_Physics_Toss (ent);
3162 case MOVETYPE_FLY_WORLDONLY:
3164 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3167 case MOVETYPE_PHYSICS:
3171 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
3173 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3177 SV_CheckVelocity (ent);
3180 SV_LinkEdict_TouchAreaGrid(ent);
3182 SV_CheckVelocity (ent);
3191 void SV_Physics (void)
3193 prvm_prog_t *prog = SVVM_prog;
3197 // free memory for resources that are no longer referenced
3198 PRVM_GarbageCollection(prog);
3200 // let the progs know that a new frame has started
3201 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3202 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3203 PRVM_serverglobalfloat(time) = sv.time;
3204 PRVM_serverglobalfloat(frametime) = sv.frametime;
3205 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3207 // run physics engine
3208 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3211 // treat each object in turn
3214 // if force_retouch, relink all the entities
3215 if (PRVM_serverglobalfloat(force_retouch) > 0)
3216 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3217 if (!ent->priv.server->free)
3218 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3220 if (sv_gameplayfix_consistentplayerprethink.integer)
3222 // run physics on the client entities in 3 stages
3223 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3224 if (!ent->priv.server->free)
3225 SV_Physics_ClientEntity_PreThink(ent);
3227 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3228 if (!ent->priv.server->free)
3229 SV_Physics_ClientEntity(ent);
3231 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3232 if (!ent->priv.server->free)
3233 SV_Physics_ClientEntity_PostThink(ent);
3237 // run physics on the client entities
3238 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3240 if (!ent->priv.server->free)
3242 SV_Physics_ClientEntity_PreThink(ent);
3243 SV_Physics_ClientEntity(ent);
3244 SV_Physics_ClientEntity_PostThink(ent);
3249 // run physics on all the non-client entities
3250 if (!sv_freezenonclients.integer)
3252 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3253 if (!ent->priv.server->free)
3254 SV_Physics_Entity(ent);
3255 // make a second pass to see if any ents spawned this frame and make
3256 // sure they run their move/think
3257 if (sv_gameplayfix_delayprojectiles.integer < 0)
3258 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3259 if (!ent->priv.server->move && !ent->priv.server->free)
3260 SV_Physics_Entity(ent);
3263 if (PRVM_serverglobalfloat(force_retouch) > 0)
3264 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3266 // LadyHavoc: endframe support
3267 if (PRVM_serverfunction(EndFrame))
3269 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3270 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3271 PRVM_serverglobalfloat(time) = sv.time;
3272 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3275 // decrement prog->num_edicts if the highest number entities died
3276 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3278 if (!sv_freezenonclients.integer)
3279 sv.time += sv.frametime;