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)
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);
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);
141 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);
236 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
238 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
250 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
251 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
253 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
256 prvm_prog_t *prog = SVVM_prog;
257 int i, bodysupercontents;
260 prvm_edict_t *traceowner, *touch;
262 // temporary storage because prvm_vec_t may differ from vec_t
263 vec3_t touchmins, touchmaxs;
264 // bounding box of entire move area
265 vec3_t clipboxmins, clipboxmaxs;
266 // size when clipping against monsters
267 vec3_t clipmins2, clipmaxs2;
268 // start and end origin of move
269 vec3_t clipstart, clipend;
272 // matrices to transform into/out of other entity's space
273 matrix4x4_t matrix, imatrix;
274 // model of other entity
276 // list of entities to test for collisions
278 static prvm_edict_t *touchedicts[MAX_EDICTS];
279 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
283 if (VectorCompare(start, pEnd))
284 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
286 if(collision_endposnudge.value > 0)
288 // TRICK: make the trace 1 qu longer!
289 VectorSubtract(pEnd, start, end);
290 len = VectorNormalizeLength(end);
291 VectorMA(pEnd, collision_endposnudge.value, end, end);
294 VectorCopy(pEnd, end);
296 if (VectorCompare(start, end))
297 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
300 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
302 VectorCopy(start, clipstart);
303 VectorCopy(end, clipend);
304 VectorClear(clipmins2);
305 VectorClear(clipmaxs2);
306 #if COLLISIONPARANOID >= 3
307 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
311 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, false);
312 cliptrace.bmodelstartsolid = cliptrace.startsolid;
313 if (cliptrace.startsolid || cliptrace.fraction < 1)
314 cliptrace.ent = prog->edicts;
315 if (type == MOVE_WORLDONLY)
318 if (type == MOVE_MISSILE)
320 // LordHavoc: modified this, was = -15, now -= 15
321 for (i = 0;i < 3;i++)
328 // create the bounding box of the entire move
329 for (i = 0;i < 3;i++)
331 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
332 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
335 // debug override to test against everything
336 if (sv_debugmove.integer)
338 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
339 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
342 // if the passedict is world, make it NULL (to avoid two checks each time)
343 if (passedict == prog->edicts)
345 // precalculate prog value for passedict for comparisons
346 passedictprog = PRVM_EDICT_TO_PROG(passedict);
347 // precalculate passedict's owner edict pointer for comparisons
348 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
351 // because this uses World_EntitiestoBox, we know all entity boxes overlap
352 // the clip region, so we can skip culling checks in the loop below
353 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
354 if (numtouchedicts > MAX_EDICTS)
356 // this never happens
357 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
358 numtouchedicts = MAX_EDICTS;
360 for (i = 0;i < numtouchedicts;i++)
362 touch = touchedicts[i];
364 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
366 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
371 // don't clip against self
372 if (passedict == touch)
374 // don't clip owned entities against owner
375 if (traceowner == touch)
377 // don't clip owner against owned entities
378 if (passedictprog == PRVM_serveredictedict(touch, owner))
380 // don't clip points against points (they can't collide)
381 if (VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
385 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
387 // might interact, so do an exact clip
389 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
391 model = SV_GetModelFromEdict(touch);
392 pitchsign = SV_GetPitchSign(prog, touch);
395 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);
397 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
398 Matrix4x4_Invert_Simple(&imatrix, &matrix);
399 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
400 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
401 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
402 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
403 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
404 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
405 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
407 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, false);
409 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
413 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
414 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
415 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
425 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
426 #if COLLISIONPARANOID >= 1
427 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
429 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
432 #if COLLISIONPARANOID >= 1
433 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)
435 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)
439 prvm_prog_t *prog = SVVM_prog;
440 vec3_t hullmins, hullmaxs;
441 int i, bodysupercontents;
445 prvm_edict_t *traceowner, *touch;
447 // temporary storage because prvm_vec_t may differ from vec_t
448 vec3_t touchmins, touchmaxs;
449 // bounding box of entire move area
450 vec3_t clipboxmins, clipboxmaxs;
451 // size of the moving object
452 vec3_t clipmins, clipmaxs;
453 // size when clipping against monsters
454 vec3_t clipmins2, clipmaxs2;
455 // start and end origin of move
456 vec3_t clipstart, clipend;
459 // matrices to transform into/out of other entity's space
460 matrix4x4_t matrix, imatrix;
461 // model of other entity
463 // list of entities to test for collisions
465 static prvm_edict_t *touchedicts[MAX_EDICTS];
466 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
470 if (VectorCompare(mins, maxs))
472 vec3_t shiftstart, shiftend;
473 VectorAdd(start, mins, shiftstart);
474 VectorAdd(pEnd, mins, shiftend);
475 if (VectorCompare(start, pEnd))
476 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
478 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
479 VectorSubtract(trace.endpos, mins, trace.endpos);
483 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
485 // TRICK: make the trace 1 qu longer!
486 VectorSubtract(pEnd, start, end);
487 len = VectorNormalizeLength(end);
488 VectorMA(pEnd, collision_endposnudge.value, end, end);
491 VectorCopy(pEnd, end);
493 if (VectorCompare(mins, maxs))
495 vec3_t shiftstart, shiftend;
496 VectorAdd(start, mins, shiftstart);
497 VectorAdd(end, mins, shiftend);
498 if (VectorCompare(start, end))
499 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
501 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
502 VectorSubtract(trace.endpos, mins, trace.endpos);
507 VectorCopy(start, clipstart);
508 VectorCopy(end, clipend);
509 VectorCopy(mins, clipmins);
510 VectorCopy(maxs, clipmaxs);
511 VectorCopy(mins, clipmins2);
512 VectorCopy(maxs, clipmaxs2);
513 #if COLLISIONPARANOID >= 3
514 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
518 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
519 cliptrace.bmodelstartsolid = cliptrace.startsolid;
520 if (cliptrace.startsolid || cliptrace.fraction < 1)
521 cliptrace.ent = prog->edicts;
522 if (type == MOVE_WORLDONLY)
525 if (type == MOVE_MISSILE)
527 // LordHavoc: modified this, was = -15, now -= 15
528 for (i = 0;i < 3;i++)
535 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
536 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
537 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
540 VectorCopy(clipmins, hullmins);
541 VectorCopy(clipmaxs, hullmaxs);
544 // create the bounding box of the entire move
545 for (i = 0;i < 3;i++)
547 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
548 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
551 // debug override to test against everything
552 if (sv_debugmove.integer)
554 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
555 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
558 // if the passedict is world, make it NULL (to avoid two checks each time)
559 if (passedict == prog->edicts)
561 // precalculate prog value for passedict for comparisons
562 passedictprog = PRVM_EDICT_TO_PROG(passedict);
563 // figure out whether this is a point trace for comparisons
564 pointtrace = VectorCompare(clipmins, clipmaxs);
565 // precalculate passedict's owner edict pointer for comparisons
566 traceowner = passedict ? PRVM_PROG_TO_EDICT(PRVM_serveredictedict(passedict, owner)) : 0;
569 // because this uses World_EntitiestoBox, we know all entity boxes overlap
570 // the clip region, so we can skip culling checks in the loop below
571 numtouchedicts = SV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
572 if (numtouchedicts > MAX_EDICTS)
574 // this never happens
575 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
576 numtouchedicts = MAX_EDICTS;
578 for (i = 0;i < numtouchedicts;i++)
580 touch = touchedicts[i];
582 if (PRVM_serveredictfloat(touch, solid) < SOLID_BBOX)
584 if (type == MOVE_NOMONSTERS && PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
589 // don't clip against self
590 if (passedict == touch)
592 // don't clip owned entities against owner
593 if (traceowner == touch)
595 // don't clip owner against owned entities
596 if (passedictprog == PRVM_serveredictedict(touch, owner))
598 // don't clip points against points (they can't collide)
599 if (pointtrace && VectorCompare(PRVM_serveredictvector(touch, mins), PRVM_serveredictvector(touch, maxs)) && (type != MOVE_MISSILE || !((int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)))
603 bodysupercontents = PRVM_serveredictfloat(touch, solid) == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
605 // might interact, so do an exact clip
607 if ((int) PRVM_serveredictfloat(touch, solid) == SOLID_BSP || type == MOVE_HITMODEL)
609 model = SV_GetModelFromEdict(touch);
610 pitchsign = SV_GetPitchSign(prog, touch);
613 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);
615 Matrix4x4_CreateTranslate(&matrix, PRVM_serveredictvector(touch, origin)[0], PRVM_serveredictvector(touch, origin)[1], PRVM_serveredictvector(touch, origin)[2]);
616 Matrix4x4_Invert_Simple(&imatrix, &matrix);
617 VM_GenerateFrameGroupBlend(prog, touch->priv.server->framegroupblend, touch);
618 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model, sv.time);
619 VM_UpdateEdictSkeleton(prog, touch, model, touch->priv.server->frameblend);
620 VectorCopy(PRVM_serveredictvector(touch, mins), touchmins);
621 VectorCopy(PRVM_serveredictvector(touch, maxs), touchmaxs);
622 if (type == MOVE_MISSILE && (int)PRVM_serveredictfloat(touch, flags) & FL_MONSTER)
623 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
625 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touchmins, touchmaxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
627 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, PRVM_serveredictfloat(touch, solid) == SOLID_BSP);
631 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
632 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
633 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
638 #if COLLISIONPARANOID >= 1
639 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)
644 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
647 VectorCopy(trace.endpos, temp);
648 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
649 #if COLLISIONPARANOID < 3
650 if (trace.startsolid || endstuck)
652 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" : "");
658 int SV_PointSuperContents(const vec3_t point)
660 prvm_prog_t *prog = SVVM_prog;
661 int supercontents = 0;
665 // matrices to transform into/out of other entity's space
666 matrix4x4_t matrix, imatrix;
667 // model of other entity
670 // list of entities to test for collisions
672 static prvm_edict_t *touchedicts[MAX_EDICTS];
674 // get world supercontents at this point
675 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
676 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
678 // if sv_gameplayfix_swiminbmodels is off we're done
679 if (!sv_gameplayfix_swiminbmodels.integer)
680 return supercontents;
682 // get list of entities at this point
683 numtouchedicts = SV_EntitiesInBox(point, point, MAX_EDICTS, touchedicts);
684 if (numtouchedicts > MAX_EDICTS)
686 // this never happens
687 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
688 numtouchedicts = MAX_EDICTS;
690 for (i = 0;i < numtouchedicts;i++)
692 touch = touchedicts[i];
694 // we only care about SOLID_BSP for pointcontents
695 if (PRVM_serveredictfloat(touch, solid) != SOLID_BSP)
698 // might interact, so do an exact clip
699 model = SV_GetModelFromEdict(touch);
700 if (!model || !model->PointSuperContents)
702 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);
703 Matrix4x4_Invert_Simple(&imatrix, &matrix);
704 Matrix4x4_Transform(&imatrix, point, transformed);
705 frame = (int)PRVM_serveredictfloat(touch, frame);
706 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
709 return supercontents;
713 ===============================================================================
715 Linking entities into the world culling system
717 ===============================================================================
720 int SV_EntitiesInBox(const vec3_t mins, const vec3_t maxs, int maxedicts, prvm_edict_t **resultedicts)
722 prvm_prog_t *prog = SVVM_prog;
723 vec3_t paddedmins, paddedmaxs;
724 if (maxedicts < 1 || resultedicts == NULL)
726 VectorSet(paddedmins, mins[0] - 10, mins[1] - 10, mins[2] - 1);
727 VectorSet(paddedmaxs, maxs[0] + 10, maxs[1] + 10, maxs[2] + 1);
728 if (sv_areadebug.integer)
730 int numresultedicts = 0;
733 for (edictindex = 1;edictindex < prog->num_edicts;edictindex++)
735 ed = PRVM_EDICT_NUM(edictindex);
736 if (!ed->priv.required->free && BoxesOverlap(PRVM_serveredictvector(ed, absmin), PRVM_serveredictvector(ed, absmax), paddedmins, paddedmaxs))
738 resultedicts[numresultedicts++] = ed;
739 if (numresultedicts == maxedicts)
743 return numresultedicts;
746 return World_EntitiesInBox(&sv.world, paddedmins, paddedmaxs, maxedicts, resultedicts);
749 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
751 prvm_prog_t *prog = SVVM_prog;
752 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(touch);
753 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(ent);
754 PRVM_serverglobalfloat(time) = sv.time;
755 PRVM_serverglobalfloat(trace_allsolid) = false;
756 PRVM_serverglobalfloat(trace_startsolid) = false;
757 PRVM_serverglobalfloat(trace_fraction) = 1;
758 PRVM_serverglobalfloat(trace_inwater) = false;
759 PRVM_serverglobalfloat(trace_inopen) = true;
760 VectorCopy (PRVM_serveredictvector(touch, origin), PRVM_serverglobalvector(trace_endpos));
761 VectorSet (PRVM_serverglobalvector(trace_plane_normal), 0, 0, 1);
762 PRVM_serverglobalfloat(trace_plane_dist) = 0;
763 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(ent);
764 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
765 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
766 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
767 PRVM_serverglobalstring(trace_dphittexturename) = 0;
768 prog->ExecuteProgram(prog, PRVM_serveredictfunction(touch, touch), "QC function self.touch is missing");
771 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
773 prvm_prog_t *prog = SVVM_prog;
774 int i, numtouchedicts, old_self, old_other;
776 static prvm_edict_t *touchedicts[MAX_EDICTS];
778 if (ent == prog->edicts)
779 return; // don't add the world
781 if (ent->priv.server->free)
784 if (PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
787 // build a list of edicts to touch, because the link loop can be corrupted
788 // by IncreaseEdicts called during touch functions
789 numtouchedicts = SV_EntitiesInBox(ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
790 if (numtouchedicts > MAX_EDICTS)
792 // this never happens
793 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
794 numtouchedicts = MAX_EDICTS;
797 old_self = PRVM_serverglobaledict(self);
798 old_other = PRVM_serverglobaledict(other);
799 for (i = 0;i < numtouchedicts;i++)
801 touch = touchedicts[i];
802 if (touch != ent && (int)PRVM_serveredictfloat(touch, solid) == SOLID_TRIGGER && PRVM_serveredictfunction(touch, touch))
804 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
807 PRVM_serverglobaledict(self) = old_self;
808 PRVM_serverglobaledict(other) = old_other;
811 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
815 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
817 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
818 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
819 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
820 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];
821 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];
822 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
823 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];
824 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];
825 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
826 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];
827 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];
828 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
829 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];
830 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];
831 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
832 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];
833 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];
834 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
835 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];
836 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];
837 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
838 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];
839 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];
848 void SV_LinkEdict (prvm_edict_t *ent)
850 prvm_prog_t *prog = SVVM_prog;
852 vec3_t mins, maxs, entmins, entmaxs, entangles;
855 if (ent == prog->edicts)
856 return; // don't add the world
858 if (ent->priv.server->free)
861 modelindex = (int)PRVM_serveredictfloat(ent, modelindex);
862 if (modelindex < 0 || modelindex >= MAX_MODELS)
864 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
867 model = SV_GetModelByIndex(modelindex);
869 VM_GenerateFrameGroupBlend(prog, ent->priv.server->framegroupblend, ent);
870 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model, sv.time);
871 VM_UpdateEdictSkeleton(prog, ent, model, ent->priv.server->frameblend);
875 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_PHYSICS)
877 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
878 // TODO special handling for spheres?
879 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
880 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
881 VectorCopy(PRVM_serveredictvector(ent, angles), entangles);
882 RotateBBox(entmins, entmaxs, entangles, mins, maxs);
883 VectorAdd(PRVM_serveredictvector(ent, origin), mins, mins);
884 VectorAdd(PRVM_serveredictvector(ent, origin), maxs, maxs);
886 else if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
890 if (!model->TraceBox)
891 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
893 if (PRVM_serveredictvector(ent, angles)[0] || PRVM_serveredictvector(ent, angles)[2] || PRVM_serveredictvector(ent, avelocity)[0] || PRVM_serveredictvector(ent, avelocity)[2])
895 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmins, mins);
896 VectorAdd(PRVM_serveredictvector(ent, origin), model->rotatedmaxs, maxs);
898 else if (PRVM_serveredictvector(ent, angles)[1] || PRVM_serveredictvector(ent, avelocity)[1])
900 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmins, mins);
901 VectorAdd(PRVM_serveredictvector(ent, origin), model->yawmaxs, maxs);
905 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmins, mins);
906 VectorAdd(PRVM_serveredictvector(ent, origin), model->normalmaxs, maxs);
911 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
912 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
913 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
918 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), mins);
919 VectorAdd(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, maxs), maxs);
923 // to make items easier to pick up and allow them to be grabbed off
924 // of shelves, the abs sizes are expanded
926 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ITEM)
937 // because movement is clipped an epsilon away from an actual edge,
938 // we must fully check even when bounding boxes don't quite touch
947 VectorCopy(mins, PRVM_serveredictvector(ent, absmin));
948 VectorCopy(maxs, PRVM_serveredictvector(ent, absmax));
950 World_LinkEdict(&sv.world, ent, mins, maxs);
954 ===============================================================================
958 ===============================================================================
963 SV_TestEntityPosition
965 returns true if the entity is in solid currently
968 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
970 prvm_prog_t *prog = SVVM_prog;
972 vec3_t org, entorigin, entmins, entmaxs;
974 contents = SV_GenericHitSuperContentsMask(ent);
975 VectorAdd(PRVM_serveredictvector(ent, origin), offset, org);
976 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
977 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
978 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
979 trace = SV_TraceBox(org, entmins, entmaxs, entorigin, ((PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), ent, contents);
980 if (trace.startsupercontents & contents)
984 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs)))
986 // q1bsp/hlbsp use hulls and if the entity does not exactly match
987 // a hull size it is incorrectly tested, so this code tries to
988 // 'fix' it slightly...
989 // FIXME: this breaks entities larger than the hull size
992 VectorAdd(org, entmins, m1);
993 VectorAdd(org, entmaxs, m2);
994 VectorSubtract(m2, m1, s);
995 #define EPSILON (1.0f / 32.0f)
996 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
997 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
998 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
999 for (i = 0;i < 8;i++)
1001 v[0] = (i & 1) ? m2[0] : m1[0];
1002 v[1] = (i & 2) ? m2[1] : m1[1];
1003 v[2] = (i & 4) ? m2[2] : m1[2];
1004 if (SV_PointSuperContents(v) & contents)
1009 // if the trace found a better position for the entity, move it there
1010 if (VectorDistance2(trace.endpos, PRVM_serveredictvector(ent, origin)) >= 0.0001)
1013 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
1014 VectorCopy(trace.endpos, PRVM_serveredictvector(ent, origin));
1016 // verify if the endpos is REALLY outside solid
1017 VectorCopy(trace.endpos, org);
1018 trace = SV_TraceBox(org, entmins, entmaxs, org, MOVE_NOMONSTERS, ent, contents);
1019 if(trace.startsolid)
1020 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
1022 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1028 // DRESK - Support for Entity Contents Transition Event
1031 SV_CheckContentsTransition
1033 returns true if entity had a valid contentstransition function call
1036 static int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
1038 prvm_prog_t *prog = SVVM_prog;
1039 int bValidFunctionCall;
1041 // Default Valid Function Call to False
1042 bValidFunctionCall = false;
1044 if(PRVM_serveredictfloat(ent, watertype) != nContents)
1045 { // Changed Contents
1046 // Acquire Contents Transition Function from QC
1047 if(PRVM_serveredictfunction(ent, contentstransition))
1048 { // Valid Function; Execute
1049 // Assign Valid Function
1050 bValidFunctionCall = true;
1051 // Prepare Parameters (Original Contents, New Contents)
1052 // Original Contents
1053 PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
1055 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1057 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1059 PRVM_serverglobalfloat(time) = sv.time;
1060 // Execute VM Function
1061 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1065 // Return if Function Call was Valid
1066 return bValidFunctionCall;
1075 void SV_CheckVelocity (prvm_edict_t *ent)
1077 prvm_prog_t *prog = SVVM_prog;
1084 for (i=0 ; i<3 ; i++)
1086 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, velocity)[i]))
1088 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1089 PRVM_serveredictvector(ent, velocity)[i] = 0;
1091 if (PRVM_IS_NAN(PRVM_serveredictvector(ent, origin)[i]))
1093 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
1094 PRVM_serveredictvector(ent, origin)[i] = 0;
1098 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1099 // player_run/player_stand1 does not horribly malfunction if the
1100 // velocity becomes a denormalized float
1101 if (VectorLength2(PRVM_serveredictvector(ent, velocity)) < 0.0001)
1102 VectorClear(PRVM_serveredictvector(ent, velocity));
1104 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1105 wishspeed = DotProduct(PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, velocity));
1106 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1108 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1109 PRVM_serveredictvector(ent, velocity)[0] *= wishspeed;
1110 PRVM_serveredictvector(ent, velocity)[1] *= wishspeed;
1111 PRVM_serveredictvector(ent, velocity)[2] *= wishspeed;
1119 Runs thinking code if time. There is some play in the exact time the think
1120 function will be called, because it is called before any movement is done
1121 in a frame. Not used for pushmove objects, because they must be exact.
1122 Returns false if the entity removed itself.
1125 static qboolean SV_RunThink (prvm_edict_t *ent)
1127 prvm_prog_t *prog = SVVM_prog;
1130 // don't let things stay in the past.
1131 // it is possible to start that way by a trigger with a local time.
1132 if (PRVM_serveredictfloat(ent, nextthink) <= 0 || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime)
1135 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1137 PRVM_serverglobalfloat(time) = max(sv.time, PRVM_serveredictfloat(ent, nextthink));
1138 PRVM_serveredictfloat(ent, nextthink) = 0;
1139 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1140 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
1141 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
1142 // mods often set nextthink to time to cause a think every frame,
1143 // we don't want to loop in that case, so exit if the new nextthink is
1144 // <= the time the qc was told, also exit if it is past the end of the
1146 if (PRVM_serveredictfloat(ent, nextthink) <= PRVM_serverglobalfloat(time) || PRVM_serveredictfloat(ent, nextthink) > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1149 return !ent->priv.server->free;
1156 Two entities have touched, so run their touch functions
1159 static void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1161 prvm_prog_t *prog = SVVM_prog;
1162 int restorevm_tempstringsbuf_cursize;
1163 int old_self, old_other;
1164 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1166 old_self = PRVM_serverglobaledict(self);
1167 old_other = PRVM_serverglobaledict(other);
1168 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1170 VM_SetTraceGlobals(prog, trace);
1172 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e1, touch) && PRVM_serveredictfloat(e1, solid) != SOLID_NOT)
1174 PRVM_serverglobalfloat(time) = sv.time;
1175 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e1);
1176 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e2);
1177 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e1, touch), "QC function self.touch is missing");
1180 if (!e1->priv.server->free && !e2->priv.server->free && PRVM_serveredictfunction(e2, touch) && PRVM_serveredictfloat(e2, solid) != SOLID_NOT)
1182 PRVM_serverglobalfloat(time) = sv.time;
1183 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(e2);
1184 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(e1);
1185 VectorCopy(PRVM_serveredictvector(e2, origin), PRVM_serverglobalvector(trace_endpos));
1186 VectorNegate(trace->plane.normal, PRVM_serverglobalvector(trace_plane_normal));
1187 PRVM_serverglobalfloat(trace_plane_dist) = -trace->plane.dist;
1188 PRVM_serverglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(e1);
1189 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1190 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1191 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1192 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1193 prog->ExecuteProgram(prog, PRVM_serveredictfunction(e2, touch), "QC function self.touch is missing");
1196 PRVM_serverglobaledict(self) = old_self;
1197 PRVM_serverglobaledict(other) = old_other;
1198 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1206 Slide off of the impacting object
1207 returns the blocked flags (1 = floor, 2 = step / wall)
1210 #define STOP_EPSILON 0.1
1211 static void ClipVelocity (prvm_vec3_t in, vec3_t normal, prvm_vec3_t out, prvm_vec_t overbounce)
1216 backoff = -DotProduct (in, normal) * overbounce;
1217 VectorMA(in, backoff, normal, out);
1219 for (i = 0;i < 3;i++)
1220 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1229 The basic solid body movement clip that slides along multiple planes
1230 Returns the clipflags if the velocity was modified (hit something solid)
1234 8 = teleported by touch method
1235 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1238 static float SV_Gravity (prvm_edict_t *ent);
1239 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1240 #define MAX_CLIP_PLANES 5
1241 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1243 prvm_prog_t *prog = SVVM_prog;
1244 int blocked, bumpcount;
1245 int i, j, numplanes;
1246 float d, time_left, gravity;
1247 vec3_t dir, push, planes[MAX_CLIP_PLANES];
1248 prvm_vec3_t primal_velocity, original_velocity, new_velocity;
1259 gravity = SV_Gravity(ent);
1261 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1263 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1264 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1266 PRVM_serveredictvector(ent, velocity)[2] -= gravity;
1271 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1272 VectorCopy(PRVM_serveredictvector(ent, velocity), primal_velocity);
1275 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1277 if (!PRVM_serveredictvector(ent, velocity)[0] && !PRVM_serveredictvector(ent, velocity)[1] && !PRVM_serveredictvector(ent, velocity)[2])
1280 VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
1281 if(!SV_PushEntity(&trace, ent, push, false, false))
1283 // we got teleported by a touch function
1284 // let's abort the move
1289 if (trace.fraction == 1)
1291 if (trace.plane.normal[2])
1293 if (trace.plane.normal[2] > 0.7)
1300 Con_Printf ("SV_FlyMove: !trace.ent");
1301 trace.ent = prog->edicts;
1304 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
1305 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
1308 else if (stepheight)
1310 // step - handle it immediately
1316 //Con_Printf("step %f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1317 VectorSet(steppush, 0, 0, stepheight);
1318 VectorCopy(PRVM_serveredictvector(ent, origin), org);
1319 if(!SV_PushEntity(&steptrace, ent, steppush, false, false))
1324 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1325 if(!SV_PushEntity(&steptrace2, ent, push, false, false))
1330 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1331 VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
1332 if(!SV_PushEntity(&steptrace3, ent, steppush, false, false))
1337 //Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
1338 // accept the new position if it made some progress...
1339 if (fabs(PRVM_serveredictvector(ent, origin)[0] - org[0]) >= 0.03125 || fabs(PRVM_serveredictvector(ent, origin)[1] - org[1]) >= 0.03125)
1341 //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]);
1343 VectorCopy(PRVM_serveredictvector(ent, origin), trace.endpos);
1344 time_left *= 1 - trace.fraction;
1350 //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]);
1351 VectorCopy(org, PRVM_serveredictvector(ent, origin));
1356 // step - return it to caller
1358 // save the trace for player extrafriction
1360 VectorCopy(trace.plane.normal, stepnormal);
1362 if (trace.fraction >= 0.001)
1364 // actually covered some distance
1365 VectorCopy(PRVM_serveredictvector(ent, velocity), original_velocity);
1369 time_left *= 1 - trace.fraction;
1371 // clipped to another plane
1372 if (numplanes >= MAX_CLIP_PLANES)
1374 // this shouldn't really happen
1375 VectorClear(PRVM_serveredictvector(ent, velocity));
1381 for (i = 0;i < numplanes;i++)
1382 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1386 VectorAdd(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity));
1391 VectorCopy(trace.plane.normal, planes[numplanes]);
1394 // modify original_velocity so it parallels all of the clip planes
1395 for (i = 0;i < numplanes;i++)
1397 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1398 for (j = 0;j < numplanes;j++)
1403 if (DotProduct(new_velocity, planes[j]) < 0)
1413 // go along this plane
1414 VectorCopy(new_velocity, PRVM_serveredictvector(ent, velocity));
1418 // go along the crease
1421 VectorClear(PRVM_serveredictvector(ent, velocity));
1425 CrossProduct(planes[0], planes[1], dir);
1426 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1427 VectorNormalize(dir);
1428 d = DotProduct(dir, PRVM_serveredictvector(ent, velocity));
1429 VectorScale(dir, d, PRVM_serveredictvector(ent, velocity));
1432 // if current velocity is against the original velocity,
1433 // stop dead to avoid tiny occilations in sloping corners
1434 if (DotProduct(PRVM_serveredictvector(ent, velocity), primal_velocity) <= 0)
1436 VectorClear(PRVM_serveredictvector(ent, velocity));
1441 //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]);
1444 if ((blocked & 1) == 0 && bumpcount > 1)
1446 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1447 // flag ONGROUND if there's ground under it
1448 trace = SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), end, MOVE_NORMAL, ent, hitsupercontentsmask);
1452 // LordHavoc: this came from QW and allows you to get out of water more easily
1453 if (sv_gameplayfix_easierwaterjump.integer && ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP) && !(blocked & 8))
1454 VectorCopy(primal_velocity, PRVM_serveredictvector(ent, velocity));
1458 if(!sv_gameplayfix_nogravityonground.integer || !((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND))
1460 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1461 PRVM_serveredictvector(ent, velocity)[2] -= gravity * 0.5f;
1474 static float SV_Gravity (prvm_edict_t *ent)
1476 prvm_prog_t *prog = SVVM_prog;
1479 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1482 return ent_gravity * sv_gravity.value * sv.frametime;
1487 ===============================================================================
1491 ===============================================================================
1494 static qboolean SV_NudgeOutOfSolid_PivotIsKnownGood(prvm_edict_t *ent, vec3_t pivot)
1496 prvm_prog_t *prog = SVVM_prog;
1500 vec3_t stuckmins, stuckmaxs;
1501 vec3_t goodmins, goodmaxs;
1505 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1506 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1507 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1508 VectorCopy(pivot, goodmins);
1509 VectorCopy(pivot, goodmaxs);
1510 for (bump = 0;bump < 6;bump++)
1512 int coord = 2-(bump >> 1);
1513 //int coord = (bump >> 1);
1514 int dir = (bump & 1);
1517 for(subbump = 0; ; ++subbump)
1519 VectorCopy(stuckorigin, testorigin);
1523 testorigin[coord] += stuckmaxs[coord] - goodmaxs[coord];
1528 testorigin[coord] += stuckmins[coord] - goodmins[coord];
1531 stucktrace = SV_TraceBox(stuckorigin, goodmins, goodmaxs, testorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1532 if (stucktrace.bmodelstartsolid)
1534 // BAD BAD, can't fix that
1538 if (stucktrace.fraction >= 1)
1543 // BAD BAD, can't fix that
1547 // we hit something... let's move out of it
1548 VectorSubtract(stucktrace.endpos, testorigin, move);
1549 nudge = DotProduct(stucktrace.plane.normal, move) + 0.03125f; // FIXME cvar this constant
1550 VectorMA(stuckorigin, nudge, stucktrace.plane.normal, stuckorigin);
1554 Con_Printf("subbump: %d\n", subbump);
1560 goodmaxs[coord] = stuckmaxs[coord];
1565 goodmins[coord] = stuckmins[coord];
1570 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1575 static qboolean SV_NudgeOutOfSolid(prvm_edict_t *ent)
1577 prvm_prog_t *prog = SVVM_prog;
1581 vec3_t stuckmins, stuckmaxs;
1583 vec_t separation = sv_gameplayfix_nudgeoutofsolid_separation.value;
1584 if (sv.worldmodel && sv.worldmodel->brushq1.numclipnodes)
1585 separation = 0.0f; // when using hulls, it can not be enlarged
1586 VectorCopy(PRVM_serveredictvector(ent, origin), stuckorigin);
1587 VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
1588 VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
1589 stuckmins[0] -= separation;
1590 stuckmins[1] -= separation;
1591 stuckmins[2] -= separation;
1592 stuckmaxs[0] += separation;
1593 stuckmaxs[1] += separation;
1594 stuckmaxs[2] += separation;
1595 for (bump = 0;bump < 10;bump++)
1597 stucktrace = SV_TraceBox(stuckorigin, stuckmins, stuckmaxs, stuckorigin, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
1598 if (!stucktrace.bmodelstartsolid || stucktrace.startdepth >= 0)
1600 // found a good location, use it
1601 VectorCopy(stuckorigin, PRVM_serveredictvector(ent, origin));
1604 nudge = -stucktrace.startdepth;
1605 VectorMA(stuckorigin, nudge, stucktrace.startdepthnormal, stuckorigin);
1614 Does not change the entities velocity at all
1615 The trace struct is filled with the trace that has been done.
1616 Returns true if the push did not result in the entity being teleported by QC code.
1619 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1621 prvm_prog_t *prog = SVVM_prog;
1629 solid = (int)PRVM_serveredictfloat(ent, solid);
1630 movetype = (int)PRVM_serveredictfloat(ent, movetype);
1631 VectorCopy(PRVM_serveredictvector(ent, mins), mins);
1632 VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
1634 // move start position out of solids
1635 if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
1637 SV_NudgeOutOfSolid(ent);
1640 VectorCopy(PRVM_serveredictvector(ent, origin), start);
1641 VectorAdd(start, push, end);
1643 if (movetype == MOVETYPE_FLYMISSILE)
1644 type = MOVE_MISSILE;
1645 else if (movetype == MOVETYPE_FLY_WORLDONLY)
1646 type = MOVE_WORLDONLY;
1647 else if (solid == SOLID_TRIGGER || solid == SOLID_NOT)
1648 type = MOVE_NOMONSTERS; // only clip against bmodels
1652 *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1653 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1656 VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
1658 ent->priv.required->mark = PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN; // -2: setorigin running
1663 if(!trace->startsolid)
1664 if(SV_TraceBox(PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, mins), PRVM_serveredictvector(ent, maxs), PRVM_serveredictvector(ent, origin), type, ent, SV_GenericHitSuperContentsMask(ent)).startsolid)
1666 Con_Printf("something eeeeevil happened\n");
1671 SV_LinkEdict_TouchAreaGrid(ent);
1673 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))))
1674 SV_Impact (ent, trace);
1676 if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
1678 ent->priv.required->mark = 0;
1681 else if(ent->priv.required->mark == PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN)
1683 ent->priv.required->mark = 0;
1688 Con_Printf("The edict mark had been overwritten! Please debug this.\n");
1700 static void SV_PushMove (prvm_edict_t *pusher, float movetime)
1702 prvm_prog_t *prog = SVVM_prog;
1704 int pusherowner, pusherprog;
1707 float savesolid, movetime2, pushltime;
1708 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, pushermins, pushermaxs, checkorigin, checkmins, checkmaxs;
1710 int numcheckentities;
1711 static prvm_edict_t *checkentities[MAX_EDICTS];
1712 dp_model_t *pushermodel;
1713 trace_t trace, trace2;
1714 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1715 static unsigned short moved_edicts[MAX_EDICTS];
1718 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])
1720 PRVM_serveredictfloat(pusher, ltime) += movetime;
1724 switch ((int) PRVM_serveredictfloat(pusher, solid))
1726 // LordHavoc: valid pusher types
1729 case SOLID_SLIDEBOX:
1730 case SOLID_CORPSE: // LordHavoc: this would be weird...
1732 // LordHavoc: no collisions
1735 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1736 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1737 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
1738 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
1739 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
1740 PRVM_serveredictfloat(pusher, ltime) += movetime;
1741 SV_LinkEdict(pusher);
1744 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, solid));
1747 index = (int) PRVM_serveredictfloat(pusher, modelindex);
1748 if (index < 1 || index >= MAX_MODELS)
1750 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), PRVM_serveredictfloat(pusher, modelindex));
1753 pushermodel = SV_GetModelByIndex(index);
1754 pusherowner = PRVM_serveredictedict(pusher, owner);
1755 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1757 rotated = VectorLength2(PRVM_serveredictvector(pusher, angles)) + VectorLength2(PRVM_serveredictvector(pusher, avelocity)) > 0;
1759 movetime2 = movetime;
1760 VectorScale(PRVM_serveredictvector(pusher, velocity), movetime2, move1);
1761 VectorScale(PRVM_serveredictvector(pusher, avelocity), movetime2, moveangle);
1762 if (moveangle[0] || moveangle[2])
1764 for (i = 0;i < 3;i++)
1768 mins[i] = pushermodel->rotatedmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1769 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1773 mins[i] = pushermodel->rotatedmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1774 maxs[i] = pushermodel->rotatedmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1778 else if (moveangle[1])
1780 for (i = 0;i < 3;i++)
1784 mins[i] = pushermodel->yawmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1785 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1789 mins[i] = pushermodel->yawmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1790 maxs[i] = pushermodel->yawmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1796 for (i = 0;i < 3;i++)
1800 mins[i] = pushermodel->normalmins[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1801 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1805 mins[i] = pushermodel->normalmins[i] + move1[i] + PRVM_serveredictvector(pusher, origin)[i] - 1;
1806 maxs[i] = pushermodel->normalmaxs[i] + PRVM_serveredictvector(pusher, origin)[i] + 1;
1811 VectorNegate (moveangle, a);
1812 AngleVectorsFLU (a, forward, left, up);
1814 VectorCopy (PRVM_serveredictvector(pusher, origin), pushorig);
1815 VectorCopy (PRVM_serveredictvector(pusher, angles), pushang);
1816 pushltime = PRVM_serveredictfloat(pusher, ltime);
1818 // move the pusher to its final position
1820 VectorMA (PRVM_serveredictvector(pusher, origin), movetime, PRVM_serveredictvector(pusher, velocity), PRVM_serveredictvector(pusher, origin));
1821 VectorMA (PRVM_serveredictvector(pusher, angles), movetime, PRVM_serveredictvector(pusher, avelocity), PRVM_serveredictvector(pusher, angles));
1822 PRVM_serveredictfloat(pusher, ltime) += movetime;
1823 SV_LinkEdict(pusher);
1825 pushermodel = SV_GetModelFromEdict(pusher);
1826 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);
1827 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1829 savesolid = PRVM_serveredictfloat(pusher, solid);
1831 // see if any solid entities are inside the final position
1834 if (PRVM_serveredictfloat(pusher, movetype) == MOVETYPE_FAKEPUSH) // Tenebrae's MOVETYPE_PUSH variant that doesn't push...
1835 numcheckentities = 0;
1836 else // MOVETYPE_PUSH
1837 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
1838 for (e = 0;e < numcheckentities;e++)
1840 prvm_edict_t *check = checkentities[e];
1841 int movetype = (int)PRVM_serveredictfloat(check, movetype);
1846 case MOVETYPE_FOLLOW:
1847 case MOVETYPE_NOCLIP:
1848 case MOVETYPE_FLY_WORLDONLY:
1854 if (PRVM_serveredictedict(check, owner) == pusherprog)
1857 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1860 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(PRVM_serveredictstring(check, classname)));
1862 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1863 check->priv.server->waterposition_forceupdate = true;
1865 checkcontents = SV_GenericHitSuperContentsMask(check);
1867 // if the entity is standing on the pusher, it will definitely be moved
1868 // if the entity is not standing on the pusher, but is in the pusher's
1869 // final position, move it
1870 if (!((int)PRVM_serveredictfloat(check, flags) & FL_ONGROUND) || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher)
1872 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1873 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1874 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1875 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1876 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1877 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents);
1878 //trace = SV_TraceBox(PRVM_serveredictvector(check, origin), PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs), PRVM_serveredictvector(check, origin), MOVE_NOMONSTERS, check, checkcontents);
1879 if (!trace.startsolid)
1881 //Con_Printf("- not in solid\n");
1886 VectorLerp(PRVM_serveredictvector(check, mins), 0.5f, PRVM_serveredictvector(check, maxs), pivot);
1887 //VectorClear(pivot);
1892 VectorSubtract (PRVM_serveredictvector(check, origin), PRVM_serveredictvector(pusher, origin), org);
1893 VectorAdd (org, pivot, org);
1894 org2[0] = DotProduct (org, forward);
1895 org2[1] = DotProduct (org, left);
1896 org2[2] = DotProduct (org, up);
1897 VectorSubtract (org2, org, move);
1898 VectorAdd (move, move1, move);
1901 VectorCopy (move1, move);
1903 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1905 VectorCopy (PRVM_serveredictvector(check, origin), check->priv.server->moved_from);
1906 VectorCopy (PRVM_serveredictvector(check, angles), check->priv.server->moved_fromangles);
1907 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1909 // physics objects need better collisions than this code can do
1910 if (movetype == MOVETYPE_PHYSICS)
1912 VectorAdd(PRVM_serveredictvector(check, origin), move, PRVM_serveredictvector(check, origin));
1913 SV_LinkEdict(check);
1914 SV_LinkEdict_TouchAreaGrid(check);
1918 // try moving the contacted entity
1919 PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
1920 if(!SV_PushEntity (&trace, check, move, true, true))
1922 // entity "check" got teleported
1923 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1924 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1925 continue; // pushed enough
1927 // FIXME: turn players specially
1928 PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
1929 PRVM_serveredictfloat(pusher, solid) = savesolid; // was SOLID_BSP
1930 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1932 // this trace.fraction < 1 check causes items to fall off of pushers
1933 // if they pass under or through a wall
1934 // the groundentity check causes items to fall off of ledges
1935 if (PRVM_serveredictfloat(check, movetype) != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(PRVM_serveredictedict(check, groundentity)) != pusher))
1936 PRVM_serveredictfloat(check, flags) = (int)PRVM_serveredictfloat(check, flags) & ~FL_ONGROUND;
1938 // if it is still inside the pusher, block
1939 VectorCopy(PRVM_serveredictvector(pusher, mins), pushermins);
1940 VectorCopy(PRVM_serveredictvector(pusher, maxs), pushermaxs);
1941 VectorCopy(PRVM_serveredictvector(check, origin), checkorigin);
1942 VectorCopy(PRVM_serveredictvector(check, mins), checkmins);
1943 VectorCopy(PRVM_serveredictvector(check, maxs), checkmaxs);
1944 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pushermins, pushermaxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, checkorigin, checkmins, checkmaxs, checkorigin, checkcontents);
1945 if (trace.startsolid)
1948 if(SV_NudgeOutOfSolid_PivotIsKnownGood(check, pivot))
1950 // hack to invoke all necessary movement triggers
1952 if(!SV_PushEntity(&trace2, check, move2, true, true))
1954 // entity "check" got teleported
1961 // still inside pusher, so it's really blocked
1964 if (PRVM_serveredictvector(check, mins)[0] == PRVM_serveredictvector(check, maxs)[0])
1966 if (PRVM_serveredictfloat(check, solid) == SOLID_NOT || PRVM_serveredictfloat(check, solid) == SOLID_TRIGGER)
1969 PRVM_serveredictvector(check, mins)[0] = PRVM_serveredictvector(check, mins)[1] = 0;
1970 VectorCopy (PRVM_serveredictvector(check, mins), PRVM_serveredictvector(check, maxs));
1974 VectorCopy (pushorig, PRVM_serveredictvector(pusher, origin));
1975 VectorCopy (pushang, PRVM_serveredictvector(pusher, angles));
1976 PRVM_serveredictfloat(pusher, ltime) = pushltime;
1977 SV_LinkEdict(pusher);
1979 // move back any entities we already moved
1980 for (i = 0;i < num_moved;i++)
1982 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1983 VectorCopy (ed->priv.server->moved_from, PRVM_serveredictvector(ed, origin));
1984 VectorCopy (ed->priv.server->moved_fromangles, PRVM_serveredictvector(ed, angles));
1988 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1989 if (PRVM_serveredictfunction(pusher, blocked))
1991 PRVM_serverglobalfloat(time) = sv.time;
1992 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(pusher);
1993 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(check);
1994 prog->ExecuteProgram(prog, PRVM_serveredictfunction(pusher, blocked), "QC function self.blocked is missing");
1999 PRVM_serveredictvector(pusher, angles)[0] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[0] * (1.0 / 360.0));
2000 PRVM_serveredictvector(pusher, angles)[1] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[1] * (1.0 / 360.0));
2001 PRVM_serveredictvector(pusher, angles)[2] -= 360.0 * floor(PRVM_serveredictvector(pusher, angles)[2] * (1.0 / 360.0));
2010 static void SV_Physics_Pusher (prvm_edict_t *ent)
2012 prvm_prog_t *prog = SVVM_prog;
2013 float thinktime, oldltime, movetime;
2015 oldltime = PRVM_serveredictfloat(ent, ltime);
2017 thinktime = PRVM_serveredictfloat(ent, nextthink);
2018 if (thinktime < PRVM_serveredictfloat(ent, ltime) + sv.frametime)
2020 movetime = thinktime - PRVM_serveredictfloat(ent, ltime);
2025 movetime = sv.frametime;
2028 // advances PRVM_serveredictfloat(ent, ltime) if not blocked
2029 SV_PushMove (ent, movetime);
2031 if (thinktime > oldltime && thinktime <= PRVM_serveredictfloat(ent, ltime))
2033 PRVM_serveredictfloat(ent, nextthink) = 0;
2034 PRVM_serverglobalfloat(time) = sv.time;
2035 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2036 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
2037 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, think), "QC function self.think is missing");
2043 ===============================================================================
2047 ===============================================================================
2050 static float unstickoffsets[] =
2052 // poutting -/+z changes first as they are least weird
2067 typedef enum unstickresult_e
2075 static unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
2077 prvm_prog_t *prog = SVVM_prog;
2080 // if not stuck in a bmodel, just return
2081 if (!SV_TestEntityPosition(ent, vec3_origin))
2082 return UNSTICK_GOOD;
2084 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
2086 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
2088 VectorCopy(unstickoffsets + i, offset);
2090 //SV_LinkEdict_TouchAreaGrid(ent);
2091 return UNSTICK_UNSTUCK;
2095 maxunstick = (int) ((PRVM_serveredictvector(ent, maxs)[2] - PRVM_serveredictvector(ent, mins)[2]) * 0.36);
2096 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
2098 for(i = 2; i <= maxunstick; ++i)
2100 VectorClear(offset);
2102 if (!SV_TestEntityPosition(ent, offset))
2105 //SV_LinkEdict_TouchAreaGrid(ent);
2106 return UNSTICK_UNSTUCK;
2109 if (!SV_TestEntityPosition(ent, offset))
2112 //SV_LinkEdict_TouchAreaGrid(ent);
2113 return UNSTICK_UNSTUCK;
2117 return UNSTICK_STUCK;
2120 qboolean SV_UnstickEntity (prvm_edict_t *ent)
2122 prvm_prog_t *prog = SVVM_prog;
2124 switch(SV_UnstickEntityReturnOffset(ent, offset))
2128 case UNSTICK_UNSTUCK:
2129 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]);
2132 if (developer_extra.integer)
2133 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2136 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2145 This is a big hack to try and fix the rare case of getting stuck in the world
2149 static void SV_CheckStuck (prvm_edict_t *ent)
2151 prvm_prog_t *prog = SVVM_prog;
2154 switch(SV_UnstickEntityReturnOffset(ent, offset))
2157 VectorCopy (PRVM_serveredictvector(ent, origin), PRVM_serveredictvector(ent, oldorigin));
2159 case UNSTICK_UNSTUCK:
2160 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]);
2163 VectorSubtract(PRVM_serveredictvector(ent, oldorigin), PRVM_serveredictvector(ent, origin), offset);
2164 if (!SV_TestEntityPosition(ent, offset))
2166 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)));
2168 //SV_LinkEdict_TouchAreaGrid(ent);
2171 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
2174 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2184 static qboolean SV_CheckWater (prvm_edict_t *ent)
2186 prvm_prog_t *prog = SVVM_prog;
2188 int nNativeContents;
2191 point[0] = PRVM_serveredictvector(ent, origin)[0];
2192 point[1] = PRVM_serveredictvector(ent, origin)[1];
2193 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, mins)[2] + 1;
2195 // DRESK - Support for Entity Contents Transition Event
2196 // NOTE: Some logic needed to be slightly re-ordered
2197 // to not affect performance and allow for the feature.
2199 // Acquire Super Contents Prior to Resets
2200 cont = SV_PointSuperContents(point);
2201 // Acquire Native Contents Here
2202 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2204 // DRESK - Support for Entity Contents Transition Event
2205 if(PRVM_serveredictfloat(ent, watertype))
2206 // Entity did NOT Spawn; Check
2207 SV_CheckContentsTransition(ent, nNativeContents);
2210 PRVM_serveredictfloat(ent, waterlevel) = 0;
2211 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2212 cont = SV_PointSuperContents(point);
2213 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2215 PRVM_serveredictfloat(ent, watertype) = nNativeContents;
2216 PRVM_serveredictfloat(ent, waterlevel) = 1;
2217 point[2] = PRVM_serveredictvector(ent, origin)[2] + (PRVM_serveredictvector(ent, mins)[2] + PRVM_serveredictvector(ent, maxs)[2])*0.5;
2218 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2220 PRVM_serveredictfloat(ent, waterlevel) = 2;
2221 point[2] = PRVM_serveredictvector(ent, origin)[2] + PRVM_serveredictvector(ent, view_ofs)[2];
2222 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2223 PRVM_serveredictfloat(ent, waterlevel) = 3;
2227 return PRVM_serveredictfloat(ent, waterlevel) > 1;
2236 static void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2238 prvm_prog_t *prog = SVVM_prog;
2240 vec3_t forward, into, side, v_angle;
2242 VectorCopy(PRVM_serveredictvector(ent, v_angle), v_angle);
2243 AngleVectors (v_angle, forward, NULL, NULL);
2244 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2246 // cut the tangential velocity
2247 i = DotProduct (stepnormal, PRVM_serveredictvector(ent, velocity));
2248 VectorScale (stepnormal, i, into);
2249 VectorSubtract (PRVM_serveredictvector(ent, velocity), into, side);
2250 PRVM_serveredictvector(ent, velocity)[0] = side[0] * (1 + d);
2251 PRVM_serveredictvector(ent, velocity)[1] = side[1] * (1 + d);
2257 =====================
2260 Player has come to a dead stop, possibly due to the problem with limited
2261 float precision at some angle joins in the BSP hull.
2263 Try fixing by pushing one pixel in each direction.
2265 This is a hack, but in the interest of good gameplay...
2266 ======================
2268 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2273 VectorCopy (PRVM_serveredictvector(ent, origin), oldorg);
2276 for (i=0 ; i<8 ; i++)
2278 // try pushing a little in an axial direction
2281 case 0: dir[0] = 2; dir[1] = 0; break;
2282 case 1: dir[0] = 0; dir[1] = 2; break;
2283 case 2: dir[0] = -2; dir[1] = 0; break;
2284 case 3: dir[0] = 0; dir[1] = -2; break;
2285 case 4: dir[0] = 2; dir[1] = 2; break;
2286 case 5: dir[0] = -2; dir[1] = 2; break;
2287 case 6: dir[0] = 2; dir[1] = -2; break;
2288 case 7: dir[0] = -2; dir[1] = -2; break;
2291 SV_PushEntity (&trace, ent, dir, false, true);
2293 // retry the original move
2294 PRVM_serveredictvector(ent, velocity)[0] = oldvel[0];
2295 PRVM_serveredictvector(ent, velocity)[1] = oldvel[1];
2296 PRVM_serveredictvector(ent, velocity)[2] = 0;
2297 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2299 if (fabs(oldorg[1] - PRVM_serveredictvector(ent, origin)[1]) > 4
2300 || fabs(oldorg[0] - PRVM_serveredictvector(ent, origin)[0]) > 4)
2302 Con_DPrint("TryUnstick - success.\n");
2306 // go back to the original pos and try again
2307 VectorCopy (oldorg, PRVM_serveredictvector(ent, origin));
2311 VectorClear (PRVM_serveredictvector(ent, velocity));
2312 Con_DPrint("TryUnstick - failure.\n");
2318 =====================
2321 Only used by players
2322 ======================
2324 static void SV_WalkMove (prvm_edict_t *ent)
2326 prvm_prog_t *prog = SVVM_prog;
2329 //int originalmove_clip;
2330 int originalmove_flags;
2331 int originalmove_groundentity;
2332 int hitsupercontentsmask;
2334 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity, entmins, entmaxs;
2335 trace_t downtrace, trace;
2336 qboolean applygravity;
2338 // if frametime is 0 (due to client sending the same timestamp twice),
2340 if (sv.frametime <= 0)
2343 if (sv_gameplayfix_unstickplayers.integer)
2344 SV_CheckStuck (ent);
2346 applygravity = !SV_CheckWater (ent) && PRVM_serveredictfloat(ent, movetype) == MOVETYPE_WALK && ! ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP);
2348 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2350 SV_CheckVelocity(ent);
2352 // do a regular slide move unless it looks like you ran into a step
2353 oldonground = (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND;
2355 VectorCopy (PRVM_serveredictvector(ent, origin), start_origin);
2356 VectorCopy (PRVM_serveredictvector(ent, velocity), start_velocity);
2358 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2360 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2363 // only try this if there was no floor in the way in the trace (no,
2364 // this check seems to be not REALLY necessary, because if clip & 1,
2365 // our trace will hit that thing too)
2366 VectorSet(upmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] + 1);
2367 VectorSet(downmove, PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2] - 1);
2368 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLYMISSILE)
2369 type = MOVE_MISSILE;
2370 else if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_FLY_WORLDONLY)
2371 type = MOVE_WORLDONLY;
2372 else if (PRVM_serveredictfloat(ent, solid) == SOLID_TRIGGER || PRVM_serveredictfloat(ent, solid) == SOLID_NOT)
2373 type = MOVE_NOMONSTERS; // only clip against bmodels
2376 VectorCopy(PRVM_serveredictvector(ent, mins), entmins);
2377 VectorCopy(PRVM_serveredictvector(ent, maxs), entmaxs);
2378 trace = SV_TraceBox(upmove, entmins, entmaxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2379 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2380 clip |= 1; // but we HAVE found a floor
2383 // if the move did not hit the ground at any point, we're not on ground
2385 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2387 SV_CheckVelocity(ent);
2389 SV_LinkEdict_TouchAreaGrid(ent);
2391 if(clip & 8) // teleport
2394 if ((int)PRVM_serveredictfloat(ent, flags) & FL_WATERJUMP)
2397 if (sv_nostep.integer)
2400 VectorCopy(PRVM_serveredictvector(ent, origin), originalmove_origin);
2401 VectorCopy(PRVM_serveredictvector(ent, velocity), originalmove_velocity);
2402 //originalmove_clip = clip;
2403 originalmove_flags = (int)PRVM_serveredictfloat(ent, flags);
2404 originalmove_groundentity = PRVM_serveredictedict(ent, groundentity);
2406 // if move didn't block on a step, return
2409 // if move was not trying to move into the step, return
2410 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2413 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY)
2415 // return if gibbed by a trigger
2416 if (PRVM_serveredictfloat(ent, movetype) != MOVETYPE_WALK)
2419 // only step up while jumping if that is enabled
2420 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2421 if (!oldonground && PRVM_serveredictfloat(ent, waterlevel) == 0)
2425 // try moving up and forward to go up a step
2426 // back to start pos
2427 VectorCopy (start_origin, PRVM_serveredictvector(ent, origin));
2428 VectorCopy (start_velocity, PRVM_serveredictvector(ent, velocity));
2431 VectorClear (upmove);
2432 upmove[2] = sv_stepheight.value;
2433 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2435 // we got teleported when upstepping... must abort the move
2440 PRVM_serveredictvector(ent, velocity)[2] = 0;
2441 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2442 PRVM_serveredictvector(ent, velocity)[2] += start_velocity[2];
2445 // we got teleported when upstepping... must abort the move
2446 // note that z velocity handling may not be what QC expects here, but we cannot help it
2450 SV_CheckVelocity(ent);
2452 SV_LinkEdict_TouchAreaGrid(ent);
2454 // check for stuckness, possibly due to the limited precision of floats
2455 // in the clipping hulls
2457 && fabs(originalmove_origin[1] - PRVM_serveredictvector(ent, origin)[1]) < 0.03125
2458 && fabs(originalmove_origin[0] - PRVM_serveredictvector(ent, origin)[0]) < 0.03125)
2460 //Con_Printf("wall\n");
2461 // stepping up didn't make any progress, revert to original move
2462 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2463 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2464 //clip = originalmove_clip;
2465 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2466 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2467 // now try to unstick if needed
2468 //clip = SV_TryUnstick (ent, oldvel);
2472 //Con_Printf("step - ");
2474 // extra friction based on view angle
2475 if (clip & 2 && sv_wallfriction.integer)
2476 SV_WallFriction (ent, stepnormal);
2478 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2479 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))
2483 VectorClear (downmove);
2484 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2485 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2487 // we got teleported when downstepping... must abort the move
2491 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2493 // this has been disabled so that you can't jump when you are stepping
2494 // up while already jumping (also known as the Quake2 double jump bug)
2496 // LordHavoc: disabled this check so you can walk on monsters/players
2497 //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
2499 //Con_Printf("onground\n");
2500 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2501 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(downtrace.ent);
2507 //Con_Printf("slope\n");
2508 // if the push down didn't end up on good ground, use the move without
2509 // the step up. This happens near wall / slope combinations, and can
2510 // cause the player to hop up higher on a slope too steep to climb
2511 VectorCopy(originalmove_origin, PRVM_serveredictvector(ent, origin));
2512 VectorCopy(originalmove_velocity, PRVM_serveredictvector(ent, velocity));
2513 //clip = originalmove_clip;
2514 PRVM_serveredictfloat(ent, flags) = originalmove_flags;
2515 PRVM_serveredictedict(ent, groundentity) = originalmove_groundentity;
2518 SV_CheckVelocity(ent);
2520 SV_LinkEdict_TouchAreaGrid(ent);
2523 //============================================================================
2529 Entities that are "stuck" to another entity
2532 static void SV_Physics_Follow (prvm_edict_t *ent)
2534 prvm_prog_t *prog = SVVM_prog;
2535 vec3_t vf, vr, vu, angles, v;
2539 if (!SV_RunThink (ent))
2542 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2543 e = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, aiment));
2544 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])
2546 // quick case for no rotation
2547 VectorAdd(PRVM_serveredictvector(e, origin), PRVM_serveredictvector(ent, view_ofs), PRVM_serveredictvector(ent, origin));
2551 angles[0] = -PRVM_serveredictvector(ent, punchangle)[0];
2552 angles[1] = PRVM_serveredictvector(ent, punchangle)[1];
2553 angles[2] = PRVM_serveredictvector(ent, punchangle)[2];
2554 AngleVectors (angles, vf, vr, vu);
2555 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];
2556 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];
2557 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];
2558 angles[0] = -PRVM_serveredictvector(e, angles)[0];
2559 angles[1] = PRVM_serveredictvector(e, angles)[1];
2560 angles[2] = PRVM_serveredictvector(e, angles)[2];
2561 AngleVectors (angles, vf, vr, vu);
2562 PRVM_serveredictvector(ent, origin)[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + PRVM_serveredictvector(e, origin)[0];
2563 PRVM_serveredictvector(ent, origin)[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + PRVM_serveredictvector(e, origin)[1];
2564 PRVM_serveredictvector(ent, origin)[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + PRVM_serveredictvector(e, origin)[2];
2566 VectorAdd (PRVM_serveredictvector(e, angles), PRVM_serveredictvector(ent, v_angle), PRVM_serveredictvector(ent, angles));
2568 //SV_LinkEdict_TouchAreaGrid(ent);
2572 ==============================================================================
2576 ==============================================================================
2581 SV_CheckWaterTransition
2585 static void SV_CheckWaterTransition (prvm_edict_t *ent)
2588 prvm_prog_t *prog = SVVM_prog;
2589 // 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
2591 VectorCopy(PRVM_serveredictvector(ent, origin), entorigin);
2592 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(entorigin));
2593 if (!PRVM_serveredictfloat(ent, watertype))
2595 // just spawned here
2596 if (!sv_gameplayfix_fixedcheckwatertransition.integer)
2598 PRVM_serveredictfloat(ent, watertype) = cont;
2599 PRVM_serveredictfloat(ent, waterlevel) = 1;
2603 // DRESK - Support for Entity Contents Transition Event
2604 // NOTE: Call here BEFORE updating the watertype below,
2605 // and suppress watersplash sound if a valid function
2606 // call was made to allow for custom "splash" sounds.
2607 else if( !SV_CheckContentsTransition(ent, cont) )
2608 { // Contents Transition Function Invalid; Potentially Play Water Sound
2609 // check if the entity crossed into or out of water
2610 if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2611 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false, 1.0f);
2614 if (cont <= CONTENTS_WATER)
2616 PRVM_serveredictfloat(ent, watertype) = cont;
2617 PRVM_serveredictfloat(ent, waterlevel) = 1;
2621 PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
2622 PRVM_serveredictfloat(ent, waterlevel) = sv_gameplayfix_fixedcheckwatertransition.integer ? 0 : cont;
2630 Toss, bounce, and fly movement. When onground, do nothing.
2634 void SV_Physics_Toss (prvm_edict_t *ent)
2636 prvm_prog_t *prog = SVVM_prog;
2641 prvm_edict_t *groundentity;
2642 float d, ent_gravity;
2646 // if onground, return without moving
2647 if ((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2649 groundentity = PRVM_PROG_TO_EDICT(PRVM_serveredictedict(ent, groundentity));
2650 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2652 // don't stick to ground if onground and moving upward
2653 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2655 else if (!PRVM_serveredictedict(ent, groundentity) || !sv_gameplayfix_noairborncorpse.integer)
2657 // we can trust FL_ONGROUND if groundentity is world because it never moves
2660 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2662 // if ent was supported by a brush model on previous frame,
2663 // and groundentity is now freed, set groundentity to 0 (world)
2664 // which leaves it suspended in the air
2665 PRVM_serveredictedict(ent, groundentity) = 0;
2666 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2669 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2671 // don't slide if still touching the groundentity
2675 ent->priv.server->suspendedinairflag = false;
2677 SV_CheckVelocity (ent);
2680 if (PRVM_serveredictfloat(ent, movetype) == MOVETYPE_TOSS || PRVM_serveredictfloat(ent, movetype) == MOVETYPE_BOUNCE)
2681 PRVM_serveredictvector(ent, velocity)[2] -= SV_Gravity(ent);
2684 VectorMA (PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2686 movetime = sv.frametime;
2687 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2690 VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
2691 if(!SV_PushEntity(&trace, ent, move, true, true))
2692 return; // teleported
2693 if (ent->priv.server->free)
2695 if (trace.bmodelstartsolid)
2697 // try to unstick the entity
2698 if (sv_gameplayfix_unstickentities.integer)
2699 SV_UnstickEntity(ent);
2700 if(!SV_PushEntity(&trace, ent, move, false, true))
2701 return; // teleported
2702 if (ent->priv.server->free)
2705 if (trace.fraction == 1)
2707 movetime *= 1 - min(1, trace.fraction);
2708 switch((int)PRVM_serveredictfloat(ent, movetype))
2710 case MOVETYPE_BOUNCEMISSILE:
2711 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2713 bouncefactor = 1.0f;
2715 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2716 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2717 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2720 case MOVETYPE_BOUNCE:
2721 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2723 bouncefactor = 0.5f;
2725 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2727 bouncestop = 60.0f / 800.0f;
2729 ClipVelocity(PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1 + bouncefactor);
2730 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2733 // LordHavoc: fixed grenades not bouncing when fired down a slope
2734 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2735 d = fabs(DotProduct(trace.plane.normal, PRVM_serveredictvector(ent, velocity)));
2737 d = PRVM_serveredictvector(ent, velocity)[2];
2738 if (trace.plane.normal[2] > 0.7 && d < sv_gravity.value * bouncestop * ent_gravity)
2740 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2741 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2742 VectorClear(PRVM_serveredictvector(ent, velocity));
2743 VectorClear(PRVM_serveredictvector(ent, avelocity));
2748 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2749 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2754 ClipVelocity (PRVM_serveredictvector(ent, velocity), trace.plane.normal, PRVM_serveredictvector(ent, velocity), 1.0);
2755 if (trace.plane.normal[2] > 0.7)
2757 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) | FL_ONGROUND;
2758 PRVM_serveredictedict(ent, groundentity) = PRVM_EDICT_TO_PROG(trace.ent);
2759 if (PRVM_serveredictfloat(((prvm_edict_t *)trace.ent), solid) == SOLID_BSP)
2760 ent->priv.server->suspendedinairflag = true;
2761 VectorClear (PRVM_serveredictvector(ent, velocity));
2762 VectorClear (PRVM_serveredictvector(ent, avelocity));
2765 PRVM_serveredictfloat(ent, flags) = (int)PRVM_serveredictfloat(ent, flags) & ~FL_ONGROUND;
2771 // check for in water
2772 SV_CheckWaterTransition (ent);
2776 ===============================================================================
2780 ===============================================================================
2787 Monsters freefall when they don't have a ground entity, otherwise
2788 all movement is done with discrete steps.
2790 This is also used for objects that have become still on the ground, but
2791 will fall if the floor is pulled out from under them.
2794 static void SV_Physics_Step (prvm_edict_t *ent)
2796 prvm_prog_t *prog = SVVM_prog;
2797 int flags = (int)PRVM_serveredictfloat(ent, flags);
2800 // Backup Velocity in the event that movetypesteplandevent is called,
2801 // to provide a parameter with the entity's velocity at impact.
2802 vec3_t backupVelocity;
2803 VectorCopy(PRVM_serveredictvector(ent, velocity), backupVelocity);
2804 // don't fall at all if fly/swim
2805 if (!(flags & (FL_FLY | FL_SWIM)))
2807 if (flags & FL_ONGROUND)
2809 // freefall if onground and moving upward
2810 // freefall if not standing on a world surface (it may be a lift or trap door)
2811 if (PRVM_serveredictvector(ent, velocity)[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2813 PRVM_serveredictfloat(ent, flags) -= FL_ONGROUND;
2814 SV_CheckVelocity(ent);
2815 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2817 SV_LinkEdict_TouchAreaGrid(ent);
2818 ent->priv.server->waterposition_forceupdate = true;
2823 // freefall if not onground
2824 int hitsound = PRVM_serveredictvector(ent, velocity)[2] < sv_gravity.value * -0.1;
2826 SV_CheckVelocity(ent);
2827 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2829 SV_LinkEdict_TouchAreaGrid(ent);
2832 if (hitsound && (int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND)
2834 // DRESK - Check for Entity Land Event Function
2835 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2836 { // Valid Function; Execute
2837 // Prepare Parameters
2838 // Assign Velocity at Impact
2839 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2840 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2841 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2843 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2845 PRVM_serverglobalfloat(time) = sv.time;
2846 // Execute VM Function
2847 prog->ExecuteProgram(prog, PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2850 // Check for Engine Landing Sound
2851 if(sv_sound_land.string)
2852 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1, false, 1.0f);
2854 ent->priv.server->waterposition_forceupdate = true;
2859 if (!SV_RunThink(ent))
2862 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin))
2864 ent->priv.server->waterposition_forceupdate = false;
2865 VectorCopy(PRVM_serveredictvector(ent, origin), ent->priv.server->waterposition_origin);
2866 SV_CheckWaterTransition(ent);
2870 //============================================================================
2872 static void SV_Physics_Entity (prvm_edict_t *ent)
2874 prvm_prog_t *prog = SVVM_prog;
2875 // don't run think/move on newly spawned projectiles as it messes up
2876 // movement interpolation and rocket trails, and is inconsistent with
2877 // respect to entities spawned in the same frame
2878 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2879 // but if it spawns a lower numbered ent, it doesn't - this never moves
2880 // ents in the first frame regardless)
2881 qboolean runmove = ent->priv.server->move;
2882 ent->priv.server->move = true;
2883 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2885 switch ((int) PRVM_serveredictfloat(ent, movetype))
2888 case MOVETYPE_FAKEPUSH:
2889 SV_Physics_Pusher (ent);
2892 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2893 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
2896 case MOVETYPE_FOLLOW:
2897 SV_Physics_Follow (ent);
2899 case MOVETYPE_NOCLIP:
2900 if (SV_RunThink(ent))
2903 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
2904 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
2909 SV_Physics_Step (ent);
2912 if (SV_RunThink (ent))
2916 case MOVETYPE_BOUNCE:
2917 case MOVETYPE_BOUNCEMISSILE:
2918 case MOVETYPE_FLYMISSILE:
2920 case MOVETYPE_FLY_WORLDONLY:
2922 if (SV_RunThink (ent))
2923 SV_Physics_Toss (ent);
2925 case MOVETYPE_PHYSICS:
2926 if (SV_RunThink(ent))
2929 SV_LinkEdict_TouchAreaGrid(ent);
2933 Con_Printf ("SV_Physics: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
2938 void SV_Physics_ClientMove(void)
2940 prvm_prog_t *prog = SVVM_prog;
2942 ent = host_client->edict;
2944 // call player physics, this needs the proper frametime
2945 PRVM_serverglobalfloat(frametime) = sv.frametime;
2948 // call standard client pre-think, with frametime = 0
2949 PRVM_serverglobalfloat(time) = sv.time;
2950 PRVM_serverglobalfloat(frametime) = 0;
2951 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2952 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
2953 PRVM_serverglobalfloat(frametime) = sv.frametime;
2955 // make sure the velocity is sane (not a NaN)
2956 SV_CheckVelocity(ent);
2958 // perform MOVETYPE_WALK behavior
2961 // call standard player post-think, with frametime = 0
2962 PRVM_serverglobalfloat(time) = sv.time;
2963 PRVM_serverglobalfloat(frametime) = 0;
2964 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
2965 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
2966 PRVM_serverglobalfloat(frametime) = sv.frametime;
2968 if(PRVM_serveredictfloat(ent, fixangle))
2970 // angle fixing was requested by physics code...
2971 // so store the current angles for later use
2972 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
2973 host_client->fixangle_angles_set = TRUE;
2975 // and clear fixangle for the next frame
2976 PRVM_serveredictfloat(ent, fixangle) = 0;
2980 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2982 prvm_prog_t *prog = SVVM_prog;
2983 // don't do physics on disconnected clients, FrikBot relies on this
2984 if (!host_client->spawned)
2987 // make sure the velocity is sane (not a NaN)
2988 SV_CheckVelocity(ent);
2990 // don't run physics here if running asynchronously
2991 if (host_client->clmovement_inputtimeout <= 0)
2994 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2997 // make sure the velocity is still sane (not a NaN)
2998 SV_CheckVelocity(ent);
3000 // call standard client pre-think
3001 PRVM_serverglobalfloat(time) = sv.time;
3002 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3003 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPreThink), "QC function PlayerPreThink is missing");
3005 // make sure the velocity is still sane (not a NaN)
3006 SV_CheckVelocity(ent);
3009 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
3011 prvm_prog_t *prog = SVVM_prog;
3012 // don't do physics on disconnected clients, FrikBot relies on this
3013 if (!host_client->spawned)
3016 // make sure the velocity is sane (not a NaN)
3017 SV_CheckVelocity(ent);
3019 // call standard player post-think
3020 PRVM_serverglobalfloat(time) = sv.time;
3021 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
3022 prog->ExecuteProgram(prog, PRVM_serverfunction(PlayerPostThink), "QC function PlayerPostThink is missing");
3024 // make sure the velocity is still sane (not a NaN)
3025 SV_CheckVelocity(ent);
3027 if(PRVM_serveredictfloat(ent, fixangle))
3029 // angle fixing was requested by physics code...
3030 // so store the current angles for later use
3031 VectorCopy(PRVM_serveredictvector(ent, angles), host_client->fixangle_angles);
3032 host_client->fixangle_angles_set = TRUE;
3034 // and clear fixangle for the next frame
3035 PRVM_serveredictfloat(ent, fixangle) = 0;
3038 // decrement the countdown variable used to decide when to go back to
3039 // synchronous physics
3040 if (host_client->clmovement_inputtimeout > sv.frametime)
3041 host_client->clmovement_inputtimeout -= sv.frametime;
3043 host_client->clmovement_inputtimeout = 0;
3046 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
3048 prvm_prog_t *prog = SVVM_prog;
3049 // don't do physics on disconnected clients, FrikBot relies on this
3050 if (!host_client->spawned)
3052 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
3056 // make sure the velocity is sane (not a NaN)
3057 SV_CheckVelocity(ent);
3059 switch ((int) PRVM_serveredictfloat(ent, movetype))
3062 case MOVETYPE_FAKEPUSH:
3063 SV_Physics_Pusher (ent);
3066 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
3067 if (PRVM_serveredictfloat(ent, nextthink) > 0 && PRVM_serveredictfloat(ent, nextthink) <= sv.time + sv.frametime)
3070 case MOVETYPE_FOLLOW:
3071 SV_Physics_Follow (ent);
3073 case MOVETYPE_NOCLIP:
3076 VectorMA(PRVM_serveredictvector(ent, origin), sv.frametime, PRVM_serveredictvector(ent, velocity), PRVM_serveredictvector(ent, origin));
3077 VectorMA(PRVM_serveredictvector(ent, angles), sv.frametime, PRVM_serveredictvector(ent, avelocity), PRVM_serveredictvector(ent, angles));
3080 SV_Physics_Step (ent);
3084 // don't run physics here if running asynchronously
3085 if (host_client->clmovement_inputtimeout <= 0)
3089 case MOVETYPE_BOUNCE:
3090 case MOVETYPE_BOUNCEMISSILE:
3091 case MOVETYPE_FLYMISSILE:
3094 SV_Physics_Toss (ent);
3097 case MOVETYPE_FLY_WORLDONLY:
3101 case MOVETYPE_PHYSICS:
3105 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)PRVM_serveredictfloat(ent, movetype));
3109 SV_CheckVelocity (ent);
3112 SV_LinkEdict_TouchAreaGrid(ent);
3114 SV_CheckVelocity (ent);
3123 void SV_Physics (void)
3125 prvm_prog_t *prog = SVVM_prog;
3129 // let the progs know that a new frame has started
3130 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3131 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3132 PRVM_serverglobalfloat(time) = sv.time;
3133 PRVM_serverglobalfloat(frametime) = sv.frametime;
3134 prog->ExecuteProgram(prog, PRVM_serverfunction(StartFrame), "QC function StartFrame is missing");
3136 // run physics engine
3137 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
3140 // treat each object in turn
3143 // if force_retouch, relink all the entities
3144 if (PRVM_serverglobalfloat(force_retouch) > 0)
3145 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3146 if (!ent->priv.server->free)
3147 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
3149 if (sv_gameplayfix_consistentplayerprethink.integer)
3151 // run physics on the client entities in 3 stages
3152 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3153 if (!ent->priv.server->free)
3154 SV_Physics_ClientEntity_PreThink(ent);
3156 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3157 if (!ent->priv.server->free)
3158 SV_Physics_ClientEntity(ent);
3160 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3161 if (!ent->priv.server->free)
3162 SV_Physics_ClientEntity_PostThink(ent);
3166 // run physics on the client entities
3167 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
3169 if (!ent->priv.server->free)
3171 SV_Physics_ClientEntity_PreThink(ent);
3172 SV_Physics_ClientEntity(ent);
3173 SV_Physics_ClientEntity_PostThink(ent);
3178 // run physics on all the non-client entities
3179 if (!sv_freezenonclients.integer)
3181 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3182 if (!ent->priv.server->free)
3183 SV_Physics_Entity(ent);
3184 // make a second pass to see if any ents spawned this frame and make
3185 // sure they run their move/think
3186 if (sv_gameplayfix_delayprojectiles.integer < 0)
3187 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
3188 if (!ent->priv.server->move && !ent->priv.server->free)
3189 SV_Physics_Entity(ent);
3192 if (PRVM_serverglobalfloat(force_retouch) > 0)
3193 PRVM_serverglobalfloat(force_retouch) = max(0, PRVM_serverglobalfloat(force_retouch) - 1);
3195 // LordHavoc: endframe support
3196 if (PRVM_serverfunction(EndFrame))
3198 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(prog->edicts);
3199 PRVM_serverglobaledict(other) = PRVM_EDICT_TO_PROG(prog->edicts);
3200 PRVM_serverglobalfloat(time) = sv.time;
3201 prog->ExecuteProgram(prog, PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3204 // decrement prog->num_edicts if the highest number entities died
3205 for (;PRVM_ED_CanAlloc(prog, PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3207 if (!sv_freezenonclients.integer)
3208 sv.time += sv.frametime;