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];
130 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
132 VectorCopy(start, clipstart);
133 VectorClear(clipmins2);
134 VectorClear(clipmaxs2);
135 #if COLLISIONPARANOID >= 3
136 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
140 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
141 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
142 if (cliptrace.startsolid || cliptrace.fraction < 1)
143 cliptrace.ent = prog->edicts;
144 if (type == MOVE_WORLDONLY)
147 if (type == MOVE_MISSILE)
149 // LordHavoc: modified this, was = -15, now -= 15
150 for (i = 0;i < 3;i++)
157 // create the bounding box of the entire move
158 for (i = 0;i < 3;i++)
160 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
161 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
164 // debug override to test against everything
165 if (sv_debugmove.integer)
167 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
168 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
171 // if the passedict is world, make it NULL (to avoid two checks each time)
172 if (passedict == prog->edicts)
174 // precalculate prog value for passedict for comparisons
175 passedictprog = PRVM_EDICT_TO_PROG(passedict);
176 // precalculate passedict's owner edict pointer for comparisons
177 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
180 // because this uses World_EntitiestoBox, we know all entity boxes overlap
181 // the clip region, so we can skip culling checks in the loop below
182 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
183 if (numtouchedicts > MAX_EDICTS)
185 // this never happens
186 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
187 numtouchedicts = MAX_EDICTS;
189 for (i = 0;i < numtouchedicts;i++)
191 touch = touchedicts[i];
193 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
195 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
200 // don't clip against self
201 if (passedict == touch)
203 // don't clip owned entities against owner
204 if (traceowner == touch)
206 // don't clip owner against owned entities
207 if (passedictprog == PRVM_serveredictedict(touch, owner))
209 // don't clip points against points (they can't collide)
210 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
214 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
216 // might interact, so do an exact clip
218 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
220 model = SV_GetModelFromEdict(touch);
221 pitchsign = SV_GetPitchSign(prog, touch);
224 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);
226 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
227 Matrix4x4_Invert_Simple(&imatrix, &matrix);
228 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
229 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
230 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
231 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
232 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
233 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
234 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);
236 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
238 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
250 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)
252 prvm_prog_t *prog = SVVM_prog;
253 int i, bodysupercontents;
256 prvm_edict_t *traceowner, *touch;
258 // temporary storage because prvm_vec_t may differ from vec_t
259 vec3_t touchmins, touchmaxs;
260 // bounding box of entire move area
261 vec3_t clipboxmins, clipboxmaxs;
262 // size when clipping against monsters
263 vec3_t clipmins2, clipmaxs2;
264 // start and end origin of move
265 vec3_t clipstart, clipend;
268 // matrices to transform into/out of other entity's space
269 matrix4x4_t matrix, imatrix;
270 // model of other entity
272 // list of entities to test for collisions
274 static prvm_edict_t *touchedicts[MAX_EDICTS];
275 if (VectorCompare(start, end))
276 return SV_TracePoint(start, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
278 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
280 VectorCopy(start, clipstart);
281 VectorCopy(end, clipend);
282 VectorClear(clipmins2);
283 VectorClear(clipmaxs2);
284 #if COLLISIONPARANOID >= 3
285 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
289 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, false);
290 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
291 if (cliptrace.startsolid || cliptrace.fraction < 1)
292 cliptrace.ent = prog->edicts;
293 if (type == MOVE_WORLDONLY)
296 if (type == MOVE_MISSILE)
298 // LordHavoc: modified this, was = -15, now -= 15
299 for (i = 0;i < 3;i++)
306 // create the bounding box of the entire move
307 for (i = 0;i < 3;i++)
309 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
310 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
313 // debug override to test against everything
314 if (sv_debugmove.integer)
316 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
317 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
320 // if the passedict is world, make it NULL (to avoid two checks each time)
321 if (passedict == prog->edicts)
323 // precalculate prog value for passedict for comparisons
324 passedictprog = PRVM_EDICT_TO_PROG(passedict);
325 // precalculate passedict's owner edict pointer for comparisons
326 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
329 // because this uses World_EntitiestoBox, we know all entity boxes overlap
330 // the clip region, so we can skip culling checks in the loop below
331 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
332 if (numtouchedicts > MAX_EDICTS)
334 // this never happens
335 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
336 numtouchedicts = MAX_EDICTS;
338 for (i = 0;i < numtouchedicts;i++)
340 touch = touchedicts[i];
342 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
344 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
349 // don't clip against self
350 if (passedict == touch)
352 // don't clip owned entities against owner
353 if (traceowner == touch)
355 // don't clip owner against owned entities
356 if (passedictprog == PRVM_serveredictedict(touch, owner))
358 // don't clip points against points (they can't collide)
359 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
363 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
365 // might interact, so do an exact clip
367 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
369 model = SV_GetModelFromEdict(touch);
370 pitchsign = SV_GetPitchSign(prog, touch);
373 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);
375 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
376 Matrix4x4_Invert_Simple(&imatrix, &matrix);
377 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
378 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
379 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
380 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
381 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
382 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
383 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);
385 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend, false);
387 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
399 #if COLLISIONPARANOID >= 1
400 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)
402 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)
405 prvm_prog_t *prog = SVVM_prog;
406 vec3_t hullmins, hullmaxs;
407 int i, bodysupercontents;
411 prvm_edict_t *traceowner, *touch;
413 // temporary storage because prvm_vec_t may differ from vec_t
414 vec3_t touchmins, touchmaxs;
415 // bounding box of entire move area
416 vec3_t clipboxmins, clipboxmaxs;
417 // size of the moving object
418 vec3_t clipmins, clipmaxs;
419 // size when clipping against monsters
420 vec3_t clipmins2, clipmaxs2;
421 // start and end origin of move
422 vec3_t clipstart, clipend;
425 // matrices to transform into/out of other entity's space
426 matrix4x4_t matrix, imatrix;
427 // model of other entity
429 // list of entities to test for collisions
431 static prvm_edict_t *touchedicts[MAX_EDICTS];
432 if (VectorCompare(mins, maxs))
434 vec3_t shiftstart, shiftend;
435 VectorAdd(start, mins, shiftstart);
436 VectorAdd(end, mins, shiftend);
437 if (VectorCompare(start, end))
438 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
440 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
441 VectorSubtract(trace.endpos, mins, trace.endpos);
445 VectorCopy(start, clipstart);
446 VectorCopy(end, clipend);
447 VectorCopy(mins, clipmins);
448 VectorCopy(maxs, clipmaxs);
449 VectorCopy(mins, clipmins2);
450 VectorCopy(maxs, clipmaxs2);
451 #if COLLISIONPARANOID >= 3
452 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
456 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, extend);
457 cliptrace.worldstartsolid = cliptrace.bmodelstartsolid = cliptrace.startsolid;
458 if (cliptrace.startsolid || cliptrace.fraction < 1)
459 cliptrace.ent = prog->edicts;
460 if (type == MOVE_WORLDONLY)
463 if (type == MOVE_MISSILE)
465 // LordHavoc: modified this, was = -15, now -= 15
466 for (i = 0;i < 3;i++)
473 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
474 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
475 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
478 VectorCopy(clipmins, hullmins);
479 VectorCopy(clipmaxs, hullmaxs);
482 // create the bounding box of the entire move
483 for (i = 0;i < 3;i++)
485 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
486 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
489 // debug override to test against everything
490 if (sv_debugmove.integer)
492 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
493 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
496 // if the passedict is world, make it NULL (to avoid two checks each time)
497 if (passedict == prog->edicts)
499 // precalculate prog value for passedict for comparisons
500 passedictprog = PRVM_EDICT_TO_PROG(passedict);
501 // figure out whether this is a point trace for comparisons
502 pointtrace = VectorCompare(clipmins, clipmaxs);
503 // precalculate passedict's owner edict pointer for comparisons
504 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
507 // because this uses World_EntitiestoBox, we know all entity boxes overlap
508 // the clip region, so we can skip culling checks in the loop below
509 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
510 if (numtouchedicts > MAX_EDICTS)
512 // this never happens
513 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
514 numtouchedicts = MAX_EDICTS;
516 for (i = 0;i < numtouchedicts;i++)
518 touch = touchedicts[i];
520 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
522 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
527 // don't clip against self
528 if (passedict == touch)
530 // don't clip owned entities against owner
531 if (traceowner == touch)
533 // don't clip owner against owned entities
534 if (passedictprog == PRVM_serveredictedict(touch, owner))
536 // don't clip points against points (they can't collide)
537 if (pointtrace && VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
541 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
543 // might interact, so do an exact clip
545 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
547 model = SV_GetModelFromEdict(touch);
548 pitchsign = SV_GetPitchSign(prog, touch);
551 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);
553 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
554 Matrix4x4_Invert_Simple(&imatrix, &matrix);
555 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
556 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
557 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
558 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
559 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
560 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
561 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);
563 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);
565 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
572 #if COLLISIONPARANOID >= 1
573 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)
575 prvm_prog_t *prog = SVVM_prog;
579 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
582 VectorCopy(trace.endpos, temp);
583 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask).startsolid;
584 #if COLLISIONPARANOID < 3
585 if (trace.startsolid || endstuck)
587 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" : "");
593 int SV_PointSuperContents(const vec3_t point)
595 prvm_prog_t *prog = SVVM_prog;
596 int supercontents = 0;
600 // matrices to transform into/out of other entity's space
601 matrix4x4_t matrix, imatrix;
602 // model of other entity
605 // list of entities to test for collisions
607 static prvm_edict_t *touchedicts[MAX_EDICTS];
609 // get world supercontents at this point
610 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
611 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
613 // if sv_gameplayfix_swiminbmodels is off we're done
614 if (!sv_gameplayfix_swiminbmodels.integer)
615 return supercontents;
617 // get list of entities at this point
618 numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
619 if (numtouchedicts > MAX_EDICTS)
621 // this never happens
622 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
623 numtouchedicts = MAX_EDICTS;
625 for (i = 0;i < numtouchedicts;i++)
627 touch = touchedicts[i];
629 // we only care about SOLID_BSP for pointcontents
630 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
633 // might interact, so do an exact clip
634 model = SV_GetModelFromEdict(touch);
635 if (!model || !model->PointSuperContents)
637 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);
638 Matrix4x4_Invert_Simple(&imatrix, &matrix);
639 Matrix4x4_Transform(&imatrix, point, transformed);
640 frame = (int)PRVM_serveredictfloat(touch, frame);
641 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
644 return supercontents;
648 ===============================================================================
650 Linking entities into the world culling system
652 ===============================================================================
655 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
657 prvm_prog_t *prog = SVVM_prog;
658 vec3_t paddedmins, paddedmaxs;
659 if (maxedicts < 1 || resultedicts == NULL)
661 // LordHavoc: discovered this actually causes its own bugs (dm6 teleporters being too close to info_teleport_destination)
662 //VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
663 //VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
664 VectorCopy(mins, paddedmins);
665 VectorCopy(maxs, paddedmaxs);
666 if (sv_areadebug.integer)
668 int numresultedicts = 0;
671 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
673 ed = PRVM_EDICT_NUM(edictindex);
674 if (!ed->priv.required->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
676 resultedicts[numresultedicts++] = ed;
677 if (numresultedicts == maxedicts)
681 return numresultedicts;
684 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
687 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
689 prvm_prog_t *prog = SVVM_prog;
690 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
691 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
692 PRVM_serverglobalfloat(time) = sv.time;
693 PRVM_serverglobalfloat(trace_allsolid) = false;
694 PRVM_serverglobalfloat(trace_startsolid) = false;
695 PRVM_serverglobalfloat(trace_fraction) = 1;
696 PRVM_serverglobalfloat(trace_inwater) = false;
697 PRVM_serverglobalfloat(trace_inopen) = true;
698 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
699 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
700 PRVM_serverglobalfloat(trace_plane_dist) = 0;
701 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
702 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
703 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
704 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
705 PRVM_serverglobalstring(trace_dphittexturename) = 0;
706 prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
709 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
711 prvm_prog_t *prog = SVVM_prog;
712 int i, numtouchedicts, old_self, old_other;
714 static prvm_edict_t *touchedicts[MAX_EDICTS];
716 if (ent == prog->edicts)
717 return; // don't add the world
719 if (ent->priv.server->free)
722 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
725 // build a list of edicts to touch, because the link loop can be corrupted
726 // by IncreaseEdicts called during touch functions
727 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
728 if (numtouchedicts > MAX_EDICTS)
730 // this never happens
731 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
732 numtouchedicts = MAX_EDICTS;
735 old_self = PRVM_serverglobaledict(self);
736 old_other = PRVM_serverglobaledict(other);
737 for (i = 0;i < numtouchedicts;i++)
739 touch = touchedicts[i];
740 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
742 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
745 PRVM_serverglobaledict(self) = old_self;
746 PRVM_serverglobaledict(other) = old_other;
749 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
753 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
755 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
756 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
757 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
758 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];
759 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];
760 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
761 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];
762 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];
763 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
764 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];
765 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];
766 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
767 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];
768 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];
769 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
770 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];
771 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];
772 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
773 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];
774 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];
775 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
776 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
777 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
786 void SV_LinkEdict (prvm_edict_t *ent)
788 prvm_prog_t *prog = SVVM_prog;
790 vec3_t mins, maxs, entmins, entmaxs, entangles;
793 if (ent == prog->edicts)
794 return; // don't add the world
796 if (ent->priv.server->free)
799 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
800 if (modelindex < 0 || modelindex >= MAX_MODELS)
802 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
805 model = SV_GetModelByIndex(modelindex);
807 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
808 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
809 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
813 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
815 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
816 // TODO special handling for spheres?
817 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
818 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
819 VectorCopy(PRVM_serveredictvector(ent, angles), entangles);
820 RotateBBox(entmins, entmaxs, entangles, mins, maxs);
821 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
822 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
824 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
828 if (!model->TraceBox)
829 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
831 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
833 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
834 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
836 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
838 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
839 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
843 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
844 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
849 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
850 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
851 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
856 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
857 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
861 // to make items easier to pick up and allow them to be grabbed off
862 // of shelves, the abs sizes are expanded
864 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
875 // because movement is clipped an epsilon away from an actual edge,
876 // we must fully check even when bounding boxes don't quite touch
885 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
886 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
888 World_LinkEdict(&sv.world, ent, mins, maxs);
892 ===============================================================================
896 ===============================================================================
901 SV_TestEntityPosition
903 returns true if the entity is in solid currently
906 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
908 prvm_prog_t *prog = SVVM_prog;
909 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
910 int skipsupercontentsmask = 0;
911 int skipmaterialflagsmask = 0;
912 vec3_t org, entorigin, entmins, entmaxs;
914 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
915 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
916 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
917 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
918 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);
919 if (trace.startsupercontents & hitsupercontentsmask)
923 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
925 // q1bsp/hlbsp use hulls and if the entity does not exactly match
926 // a hull size it is incorrectly tested, so this code tries to
927 // 'fix' it slightly...
928 // FIXME: this breaks entities larger than the hull size
931 VectorAdd(org, entmins, m1);
932 VectorAdd(org, entmaxs, m2);
933 VectorSubtract(m2, m1, s);
934 #define EPSILON (1.0f / 32.0f)
935 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
936 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
937 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
938 for (i = 0;i < 8;i++)
940 v[0] = (i & 1) ? m2[0] : m1[0];
941 v[1] = (i & 2) ? m2[1] : m1[1];
942 v[2] = (i & 4) ? m2[2] : m1[2];
943 if (SV_PointSuperContents(v) & hitsupercontentsmask)
948 // if the trace found a better position for the entity, move it there
949 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
952 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
953 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
955 // verify if the endpos is REALLY outside solid
956 VectorCopy(trace.endpos, org);
957 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
959 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
961 VectorCopy(org, PRVM_serveredictvector(ent, origin));
967 // DRESK - Support for Entity Contents Transition Event
970 SV_CheckContentsTransition
972 returns true if entity had a valid contentstransition function call
975 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
977 prvm_prog_t *prog = SVVM_prog;
978 int bValidFunctionCall;
980 // Default Valid Function Call to False
981 bValidFunctionCall = false;
983 if(PRVM_serveredictfloat(ent, watertype) != nContents)
984 { // Changed Contents
985 // Acquire Contents Transition Function from QC
986 if(PRVM_serveredictfunction(ent, contentstransition))
987 { // Valid Function; Execute
988 // Assign Valid Function
989 bValidFunctionCall = true;
990 // Prepare Parameters (Original Contents, New Contents)
992 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
994 PRVM_G_FLOAT(OFS_PARM1) = nContents;
996 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
998 PRVM_serverglobalfloat(time) = sv.time;
999 // Execute VM Function
1000 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1004 // Return if Function Call was Valid
1005 return bValidFunctionCall;
1014 void SV_CheckVelocity (prvm_edict_t *ent)
1016 prvm_prog_t *prog = SVVM_prog;
1023 for (i=0 ; i<3 ; i++)
1025 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1027 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1028 PRVM_serveredictvector(ent, velocity)[i] = 0;
1030 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1032 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1033 PRVM_serveredictvector(ent, origin)[i] = 0;
1037 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1038 // player_run/player_stand1 does not horribly malfunction if the
1039 // velocity becomes a denormalized float
1040 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0000001)
1041 VectorClear(PRVM_serveredictvector(ent, velocity));
1043 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1044 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1045 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1047 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1048 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1049 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1050 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1058 Runs thinking code if time. There is some play in the exact time the think
1059 function will be called, because it is called before any movement is done
1060 in a frame. Not used for pushmove objects, because they must be exact.
1061 Returns false if the entity removed itself.
1064 static qboolean SV_RunThink (prvm_edict_t *ent)
1066 prvm_prog_t *prog = SVVM_prog;
1069 // don't let things stay in the past.
1070 // it is possible to start that way by a trigger with a local time.
1071 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1074 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1076 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1077 PRVM_serveredictfloat(ent, nextthink) = 0;
1078 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1079 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1080 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1081 // mods often set nextthink to time to cause a think every frame,
1082 // we don't want to loop in that case, so exit if the new nextthink is
1083 // <= the time the qc was told, also exit if it is past the end of the
1085 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1088 return !ent->priv.server->free;
1095 Two entities have touched, so run their touch functions
1098 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1100 prvm_prog_t *prog = SVVM_prog;
1101 int restorevm_tempstringsbuf_cursize;
1102 int old_self, old_other;
1103 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1105 old_self = PRVM_serverglobaledict(self);
1106 old_other = PRVM_serverglobaledict(other);
1107 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1109 VM_SetTraceGlobals(prog, trace);
1111 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1113 PRVM_serverglobalfloat(time) = sv.time;
1114 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1115 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1116 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1119 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1121 PRVM_serverglobalfloat(time) = sv.time;
1122 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1123 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1124 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1125 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1126 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1127 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1128 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1129 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1130 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1131 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1132 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1135 PRVM_serverglobaledict(self) = old_self;
1136 PRVM_serverglobaledict(other) = old_other;
1137 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1145 Slide off of the impacting object
1146 returns the blocked flags (1 = floor, 2 = step / wall)
1149 #define STOP_EPSILON 0.1
1150 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1155 backoff = -DotProduct (in, normal) * overbounce;
1156 VectorMA(in, backoff, normal, out);
1158 for (i = 0;i < 3;i++)
1159 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1168 The basic solid body movement clip that slides along multiple planes
1169 Returns the clipflags if the velocity was modified (hit something solid)
1173 8 = teleported by touch method
1174 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1177 static float SV_Gravity (prvm_edict_t *ent);
1178 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink);
1179 #define MAX_CLIP_PLANES 5
1180 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float stepheight)
1182 prvm_prog_t *prog = SVVM_prog;
1183 int blocked, bumpcount;
1184 int i, j, numplanes;
1185 float d, time_left, gravity;
1186 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1187 prvm_vec3_t primal_velocity, original_velocity, new_velocity, restore_velocity;
1196 VectorCopy(PRVM_serveredictvector(ent, velocity), restore_velocity);
1200 gravity = SV_Gravity(ent);
1202 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1204 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1205 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1207 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1212 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1213 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1216 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1218 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1221 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1222 if(!SV_PushEntity(&trace, ent, push, false))
1224 // we got teleported by a touch function
1225 // let's abort the move
1230 // this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
1231 // abort move if we're stuck in the world (and didn't make it out)
1232 if (trace.worldstartsolid && trace.allsolid)
1234 VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
1238 if (trace.fraction == 1)
1240 if (trace.plane.normal[2])
1242 if (trace.plane.normal[2] > 0.7)
1249 Con_Printf ("SV_FlyMove: !trace.ent");
1250 trace.ent = prog->edicts;
1253 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1254 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1257 else if (stepheight)
1259 // step - handle it immediately
1265 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1266 VectorSet(steppush, 0, 0, stepheight);
1267 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1268 if(!SV_PushEntity(&steptrace, ent, steppush, false))
1273 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1274 if(!SV_PushEntity(&steptrace2, ent, push, false))
1279 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1280 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1281 if(!SV_PushEntity(&steptrace3, ent, steppush, false))
1286 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1287 // accept the new position if it made some progress...
1288 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1290 //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]);
1292 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1293 time_left *= 1 - trace.fraction;
1299 //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]);
1300 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1305 // step - return it to caller
1307 // save the trace for player extrafriction
1309 VectorCopy(trace.plane.normal, stepnormal);
1311 if (trace.fraction >= 0.001)
1313 // actually covered some distance
1314 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1318 time_left *= 1 - trace.fraction;
1320 // clipped to another plane
1321 if (numplanes >= MAX_CLIP_PLANES)
1323 // this shouldn't really happen
1324 VectorClear(PRVM_serveredictvector(ent, velocity));
1330 for (i = 0;i < numplanes;i++)
1331 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1335 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1340 VectorCopy(trace.plane.normal, planes[numplanes]);
1343 // modify original_velocity so it parallels all of the clip planes
1344 for (i = 0;i < numplanes;i++)
1346 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1347 for (j = 0;j < numplanes;j++)
1352 if (DotProduct(new_velocity, planes[j]) < 0)
1362 // go along this plane
1363 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1367 // go along the crease
1370 VectorClear(PRVM_serveredictvector(ent, velocity));
1374 CrossProduct(planes[0], planes[1], dir);
1375 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1376 VectorNormalize(dir);
1377 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1378 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1381 // if current velocity is against the original velocity,
1382 // stop dead to avoid tiny occilations in sloping corners
1383 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1385 VectorClear(PRVM_serveredictvector(ent, velocity));
1390 //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]);
1393 if ((blocked & 1) == 0 && bumpcount > 1)
1395 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1396 // flag ONGROUND if there's ground under it
1397 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask);
1401 // LordHavoc: this came from QW and allows you to get out of water more easily
1402 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1403 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1407 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1409 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1410 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1423 static float SV_Gravity (prvm_edict_t *ent)
1425 prvm_prog_t *prog = SVVM_prog;
1428 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1431 return ent_gravity * sv_gravity.value * sv.frametime;
1436 ===============================================================================
1440 ===============================================================================
1443 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1445 prvm_prog_t *prog = SVVM_prog;
1449 vec3_t stuckmins, stuckmaxs;
1450 vec3_t goodmins, goodmaxs;
1454 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1455 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1456 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1457 VectorCopy(pivot, goodmins);
1458 VectorCopy(pivot, goodmaxs);
1459 for (bump = 0;bump < 6;bump++)
1461 int coord = 2-(bump >> 1);
1462 //int coord = (bump >> 1);
1463 int dir = (bump & 1);
1466 for(subbump = 0; ; ++subbump)
1468 VectorCopy(stuckorigin, testorigin);
1472 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1477 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1480 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1481 if (stucktrace.bmodelstartsolid)
1483 // BAD BAD, can't fix that
1487 if (stucktrace.fraction >= 1)
1492 // BAD BAD, can't fix that
1496 // we hit something... let's move out of it
1497 VectorSubtract(stucktrace.endpos, testorigin, move);
1498 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1499 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1503 Con_Printf("subbump: %d\n", subbump);
1509 goodmaxs[coord] = stuckmaxs[coord];
1514 goodmins[coord] = stuckmins[coord];
1519 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1524 qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1526 prvm_prog_t *prog = SVVM_prog;
1530 vec3_t stuckmins, stuckmaxs;
1532 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1533 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1534 separation = 0.0f; // when using hulls, it can not be enlarged
1535 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1536 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1537 stuckmins[0] -= separation;
1538 stuckmins[1] -= separation;
1539 stuckmins[2] -= separation;
1540 stuckmaxs[0] += separation;
1541 stuckmaxs[1] += separation;
1542 stuckmaxs[2] += separation;
1543 // first pass we try to get it out of brush entities
1544 // second pass we try to get it out of world only (can't win them all)
1545 for (pass = 0;pass < 2;pass++)
1547 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1548 for (bump = 0;bump < 10;bump++)
1550 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, pass ? MOVE_WORLDONLY : MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1551 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1553 // found a good location, use it
1554 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1557 nudge = -stucktrace.startdepth;
1558 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1568 Does not change the entities velocity at all
1569 The trace struct is filled with the trace that has been done.
1570 Returns true if the push did not result in the entity being teleported by QC code.
1573 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean dolink)
1575 prvm_prog_t *prog = SVVM_prog;
1583 solid = (int)PRVM_serveredictfloat(ent, solid);
1584 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1585 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1586 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1588 // move start position out of solids
1589 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1591 SV_NudgeOutOfSolid(ent);
1594 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1595 VectorAdd(start, push, end);
1597 if (movetype == MOVETYPE_FLYMISSILE)
1598 type = MOVE_MISSILE;
1599 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1600 type = MOVE_WORLDONLY;
1601 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1602 type = MOVE_NOMONSTERS; // only clip against bmodels
1606 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
1607 // fail the move if stuck in world
1608 if (trace->worldstartsolid)
1611 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1613 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1618 if(!trace->startsolid)
1619 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)
1621 Con_Printf("something eeeeevil happened\n");
1626 SV_LinkEdict_TouchAreaGrid(ent);
1628 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))))
1629 SV_Impact (ent, trace);
1631 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1633 ent->priv.required->mark = 0;
1636 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1638 ent->priv.required->mark = 0;
1643 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1655 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1657 prvm_prog_t *prog = SVVM_prog;
1659 int pusherowner, pusherprog;
1662 float savesolid, movetime2, pushltime;
1663 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1665 int numcheckentities;
1666 static prvm_edict_t *checkentities[MAX_EDICTS];
1667 dp_model_t *pushermodel;
1668 trace_t trace, trace2;
1669 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1670 static unsigned short moved_edicts[MAX_EDICTS];
1673 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])
1675 PRVM_serveredictfloat(pusher, ltime) += movetime;
1679 switch ((int) PRVM_serveredictfloat(pusher, solid))
1681 // LordHavoc: valid pusher types
1684 case SOLID_SLIDEBOX:
1685 case SOLID_CORPSE: // LordHavoc: this would be weird...
1687 // LordHavoc: no collisions
1690 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1691 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1692 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1693 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1694 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1695 PRVM_serveredictfloat(pusher, ltime) += movetime;
1696 SV_LinkEdict(pusher);
1699 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1702 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1703 if (index < 1 || index >= MAX_MODELS)
1705 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1708 pushermodel = SV_GetModelByIndex(index);
1709 pusherowner = PRVM_serveredictedict(pusher, owner);
1710 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1712 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1714 movetime2 = movetime;
1715 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1716 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1717 if (moveangle[0] || moveangle[2])
1719 for (i = 0;i < 3;i++)
1723 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1724 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1728 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1729 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1733 else if (moveangle[1])
1735 for (i = 0;i < 3;i++)
1739 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1740 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1744 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1745 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1751 for (i = 0;i < 3;i++)
1755 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1756 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1760 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1761 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1766 VectorNegate (moveangle, a);
1767 AngleVectorsFLU (a, forward, left, up);
1769 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1770 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1771 pushltime = PRVM_serveredictfloat(pusher, ltime);
1773 // move the pusher to its final position
1775 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1776 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1777 PRVM_serveredictfloat(pusher, ltime) += movetime;
1778 SV_LinkEdict(pusher);
1780 pushermodel = SV_GetModelFromEdict(pusher);
1781 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);
1782 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1784 savesolid = PRVM_serveredictfloat(pusher, solid);
1786 // see if any solid entities are inside the final position
1789 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1790 numcheckentities = 0;
1791 else // MOVETYPE_PUSH
1792 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1793 for (e = 0;e < numcheckentities;e++)
1795 prvm_edict_t *check = checkentities[e];
1796 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1801 case MOVETYPE_FOLLOW:
1802 case MOVETYPE_NOCLIP:
1803 case MOVETYPE_FLY_WORLDONLY:
1809 if (PRVM_serveredictedict(check, owner) == pusherprog)
1812 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1815 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1817 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1818 check->priv.server->waterposition_forceupdate = true;
1820 checkcontents = SV_GenericHitSuperContentsMask(check);
1822 // if the entity is standing on the pusher, it will definitely be moved
1823 // if the entity is not standing on the pusher, but is in the pusher's
1824 // final position, move it
1825 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1827 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1828 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1829 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1830 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1831 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1832 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);
1833 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1834 if (!trace.startsolid)
1836 //Con_Printf("- not in solid\n");
1841 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1842 //VectorClear(pivot);
1847 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1848 VectorAdd (org, pivot, org);
1849 org2[0] = DotProduct (org, forward);
1850 org2[1] = DotProduct (org, left);
1851 org2[2] = DotProduct (org, up);
1852 VectorSubtract (org2, org, move);
1853 VectorAdd (move, move1, move);
1856 VectorCopy (move1, move);
1858 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1860 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1861 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1862 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1864 // physics objects need better collisions than this code can do
1865 if (movetype == MOVETYPE_PHYSICS)
1867 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1868 SV_LinkEdict(check);
1869 SV_LinkEdict_TouchAreaGrid(check);
1873 // try moving the contacted entity
1874 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1875 if(!SV_PushEntity (&trace, check, move, true))
1877 // entity "check" got teleported
1878 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1879 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1880 continue; // pushed enough
1882 // FIXME: turn players specially
1883 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1884 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1885 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1887 // this trace.fraction < 1 check causes items to fall off of pushers
1888 // if they pass under or through a wall
1889 // the groundentity check causes items to fall off of ledges
1890 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1891 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1893 // if it is still inside the pusher, block
1894 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1895 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1896 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1897 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1898 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1899 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);
1900 if (trace.startsolid)
1903 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1905 // hack to invoke all necessary movement triggers
1907 if(!SV_PushEntity(&trace2, check, move2, true))
1909 // entity "check" got teleported
1916 // still inside pusher, so it's really blocked
1919 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1921 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1924 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1925 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1929 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1930 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1931 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1932 SV_LinkEdict(pusher);
1934 // move back any entities we already moved
1935 for (i = 0;i < num_moved;i++)
1937 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1938 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1939 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1943 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1944 if (PRVM_serveredictfunction(pusher, blocked))
1946 PRVM_serverglobalfloat(time) = sv.time;
1947 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1948 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1949 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1954 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1955 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1956 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1965 static void SV_Physics_Pusher (prvm_edict_t *ent)
1967 prvm_prog_t *prog = SVVM_prog;
1968 float thinktime, oldltime, movetime;
1970 oldltime = PRVM_serveredictfloat(ent, ltime);
1972 thinktime = PRVM_serveredictfloat(ent, nextthink);
1973 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
1975 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
1980 movetime = sv.frametime;
1983 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
1984 SV_PushMove (ent, movetime);
1986 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
1988 PRVM_serveredictfloat(ent, nextthink) = 0;
1989 PRVM_serverglobalfloat(time) = sv.time;
1990 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1991 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1992 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1998 ===============================================================================
2002 ===============================================================================
2005 static float unstickoffsets[] =
2007 // poutting -/+z changes first as they are least weird
2022 typedef enum unstickresult_e
2030 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2032 prvm_prog_t *prog = SVVM_prog;
2035 // if not stuck in a bmodel, just return
2036 if (!SV_TestEntityPosition(ent, vec3_origin))
2037 return UNSTICK_GOOD;
2039 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2041 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2043 VectorCopy(unstickoffsets + i, offset);
2045 //SV_LinkEdict_TouchAreaGrid(ent);
2046 return UNSTICK_UNSTUCK;
2050 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2051 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2053 for(i = 2; i <= maxunstick; ++i)
2055 VectorClear(offset);
2057 if (!SV_TestEntityPosition(ent, offset))
2060 //SV_LinkEdict_TouchAreaGrid(ent);
2061 return UNSTICK_UNSTUCK;
2064 if (!SV_TestEntityPosition(ent, offset))
2067 //SV_LinkEdict_TouchAreaGrid(ent);
2068 return UNSTICK_UNSTUCK;
2072 return UNSTICK_STUCK;
2075 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2077 prvm_prog_t *prog = SVVM_prog;
2079 switch(SV_UnstickEntityReturnOffset(ent, offset))
2083 case UNSTICK_UNSTUCK:
2084 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]);
2087 if (developer_extra.integer)
2088 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2091 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2100 This is a big hack to try and fix the rare case of getting stuck in the world
2104 static void SV_CheckStuck (prvm_edict_t *ent)
2106 prvm_prog_t *prog = SVVM_prog;
2109 switch(SV_UnstickEntityReturnOffset(ent, offset))
2112 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2114 case UNSTICK_UNSTUCK:
2115 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]);
2118 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2119 if (!SV_TestEntityPosition(ent, offset))
2121 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)));
2123 //SV_LinkEdict_TouchAreaGrid(ent);
2126 Con_DPrintf("Stuck player 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");
2139 static qboolean SV_CheckWater (prvm_edict_t *ent)
2141 prvm_prog_t *prog = SVVM_prog;
2143 int nNativeContents;
2146 point[0] = PRVM_serveredictvector(ent, origin)[0];
2147 point[1] = PRVM_serveredictvector(ent, origin)[1];
2148 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2150 // DRESK - Support for Entity Contents Transition Event
2151 // NOTE: Some logic needed to be slightly re-ordered
2152 // to not affect performance and allow for the feature.
2154 // Acquire Super Contents Prior to Resets
2155 cont = SV_PointSuperContents(point);
2156 // Acquire Native Contents Here
2157 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2159 // DRESK - Support for Entity Contents Transition Event
2160 if(PRVM_serveredictfloat(ent, watertype))
2161 // Entity did NOT Spawn; Check
2162 SV_CheckContentsTransition(ent, nNativeContents);
2165 PRVM_serveredictfloat(ent, waterlevel) = 0;
2166 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2167 cont = SV_PointSuperContents(point);
2168 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2170 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2171 PRVM_serveredictfloat(ent, waterlevel) = 1;
2172 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2173 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2175 PRVM_serveredictfloat(ent, waterlevel) = 2;
2176 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2177 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2178 PRVM_serveredictfloat(ent, waterlevel) = 3;
2182 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2191 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2193 prvm_prog_t *prog = SVVM_prog;
2195 vec3_t forward, into, side, v_angle;
2197 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2198 AngleVectors (v_angle, forward, NULL, NULL);
2199 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2201 // cut the tangential velocity
2202 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2203 VectorScale (stepnormal, i, into);
2204 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2205 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2206 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2212 =====================
2215 Player has come to a dead stop, possibly due to the problem with limited
2216 float precision at some angle joins in the BSP hull.
2218 Try fixing by pushing one pixel in each direction.
2220 This is a hack, but in the interest of good gameplay...
2221 ======================
2223 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2228 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2231 for (i=0 ; i<8 ; i++)
2233 // try pushing a little in an axial direction
2236 case 0: dir[0] = 2; dir[1] = 0; break;
2237 case 1: dir[0] = 0; dir[1] = 2; break;
2238 case 2: dir[0] = -2; dir[1] = 0; break;
2239 case 3: dir[0] = 0; dir[1] = -2; break;
2240 case 4: dir[0] = 2; dir[1] = 2; break;
2241 case 5: dir[0] = -2; dir[1] = 2; break;
2242 case 6: dir[0] = 2; dir[1] = -2; break;
2243 case 7: dir[0] = -2; dir[1] = -2; break;
2246 SV_PushEntity (&trace, ent, dir, false, true);
2248 // retry the original move
2249 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2250 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2251 PRVM_serveredictvector(ent, velocity)[2] = 0;
2252 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2254 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2255 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2257 Con_DPrint("TryUnstick - success.\n");
2261 // go back to the original pos and try again
2262 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2266 VectorClear (PRVM_serveredictvector(ent, velocity));
2267 Con_DPrint("TryUnstick - failure.\n");
2273 =====================
2276 Only used by players
2277 ======================
2279 static void SV_WalkMove (prvm_edict_t *ent)
2281 prvm_prog_t *prog = SVVM_prog;
2284 //int originalmove_clip;
2285 int originalmove_flags;
2286 int originalmove_groundentity;
2287 int hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2288 int skipsupercontentsmask = 0;
2289 int skipmaterialflagsmask = 0;
2291 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2292 trace_t downtrace, trace;
2293 qboolean applygravity;
2295 // if frametime is 0 (due to client sending the same timestamp twice),
2297 if (sv.frametime <= 0)
2300 if (sv_gameplayfix_unstickplayers.integer)
2301 SV_CheckStuck (ent);
2303 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2305 SV_CheckVelocity(ent);
2307 // do a regular slide move unless it looks like you ran into a step
2308 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2310 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2311 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2313 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2315 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2318 // only try this if there was no floor in the way in the trace (no,
2319 // this check seems to be not REALLY necessary, because if clip & 1,
2320 // our trace will hit that thing too)
2321 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2322 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2323 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2324 type = MOVE_MISSILE;
2325 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2326 type = MOVE_WORLDONLY;
2327 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2328 type = MOVE_NOMONSTERS; // only clip against bmodels
2331 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2332 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2333 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent), skipsupercontentsmask, skipmaterialflagsmask, collision_extendmovelength.value);
2334 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2335 clip |= 1; // but we HAVE found a floor
2338 // if the move did not hit the ground at any point, we're not on ground
2340 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2342 SV_CheckVelocity(ent);
2344 SV_LinkEdict_TouchAreaGrid(ent);
2346 if(clip & 8) // teleport
2349 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2352 if (sv_nostep.integer)
2355 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2356 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2357 //originalmove_clip = clip;
2358 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2359 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2361 // if move didn't block on a step, return
2364 // if move was not trying to move into the step, return
2365 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2368 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2370 // return if gibbed by a trigger
2371 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2374 // return if attempting to jump while airborn (unless sv_jumpstep)
2375 if (!sv_jumpstep.integer)
2376 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2380 // try moving up and forward to go up a step
2381 // back to start pos
2382 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2383 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2386 VectorClear (upmove);
2387 upmove[2] = sv_stepheight.value;
2388 if(!SV_PushEntity(&trace, ent, upmove, true))
2390 // we got teleported when upstepping... must abort the move
2395 PRVM_serveredictvector(ent, velocity)[2] = 0;
2396 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, skipsupercontentsmask, skipmaterialflagsmask, 0);
2397 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2400 // we got teleported when upstepping... must abort the move
2401 // note that z velocity handling may not be what QC expects here, but we cannot help it
2405 SV_CheckVelocity(ent);
2407 SV_LinkEdict_TouchAreaGrid(ent);
2409 // check for stuckness, possibly due to the limited precision of floats
2410 // in the clipping hulls
2412 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2413 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2415 //Con_Printf("wall\n");
2416 // stepping up didn't make any progress, revert to original move
2417 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2418 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2419 //clip = originalmove_clip;
2420 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2421 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2422 // now try to unstick if needed
2423 //clip = SV_TryUnstick (ent, oldvel);
2427 //Con_Printf("step - ");
2429 // extra friction based on view angle
2430 if (clip & 2 && sv_wallfriction.integer)
2431 SV_WallFriction (ent, stepnormal);
2433 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2434 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))
2438 VectorClear (downmove);
2439 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2440 if(!SV_PushEntity (&downtrace, ent, downmove, true))
2442 // we got teleported when downstepping... must abort the move
2446 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2448 // this has been disabled so that you can't jump when you are stepping
2449 // up while already jumping (also known as the Quake2 double jump bug)
2451 // LordHavoc: disabled this check so you can walk on monsters/players
2452 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2454 //Con_Printf("onground\n");
2455 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2456 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2462 //Con_Printf("slope\n");
2463 // if the push down didn't end up on good ground, use the move without
2464 // the step up. This happens near wall / slope combinations, and can
2465 // cause the player to hop up higher on a slope too steep to climb
2466 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2467 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2468 //clip = originalmove_clip;
2469 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2470 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2473 SV_CheckVelocity(ent);
2475 SV_LinkEdict_TouchAreaGrid(ent);
2478 //============================================================================
2484 Entities that are "stuck" to another entity
2487 static void SV_Physics_Follow (prvm_edict_t *ent)
2489 prvm_prog_t *prog = SVVM_prog;
2490 vec3_t vf, vr, vu, angles, v;
2493 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2494 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2495 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])
2497 // quick case for no rotation
2498 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2502 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2503 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2504 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2505 AngleVectors (angles, vf, vr, vu);
2506 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];
2507 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];
2508 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];
2509 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2510 angles[1] = PRVM_serveredictvector(e, angles)[1];
2511 angles[2] = PRVM_serveredictvector(e, angles)[2];
2512 AngleVectors (angles, vf, vr, vu);
2513 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2514 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2515 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2517 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2519 //SV_LinkEdict_TouchAreaGrid(ent);
2523 ==============================================================================
2527 ==============================================================================
2532 SV_CheckWaterTransition
2536 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2539 prvm_prog_t *prog = SVVM_prog;
2540 // LordHavoc: 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
2542 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2543 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(entorigin));
2544 if (!PRVM_serveredictfloat(ent, watertype))
2546 // just spawned here
2547 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2549 PRVM_serveredictfloat(ent, watertype) = cont;
2550 PRVM_serveredictfloat(ent, waterlevel) = 1;
2554 // DRESK - Support for Entity Contents Transition Event
2555 // NOTE: Call here BEFORE updating the watertype below,
2556 // and suppress watersplash sound if a valid function
2557 // call was made to allow for custom "splash" sounds.
2558 else if( !SV_CheckContentsTransition(ent, cont) )
2559 { // Contents Transition Function Invalid; Potentially Play Water Sound
2560 // check if the entity crossed into or out of water
2561 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2562 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2565 if (cont <= CONTENTS_WATER)
2567 PRVM_serveredictfloat(ent, watertype) = cont;
2568 PRVM_serveredictfloat(ent, waterlevel) = 1;
2572 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2573 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2581 Toss, bounce, and fly movement. When onground, do nothing.
2585 void SV_Physics_Toss (prvm_edict_t *ent)
2587 prvm_prog_t *prog = SVVM_prog;
2592 prvm_edict_t *groundentity;
2593 float d, ent_gravity;
2597 // if onground, return without moving
2598 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2600 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2601 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2603 // don't stick to ground if onground and moving upward
2604 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2606 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2608 // we can trust FL_ONGROUND if groundentity is world because it never moves
2611 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2613 // if ent was supported by a brush model on previous frame,
2614 // and groundentity is now freed, set groundentity to 0 (world)
2615 // which leaves it suspended in the air
2616 PRVM_serveredictedict(ent, groundentity) = 0;
2617 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2620 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2622 // don't slide if still touching the groundentity
2626 ent->priv.server->suspendedinairflag = false;
2628 SV_CheckVelocity (ent);
2631 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2632 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2635 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2637 movetime = sv.frametime;
2638 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2641 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2642 if(!SV_PushEntity(&trace, ent, move, true))
2643 return; // teleported
2644 if (ent->priv.server->free)
2646 if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
2648 // try to unstick the entity
2649 SV_UnstickEntity(ent);
2650 if(!SV_PushEntity(&trace, ent, move, true))
2651 return; // teleported
2652 if (ent->priv.server->free)
2655 if (trace.fraction == 1)
2657 movetime *= 1 - min(1, trace.fraction);
2658 switch((int)PRVM_serveredictfloat(ent, movetype))
2660 case MOVETYPE_BOUNCEMISSILE:
2661 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2663 bouncefactor = 1.0f;
2665 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2666 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2667 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2670 case MOVETYPE_BOUNCE:
2671 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2673 bouncefactor = 0.5f;
2675 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2677 bouncestop = 60.0f / 800.0f;
2679 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2680 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2683 // LordHavoc: fixed grenades not bouncing when fired down a slope
2684 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2685 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2687 d = PRVM_serveredictvector(ent, velocity)[2];
2688 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2690 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2691 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2692 VectorClear(PRVM_serveredictvector(ent, velocity));
2693 VectorClear(PRVM_serveredictvector(ent, avelocity));
2698 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2699 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2704 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2705 if (trace.plane.normal[2] > 0.7)
2707 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2708 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2709 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2710 ent->priv.server->suspendedinairflag = true;
2711 VectorClear (PRVM_serveredictvector(ent, velocity));
2712 VectorClear (PRVM_serveredictvector(ent, avelocity));
2717 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2718 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2725 // check for in water
2726 SV_CheckWaterTransition (ent);
2730 ===============================================================================
2734 ===============================================================================
2741 Monsters freefall when they don't have a ground entity, otherwise
2742 all movement is done with discrete steps.
2744 This is also used for objects that have become still on the ground, but
2745 will fall if the floor is pulled out from under them.
2748 static void SV_Physics_Step (prvm_edict_t *ent)
2750 prvm_prog_t *prog = SVVM_prog;
2751 int flags = (int)PRVM_serveredictfloat(ent, flags);
2754 // Backup Velocity in the event that movetypesteplandevent is called,
2755 // to provide a parameter with the entity's velocity at impact.
2756 vec3_t backupVelocity;
2757 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2758 // don't fall at all if fly/swim
2759 if (!(flags & (FL_FLY | FL_SWIM)))
2761 if (flags & FL_ONGROUND)
2763 // freefall if onground and moving upward
2764 // freefall if not standing on a world surface (it may be a lift or trap door)
2765 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2767 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2768 SV_CheckVelocity(ent);
2769 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2771 SV_LinkEdict_TouchAreaGrid(ent);
2772 ent->priv.server->waterposition_forceupdate = true;
2777 // freefall if not onground
2778 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2780 SV_CheckVelocity(ent);
2781 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0, 0, 0);
2783 SV_LinkEdict_TouchAreaGrid(ent);
2786 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2788 // DRESK - Check for Entity Land Event Function
2789 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2790 { // Valid Function; Execute
2791 // Prepare Parameters
2792 // Assign Velocity at Impact
2793 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2794 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2795 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2797 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2799 PRVM_serverglobalfloat(time) = sv.time;
2800 // Execute VM Function
2801 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2804 // Check for Engine Landing Sound
2805 if(sv_sound_land.string)
2806 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2808 ent->priv.server->waterposition_forceupdate = true;
2813 //============================================================================
2815 static void SV_Physics_Entity (prvm_edict_t *ent)
2817 prvm_prog_t *prog = SVVM_prog;
2818 // don't run think/move on newly spawned projectiles as it messes up
2819 // movement interpolation and rocket trails, and is inconsistent with
2820 // respect to entities spawned in the same frame
2821 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2822 // but if it spawns a lower numbered ent, it doesn't - this never moves
2823 // ents in the first frame regardless)
2824 qboolean runmove = ent->priv.server->move;
2825 ent->priv.server->move = true;
2826 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2828 switch ((int) PRVM_serveredictfloat(ent, movetype))
2831 case MOVETYPE_FAKEPUSH:
2832 SV_Physics_Pusher (ent);
2835 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2836 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2839 case MOVETYPE_FOLLOW:
2840 if(SV_RunThink(ent))
2841 SV_Physics_Follow (ent);
2843 case MOVETYPE_NOCLIP:
2844 if (SV_RunThink(ent))
2847 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2848 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2853 SV_Physics_Step (ent);
2855 if (SV_RunThink(ent))
2856 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2858 ent->priv.server->waterposition_forceupdate = false;
2859 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2860 SV_CheckWaterTransition(ent);
2864 if (SV_RunThink (ent))
2868 case MOVETYPE_BOUNCE:
2869 case MOVETYPE_BOUNCEMISSILE:
2870 case MOVETYPE_FLYMISSILE:
2872 case MOVETYPE_FLY_WORLDONLY:
2874 if (SV_RunThink (ent))
2875 SV_Physics_Toss (ent);
2877 case MOVETYPE_PHYSICS:
2878 if (SV_RunThink(ent))
2881 SV_LinkEdict_TouchAreaGrid(ent);
2885 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2887 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2892 static void SV_Physics_ClientEntity_NoThink (prvm_edict_t *ent)
2894 prvm_prog_t *prog = SVVM_prog;
2896 // don't run think at all, that is done during server frames
2897 // instead, call the movetypes directly so they match client input
2899 // This probably only makes sense for CSQC-networked (SendEntity field set) player entities
2900 switch ((int) PRVM_serveredictfloat(ent, movetype))
2903 case MOVETYPE_FAKEPUSH:
2904 // push physics relies heavily on think times and calls, and so cannot be predicted currently
2905 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2909 case MOVETYPE_FOLLOW:
2910 SV_Physics_Follow (ent);
2912 case MOVETYPE_NOCLIP:
2913 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2914 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2917 SV_Physics_Step (ent);
2923 case MOVETYPE_BOUNCE:
2924 case MOVETYPE_BOUNCEMISSILE:
2925 case MOVETYPE_FLYMISSILE:
2926 SV_Physics_Toss (ent);
2929 case MOVETYPE_FLY_WORLDONLY:
2932 case MOVETYPE_PHYSICS:
2935 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
2937 Con_Printf ("SV_Physics_ClientEntity_NoThink: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2942 void SV_Physics_ClientMove(void)
2944 prvm_prog_t *prog = SVVM_prog;
2946 ent = host_client->edict;
2948 // call player physics, this needs the proper frametime
2949 PRVM_serverglobalfloat(frametime) = sv.frametime;
2952 // call standard client pre-think, with frametime = 0
2953 PRVM_serverglobalfloat(time) = sv.time;
2954 PRVM_serverglobalfloat(frametime) = 0;
2955 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2956 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2957 PRVM_serverglobalfloat(frametime) = sv.frametime;
2959 // make sure the velocity is sane (not a NaN)
2960 SV_CheckVelocity(ent);
2962 // perform movetype behaviour
2963 // note: will always be MOVETYPE_WALK if disableclientprediction = 0
2964 SV_Physics_ClientEntity_NoThink (ent);
2966 // call standard player post-think, with frametime = 0
2967 PRVM_serverglobalfloat(time) = sv.time;
2968 PRVM_serverglobalfloat(frametime) = 0;
2969 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2970 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2971 PRVM_serverglobalfloat(frametime) = sv.frametime;
2973 if(PRVM_serveredictfloat(ent, fixangle))
2975 // angle fixing was requested by physics code...
2976 // so store the current angles for later use
2977 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2978 host_client->fixangle_angles_set = TRUE;
2980 // and clear fixangle for the next frame
2981 PRVM_serveredictfloat(ent, fixangle) = 0;
2985 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2987 prvm_prog_t *prog = SVVM_prog;
2988 // don't do physics on disconnected clients, FrikBot relies on this
2989 if (!host_client->begun)
2992 // make sure the velocity is sane (not a NaN)
2993 SV_CheckVelocity(ent);
2995 // don't run physics here if running asynchronously
2996 if (host_client->clmovement_inputtimeout <= 0)
2999 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
3002 // make sure the velocity is still sane (not a NaN)
3003 SV_CheckVelocity(ent);
3005 // call standard client pre-think
3006 PRVM_serverglobalfloat(time) = sv.time;
3007 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3008 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3010 // make sure the velocity is still sane (not a NaN)
3011 SV_CheckVelocity(ent);
3014 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3016 prvm_prog_t *prog = SVVM_prog;
3017 // don't do physics on disconnected clients, FrikBot relies on this
3018 if (!host_client->begun)
3021 // make sure the velocity is sane (not a NaN)
3022 SV_CheckVelocity(ent);
3024 // call standard player post-think
3025 PRVM_serverglobalfloat(time) = sv.time;
3026 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3027 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3029 // make sure the velocity is still sane (not a NaN)
3030 SV_CheckVelocity(ent);
3032 if(PRVM_serveredictfloat(ent, fixangle))
3034 // angle fixing was requested by physics code...
3035 // so store the current angles for later use
3036 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3037 host_client->fixangle_angles_set = TRUE;
3039 // and clear fixangle for the next frame
3040 PRVM_serveredictfloat(ent, fixangle) = 0;
3043 // decrement the countdown variable used to decide when to go back to
3044 // synchronous physics
3045 if (host_client->clmovement_inputtimeout > sv.frametime)
3046 host_client->clmovement_inputtimeout -= sv.frametime;
3048 host_client->clmovement_inputtimeout = 0;
3051 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3053 prvm_prog_t *prog = SVVM_prog;
3054 // don't do physics on disconnected clients, FrikBot relies on this
3055 if (!host_client->begun)
3057 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3061 // make sure the velocity is sane (not a NaN)
3062 SV_CheckVelocity(ent);
3064 switch ((int) PRVM_serveredictfloat(ent, movetype))
3067 case MOVETYPE_FAKEPUSH:
3068 SV_Physics_Pusher (ent);
3071 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3072 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3075 case MOVETYPE_FOLLOW:
3077 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3078 SV_Physics_Follow (ent);
3080 case MOVETYPE_NOCLIP:
3082 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3085 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3086 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3090 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3091 SV_Physics_Step (ent);
3092 if (SV_RunThink(ent))
3093 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
3095 ent->priv.server->waterposition_forceupdate = false;
3096 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
3097 SV_CheckWaterTransition(ent);
3102 // don't run physics here if running asynchronously
3103 if (host_client->clmovement_inputtimeout <= 0)
3107 case MOVETYPE_BOUNCE:
3108 case MOVETYPE_BOUNCEMISSILE:
3109 case MOVETYPE_FLYMISSILE:
3112 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3113 SV_Physics_Toss (ent);
3116 case MOVETYPE_FLY_WORLDONLY:
3118 if (host_client->clmovement_inputtimeout <= 0) // don't run physics here if running asynchronously
3121 case MOVETYPE_PHYSICS:
3125 if((int) PRVM_serveredictfloat(ent, movetype) >= MOVETYPE_USER_FIRST && (int) PRVM_serveredictfloat(ent, movetype) <= MOVETYPE_USER_LAST)
3127 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3131 SV_CheckVelocity (ent);
3134 SV_LinkEdict_TouchAreaGrid(ent);
3136 SV_CheckVelocity (ent);
3145 void SV_Physics (void)
3147 prvm_prog_t *prog = SVVM_prog;
3151 // let the progs know that a new frame has started
3152 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3153 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3154 PRVM_serverglobalfloat(time) = sv.time;
3155 PRVM_serverglobalfloat(frametime) = sv.frametime;
3156 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3158 // run physics engine
3159 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3162 // treat each object in turn
3165 // if force_retouch, relink all the entities
3166 if (PRVM_serverglobalfloat(force_retouch) > 0)
3167 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3168 if (!ent->priv.server->free)
3169 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3171 if (sv_gameplayfix_consistentplayerprethink.integer)
3173 // run physics on the client entities in 3 stages
3174 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3175 if (!ent->priv.server->free)
3176 SV_Physics_ClientEntity_PreThink(ent);
3178 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3179 if (!ent->priv.server->free)
3180 SV_Physics_ClientEntity(ent);
3182 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3183 if (!ent->priv.server->free)
3184 SV_Physics_ClientEntity_PostThink(ent);
3188 // run physics on the client entities
3189 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3191 if (!ent->priv.server->free)
3193 SV_Physics_ClientEntity_PreThink(ent);
3194 SV_Physics_ClientEntity(ent);
3195 SV_Physics_ClientEntity_PostThink(ent);
3200 // run physics on all the non-client entities
3201 if (!sv_freezenonclients.integer)
3203 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3204 if (!ent->priv.server->free)
3205 SV_Physics_Entity(ent);
3206 // make a second pass to see if any ents spawned this frame and make
3207 // sure they run their move/think
3208 if (sv_gameplayfix_delayprojectiles.integer < 0)
3209 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3210 if (!ent->priv.server->move && !ent->priv.server->free)
3211 SV_Physics_Entity(ent);
3214 if (PRVM_serverglobalfloat(force_retouch) > 0)
3215 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3217 // LordHavoc: endframe support
3218 if (PRVM_serverfunction(EndFrame))
3220 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3221 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3222 PRVM_serverglobalfloat(time) = sv.time;
3223 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3226 // decrement prog->num_edicts if the highest number entities died
3227 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3229 if (!sv_freezenonclients.integer)
3230 sv.time += sv.frametime;