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.
27 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.
29 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38 solid_edge items only clip against bsp models.
42 #define MOVE_EPSILON 0.01
44 void SV_Physics_Toss (prvm_edict_t *ent);
46 int SV_GetPitchSign(prvm_edict_t *ent)
50 (model = SV_GetModelFromEdict(ent))
52 model->type == mod_alias
55 (((unsigned char)PRVM_serveredictfloat(ent, pflags)) & PFLAGS_FULLDYNAMIC)
57 ((gamemode == GAME_TENEBRAE) && ((unsigned int)ent->fields.server->effects & (16 | 32)))
65 ===============================================================================
69 ===============================================================================
72 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
76 int dphitcontentsmask = (int)PRVM_serveredictfloat(passedict, dphitcontentsmask);
77 if (dphitcontentsmask)
78 return dphitcontentsmask;
79 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
81 if ((int)passedict->fields.server->flags & FL_MONSTER)
82 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
84 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
86 else if (passedict->fields.server->solid == SOLID_CORPSE)
87 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
88 else if (passedict->fields.server->solid == SOLID_TRIGGER)
89 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
91 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
94 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
102 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
104 int i, bodysupercontents;
107 prvm_edict_t *traceowner, *touch;
109 // bounding box of entire move area
110 vec3_t clipboxmins, clipboxmaxs;
111 // size when clipping against monsters
112 vec3_t clipmins2, clipmaxs2;
113 // start and end origin of move
117 // matrices to transform into/out of other entity's space
118 matrix4x4_t matrix, imatrix;
119 // model of other entity
121 // list of entities to test for collisions
123 static prvm_edict_t *touchedicts[MAX_EDICTS];
125 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
127 VectorCopy(start, clipstart);
128 VectorClear(clipmins2);
129 VectorClear(clipmaxs2);
130 #if COLLISIONPARANOID >= 3
131 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
135 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
136 cliptrace.bmodelstartsolid = cliptrace.startsolid;
137 if (cliptrace.startsolid || cliptrace.fraction < 1)
138 cliptrace.ent = prog->edicts;
139 if (type == MOVE_WORLDONLY)
142 if (type == MOVE_MISSILE)
144 // LordHavoc: modified this, was = -15, now -= 15
145 for (i = 0;i < 3;i++)
152 // create the bounding box of the entire move
153 for (i = 0;i < 3;i++)
155 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
156 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
159 // debug override to test against everything
160 if (sv_debugmove.integer)
162 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
163 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
166 // if the passedict is world, make it NULL (to avoid two checks each time)
167 if (passedict == prog->edicts)
169 // precalculate prog value for passedict for comparisons
170 passedictprog = PRVM_EDICT_TO_PROG(passedict);
171 // precalculate passedict's owner edict pointer for comparisons
172 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
175 // because this uses World_EntitiestoBox, we know all entity boxes overlap
176 // the clip region, so we can skip culling checks in the loop below
177 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
178 if (numtouchedicts > MAX_EDICTS)
180 // this never happens
181 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
182 numtouchedicts = MAX_EDICTS;
184 for (i = 0;i < numtouchedicts;i++)
186 touch = touchedicts[i];
188 if (touch->fields.server->solid < SOLID_BBOX)
190 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
195 // don't clip against self
196 if (passedict == touch)
198 // don't clip owned entities against owner
199 if (traceowner == touch)
201 // don't clip owner against owned entities
202 if (passedictprog == touch->fields.server->owner)
204 // don't clip points against points (they can't collide)
205 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
209 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
211 // might interact, so do an exact clip
213 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
215 model = SV_GetModelFromEdict(touch);
216 pitchsign = SV_GetPitchSign(touch);
219 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
221 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
222 Matrix4x4_Invert_Simple(&imatrix, &matrix);
223 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
224 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
225 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
226 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
227 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
229 Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
231 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
243 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
244 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
246 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
249 int i, bodysupercontents;
252 prvm_edict_t *traceowner, *touch;
254 // bounding box of entire move area
255 vec3_t clipboxmins, clipboxmaxs;
256 // size when clipping against monsters
257 vec3_t clipmins2, clipmaxs2;
258 // start and end origin of move
259 vec3_t clipstart, clipend;
262 // matrices to transform into/out of other entity's space
263 matrix4x4_t matrix, imatrix;
264 // model of other entity
266 // list of entities to test for collisions
268 static prvm_edict_t *touchedicts[MAX_EDICTS];
269 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
273 if (VectorCompare(start, pEnd))
274 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
276 if(collision_endposnudge.value > 0)
278 // TRICK: make the trace 1 qu longer!
279 VectorSubtract(pEnd, start, end);
280 len = VectorNormalizeLength(end);
281 VectorMA(pEnd, collision_endposnudge.value, end, end);
284 VectorCopy(pEnd, end);
286 if (VectorCompare(start, end))
287 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
290 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
292 VectorCopy(start, clipstart);
293 VectorCopy(end, clipend);
294 VectorClear(clipmins2);
295 VectorClear(clipmaxs2);
296 #if COLLISIONPARANOID >= 3
297 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
301 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask, false);
302 cliptrace.bmodelstartsolid = cliptrace.startsolid;
303 if (cliptrace.startsolid || cliptrace.fraction < 1)
304 cliptrace.ent = prog->edicts;
305 if (type == MOVE_WORLDONLY)
308 if (type == MOVE_MISSILE)
310 // LordHavoc: modified this, was = -15, now -= 15
311 for (i = 0;i < 3;i++)
318 // create the bounding box of the entire move
319 for (i = 0;i < 3;i++)
321 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
322 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
325 // debug override to test against everything
326 if (sv_debugmove.integer)
328 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
329 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
332 // if the passedict is world, make it NULL (to avoid two checks each time)
333 if (passedict == prog->edicts)
335 // precalculate prog value for passedict for comparisons
336 passedictprog = PRVM_EDICT_TO_PROG(passedict);
337 // precalculate passedict's owner edict pointer for comparisons
338 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
341 // because this uses World_EntitiestoBox, we know all entity boxes overlap
342 // the clip region, so we can skip culling checks in the loop below
343 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
344 if (numtouchedicts > MAX_EDICTS)
346 // this never happens
347 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
348 numtouchedicts = MAX_EDICTS;
350 for (i = 0;i < numtouchedicts;i++)
352 touch = touchedicts[i];
354 if (touch->fields.server->solid < SOLID_BBOX)
356 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
361 // don't clip against self
362 if (passedict == touch)
364 // don't clip owned entities against owner
365 if (traceowner == touch)
367 // don't clip owner against owned entities
368 if (passedictprog == touch->fields.server->owner)
370 // don't clip points against points (they can't collide)
371 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
375 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
377 // might interact, so do an exact clip
379 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
381 model = SV_GetModelFromEdict(touch);
382 pitchsign = SV_GetPitchSign(touch);
385 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
387 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
388 Matrix4x4_Invert_Simple(&imatrix, &matrix);
389 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
390 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
391 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
392 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
393 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
395 Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask, false);
397 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
401 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
402 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
403 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
413 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
414 #if COLLISIONPARANOID >= 1
415 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)
417 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)
420 #if COLLISIONPARANOID >= 1
421 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)
423 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)
427 vec3_t hullmins, hullmaxs;
428 int i, bodysupercontents;
432 prvm_edict_t *traceowner, *touch;
434 // bounding box of entire move area
435 vec3_t clipboxmins, clipboxmaxs;
436 // size of the moving object
437 vec3_t clipmins, clipmaxs;
438 // size when clipping against monsters
439 vec3_t clipmins2, clipmaxs2;
440 // start and end origin of move
441 vec3_t clipstart, clipend;
444 // matrices to transform into/out of other entity's space
445 matrix4x4_t matrix, imatrix;
446 // model of other entity
448 // list of entities to test for collisions
450 static prvm_edict_t *touchedicts[MAX_EDICTS];
451 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
455 if (VectorCompare(mins, maxs))
457 vec3_t shiftstart, shiftend;
458 VectorAdd(start, mins, shiftstart);
459 VectorAdd(pEnd, mins, shiftend);
460 if (VectorCompare(start, pEnd))
461 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
463 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
464 VectorSubtract(trace.endpos, mins, trace.endpos);
468 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
470 // TRICK: make the trace 1 qu longer!
471 VectorSubtract(pEnd, start, end);
472 len = VectorNormalizeLength(end);
473 VectorMA(pEnd, collision_endposnudge.value, end, end);
476 VectorCopy(pEnd, end);
478 if (VectorCompare(mins, maxs))
480 vec3_t shiftstart, shiftend;
481 VectorAdd(start, mins, shiftstart);
482 VectorAdd(end, mins, shiftend);
483 if (VectorCompare(start, end))
484 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
486 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
487 VectorSubtract(trace.endpos, mins, trace.endpos);
492 VectorCopy(start, clipstart);
493 VectorCopy(end, clipend);
494 VectorCopy(mins, clipmins);
495 VectorCopy(maxs, clipmaxs);
496 VectorCopy(mins, clipmins2);
497 VectorCopy(maxs, clipmaxs2);
498 #if COLLISIONPARANOID >= 3
499 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
503 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
504 cliptrace.bmodelstartsolid = cliptrace.startsolid;
505 if (cliptrace.startsolid || cliptrace.fraction < 1)
506 cliptrace.ent = prog->edicts;
507 if (type == MOVE_WORLDONLY)
510 if (type == MOVE_MISSILE)
512 // LordHavoc: modified this, was = -15, now -= 15
513 for (i = 0;i < 3;i++)
520 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
521 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
522 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
525 VectorCopy(clipmins, hullmins);
526 VectorCopy(clipmaxs, hullmaxs);
529 // create the bounding box of the entire move
530 for (i = 0;i < 3;i++)
532 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
533 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
536 // debug override to test against everything
537 if (sv_debugmove.integer)
539 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
540 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
543 // if the passedict is world, make it NULL (to avoid two checks each time)
544 if (passedict == prog->edicts)
546 // precalculate prog value for passedict for comparisons
547 passedictprog = PRVM_EDICT_TO_PROG(passedict);
548 // figure out whether this is a point trace for comparisons
549 pointtrace = VectorCompare(clipmins, clipmaxs);
550 // precalculate passedict's owner edict pointer for comparisons
551 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
554 // because this uses World_EntitiestoBox, we know all entity boxes overlap
555 // the clip region, so we can skip culling checks in the loop below
556 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
557 if (numtouchedicts > MAX_EDICTS)
559 // this never happens
560 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
561 numtouchedicts = MAX_EDICTS;
563 for (i = 0;i < numtouchedicts;i++)
565 touch = touchedicts[i];
567 if (touch->fields.server->solid < SOLID_BBOX)
569 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
574 // don't clip against self
575 if (passedict == touch)
577 // don't clip owned entities against owner
578 if (traceowner == touch)
580 // don't clip owner against owned entities
581 if (passedictprog == touch->fields.server->owner)
583 // don't clip points against points (they can't collide)
584 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
588 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
590 // might interact, so do an exact clip
592 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
594 model = SV_GetModelFromEdict(touch);
595 pitchsign = SV_GetPitchSign(touch);
598 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
600 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
601 Matrix4x4_Invert_Simple(&imatrix, &matrix);
602 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
603 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
604 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
605 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
606 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
608 Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
610 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
614 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
615 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
616 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
621 #if COLLISIONPARANOID >= 1
622 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)
627 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
630 VectorCopy(trace.endpos, temp);
631 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
632 #if COLLISIONPARANOID < 3
633 if (trace.startsolid || endstuck)
635 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, passedict->fields.server->origin[0], passedict->fields.server->origin[1], passedict->fields.server->origin[2], end[0] - passedict->fields.server->origin[0], end[1] - passedict->fields.server->origin[1], end[2] - passedict->fields.server->origin[2], trace.fraction, trace.endpos[0] - passedict->fields.server->origin[0], trace.endpos[1] - passedict->fields.server->origin[1], trace.endpos[2] - passedict->fields.server->origin[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
641 int SV_PointSuperContents(const vec3_t point)
643 int supercontents = 0;
647 // matrices to transform into/out of other entity's space
648 matrix4x4_t matrix, imatrix;
649 // model of other entity
652 // list of entities to test for collisions
654 static prvm_edict_t *touchedicts[MAX_EDICTS];
656 // get world supercontents at this point
657 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
658 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
660 // if sv_gameplayfix_swiminbmodels is off we're done
661 if (!sv_gameplayfix_swiminbmodels.integer)
662 return supercontents;
664 // get list of entities at this point
665 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
666 if (numtouchedicts > MAX_EDICTS)
668 // this never happens
669 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
670 numtouchedicts = MAX_EDICTS;
672 for (i = 0;i < numtouchedicts;i++)
674 touch = touchedicts[i];
676 // we only care about SOLID_BSP for pointcontents
677 if (touch->fields.server->solid != SOLID_BSP)
680 // might interact, so do an exact clip
681 model = SV_GetModelFromEdict(touch);
682 if (!model || !model->PointSuperContents)
684 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
685 Matrix4x4_Invert_Simple(&imatrix, &matrix);
686 Matrix4x4_Transform(&imatrix, point, transformed);
687 frame = (int)touch->fields.server->frame;
688 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
691 return supercontents;
695 ===============================================================================
697 Linking entities into the world culling system
699 ===============================================================================
702 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
704 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
705 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
706 prog->globals.server->time = sv.time;
707 prog->globals.server->trace_allsolid = false;
708 prog->globals.server->trace_startsolid = false;
709 prog->globals.server->trace_fraction = 1;
710 prog->globals.server->trace_inwater = false;
711 prog->globals.server->trace_inopen = true;
712 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
713 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
714 prog->globals.server->trace_plane_dist = 0;
715 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
716 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
717 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
718 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
719 PRVM_serverglobalstring(trace_dphittexturename) = 0;
720 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
723 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
725 int i, numtouchedicts, old_self, old_other;
727 static prvm_edict_t *touchedicts[MAX_EDICTS];
729 if (ent == prog->edicts)
730 return; // don't add the world
732 if (ent->priv.server->free)
735 if (ent->fields.server->solid == SOLID_NOT)
738 // build a list of edicts to touch, because the link loop can be corrupted
739 // by IncreaseEdicts called during touch functions
740 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
741 if (numtouchedicts > MAX_EDICTS)
743 // this never happens
744 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
745 numtouchedicts = MAX_EDICTS;
748 old_self = prog->globals.server->self;
749 old_other = prog->globals.server->other;
750 for (i = 0;i < numtouchedicts;i++)
752 touch = touchedicts[i];
753 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
755 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
758 prog->globals.server->self = old_self;
759 prog->globals.server->other = old_other;
762 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
766 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
768 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
769 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
770 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
771 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];
772 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];
773 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
774 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];
775 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];
776 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
777 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];
778 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];
779 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
780 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
781 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
782 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
783 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
784 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
785 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
786 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
787 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
788 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
789 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
790 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
799 void SV_LinkEdict (prvm_edict_t *ent)
805 if (ent == prog->edicts)
806 return; // don't add the world
808 if (ent->priv.server->free)
811 modelindex = (int)ent->fields.server->modelindex;
812 if (modelindex < 0 || modelindex >= MAX_MODELS)
814 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
817 model = SV_GetModelByIndex(modelindex);
819 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
820 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
821 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
825 if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
827 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
828 // TODO special handling for spheres?
829 RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs);
830 VectorAdd(ent->fields.server->origin, mins, mins);
831 VectorAdd(ent->fields.server->origin, maxs, maxs);
833 else if (ent->fields.server->solid == SOLID_BSP)
837 if (!model->TraceBox)
838 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
840 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
842 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
843 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
845 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
847 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
848 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
852 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
853 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
858 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
859 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
860 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
865 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
866 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
870 // to make items easier to pick up and allow them to be grabbed off
871 // of shelves, the abs sizes are expanded
873 if ((int)ent->fields.server->flags & FL_ITEM)
884 // because movement is clipped an epsilon away from an actual edge,
885 // we must fully check even when bounding boxes don't quite touch
894 VectorCopy(mins, ent->fields.server->absmin);
895 VectorCopy(maxs, ent->fields.server->absmax);
897 World_LinkEdict(&sv.world, ent, mins, maxs);
901 ===============================================================================
905 ===============================================================================
910 SV_TestEntityPosition
912 returns true if the entity is in solid currently
915 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
920 contents = SV_GenericHitSuperContentsMask(ent);
921 VectorAdd(ent->fields.server->origin, offset, org);
922 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
923 if (trace.startsupercontents & contents)
927 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
929 // q1bsp/hlbsp use hulls and if the entity does not exactly match
930 // a hull size it is incorrectly tested, so this code tries to
931 // 'fix' it slightly...
932 // FIXME: this breaks entities larger than the hull size
935 VectorAdd(org, ent->fields.server->mins, m1);
936 VectorAdd(org, ent->fields.server->maxs, m2);
937 VectorSubtract(m2, m1, s);
938 #define EPSILON (1.0f / 32.0f)
939 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
940 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
941 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
942 for (i = 0;i < 8;i++)
944 v[0] = (i & 1) ? m2[0] : m1[0];
945 v[1] = (i & 2) ? m2[1] : m1[1];
946 v[2] = (i & 4) ? m2[2] : m1[2];
947 if (SV_PointSuperContents(v) & contents)
952 // if the trace found a better position for the entity, move it there
953 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
956 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
957 VectorCopy(trace.endpos, ent->fields.server->origin);
959 // verify if the endpos is REALLY outside solid
960 VectorCopy(trace.endpos, org);
961 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
963 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
965 VectorCopy(org, ent->fields.server->origin);
976 void SV_CheckAllEnts (void)
981 // see if any solid entities are inside the final position
982 check = PRVM_NEXT_EDICT(prog->edicts);
983 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
985 if (check->priv.server->free)
987 if (check->fields.server->movetype == MOVETYPE_PUSH
988 || check->fields.server->movetype == MOVETYPE_NONE
989 || check->fields.server->movetype == MOVETYPE_FOLLOW
990 || check->fields.server->movetype == MOVETYPE_NOCLIP)
993 if (SV_TestEntityPosition (check, vec3_origin))
994 Con_Print("entity in invalid position\n");
998 // DRESK - Support for Entity Contents Transition Event
1001 SV_CheckContentsTransition
1003 returns true if entity had a valid contentstransition function call
1006 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
1008 int bValidFunctionCall;
1010 // Default Valid Function Call to False
1011 bValidFunctionCall = false;
1013 if(ent->fields.server->watertype != nContents)
1014 { // Changed Contents
1015 // Acquire Contents Transition Function from QC
1016 if(PRVM_serveredictfunction(ent, contentstransition))
1017 { // Valid Function; Execute
1018 // Assign Valid Function
1019 bValidFunctionCall = true;
1020 // Prepare Parameters (Original Contents, New Contents)
1021 // Original Contents
1022 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
1024 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1026 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1027 // Execute VM Function
1028 PRVM_ExecuteProgram(PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
1032 // Return if Function Call was Valid
1033 return bValidFunctionCall;
1042 void SV_CheckVelocity (prvm_edict_t *ent)
1050 for (i=0 ; i<3 ; i++)
1052 if (IS_NAN(ent->fields.server->velocity[i]))
1054 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1055 ent->fields.server->velocity[i] = 0;
1057 if (IS_NAN(ent->fields.server->origin[i]))
1059 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1060 ent->fields.server->origin[i] = 0;
1064 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1065 // player_run/player_stand1 does not horribly malfunction if the
1066 // velocity becomes a denormalized float
1067 if (VectorLength2(ent->fields.server->velocity) < 0.0001)
1068 VectorClear(ent->fields.server->velocity);
1070 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1071 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1072 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1074 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1075 ent->fields.server->velocity[0] *= wishspeed;
1076 ent->fields.server->velocity[1] *= wishspeed;
1077 ent->fields.server->velocity[2] *= wishspeed;
1085 Runs thinking code if time. There is some play in the exact time the think
1086 function will be called, because it is called before any movement is done
1087 in a frame. Not used for pushmove objects, because they must be exact.
1088 Returns false if the entity removed itself.
1091 qboolean SV_RunThink (prvm_edict_t *ent)
1095 // don't let things stay in the past.
1096 // it is possible to start that way by a trigger with a local time.
1097 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1100 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1102 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1103 ent->fields.server->nextthink = 0;
1104 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1105 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1106 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1107 // mods often set nextthink to time to cause a think every frame,
1108 // we don't want to loop in that case, so exit if the new nextthink is
1109 // <= the time the qc was told, also exit if it is past the end of the
1111 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1114 return !ent->priv.server->free;
1121 Two entities have touched, so run their touch functions
1124 extern void VM_SetTraceGlobals(const trace_t *trace);
1125 extern sizebuf_t vm_tempstringsbuf;
1126 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
1128 int restorevm_tempstringsbuf_cursize;
1129 int old_self, old_other;
1130 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1132 old_self = prog->globals.server->self;
1133 old_other = prog->globals.server->other;
1134 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1136 VM_SetTraceGlobals(trace);
1138 prog->globals.server->time = sv.time;
1139 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1141 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1142 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1143 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1146 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1148 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1149 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1150 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1151 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1152 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1153 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1154 PRVM_serverglobalfloat(trace_dpstartcontents) = 0;
1155 PRVM_serverglobalfloat(trace_dphitcontents) = 0;
1156 PRVM_serverglobalfloat(trace_dphitq3surfaceflags) = 0;
1157 PRVM_serverglobalstring(trace_dphittexturename) = 0;
1158 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1161 prog->globals.server->self = old_self;
1162 prog->globals.server->other = old_other;
1163 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1171 Slide off of the impacting object
1172 returns the blocked flags (1 = floor, 2 = step / wall)
1175 #define STOP_EPSILON 0.1
1176 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1181 backoff = -DotProduct (in, normal) * overbounce;
1182 VectorMA(in, backoff, normal, out);
1184 for (i = 0;i < 3;i++)
1185 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1194 The basic solid body movement clip that slides along multiple planes
1195 Returns the clipflags if the velocity was modified (hit something solid)
1199 8 = teleported by touch method
1200 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1203 static float SV_Gravity (prvm_edict_t *ent);
1204 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1205 #define MAX_CLIP_PLANES 5
1206 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1208 int blocked, bumpcount;
1209 int i, j, numplanes;
1210 float d, time_left, gravity;
1211 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1220 if(sv_gameplayfix_nogravityonground.integer)
1221 if((int)ent->fields.server->flags & FL_ONGROUND)
1222 applygravity = false;
1226 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1228 gravity = SV_Gravity(ent) * 0.5f;
1229 ent->fields.server->velocity[2] -= gravity;
1233 applygravity = false;
1234 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1238 VectorCopy(ent->fields.server->velocity, original_velocity);
1239 VectorCopy(ent->fields.server->velocity, primal_velocity);
1242 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1244 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1247 VectorScale(ent->fields.server->velocity, time_left, push);
1248 if(!SV_PushEntity(&trace, ent, push, false, false))
1250 // we got teleported by a touch function
1251 // let's abort the move
1256 if (trace.fraction == 1)
1258 if (trace.plane.normal[2])
1260 if (trace.plane.normal[2] > 0.7)
1267 Con_Printf ("SV_FlyMove: !trace.ent");
1268 trace.ent = prog->edicts;
1271 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1272 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1275 else if (stepheight)
1277 // step - handle it immediately
1283 //Con_Printf("step %f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1284 VectorSet(steppush, 0, 0, stepheight);
1285 VectorCopy(ent->fields.server->origin, org);
1286 if(!SV_PushEntity(&steptrace, ent, steppush, false, false))
1291 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1292 if(!SV_PushEntity(&steptrace2, ent, push, false, false))
1297 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1298 VectorSet(steppush, 0, 0, org[2] - ent->fields.server->origin[2]);
1299 if(!SV_PushEntity(&steptrace3, ent, steppush, false, false))
1304 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1305 // accept the new position if it made some progress...
1306 if (fabs(ent->fields.server->origin[0] - org[0]) >= 0.03125 || fabs(ent->fields.server->origin[1] - org[1]) >= 0.03125)
1308 //Con_Printf("accepted (delta %f %f %f)\n", ent->fields.server->origin[0] - org[0], ent->fields.server->origin[1] - org[1], ent->fields.server->origin[2] - org[2]);
1310 VectorCopy(ent->fields.server->origin, trace.endpos);
1311 time_left *= 1 - trace.fraction;
1317 //Con_Printf("REJECTED (delta %f %f %f)\n", ent->fields.server->origin[0] - org[0], ent->fields.server->origin[1] - org[1], ent->fields.server->origin[2] - org[2]);
1318 VectorCopy(org, ent->fields.server->origin);
1323 // step - return it to caller
1325 // save the trace for player extrafriction
1327 VectorCopy(trace.plane.normal, stepnormal);
1329 if (trace.fraction >= 0.001)
1331 // actually covered some distance
1332 VectorCopy(ent->fields.server->velocity, original_velocity);
1336 time_left *= 1 - trace.fraction;
1338 // clipped to another plane
1339 if (numplanes >= MAX_CLIP_PLANES)
1341 // this shouldn't really happen
1342 VectorClear(ent->fields.server->velocity);
1348 for (i = 0;i < numplanes;i++)
1349 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1353 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1358 VectorCopy(trace.plane.normal, planes[numplanes]);
1361 // modify original_velocity so it parallels all of the clip planes
1362 for (i = 0;i < numplanes;i++)
1364 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1365 for (j = 0;j < numplanes;j++)
1370 if (DotProduct(new_velocity, planes[j]) < 0)
1380 // go along this plane
1381 VectorCopy(new_velocity, ent->fields.server->velocity);
1385 // go along the crease
1388 VectorClear(ent->fields.server->velocity);
1392 CrossProduct(planes[0], planes[1], dir);
1393 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1394 VectorNormalize(dir);
1395 d = DotProduct(dir, ent->fields.server->velocity);
1396 VectorScale(dir, d, ent->fields.server->velocity);
1399 // if current velocity is against the original velocity,
1400 // stop dead to avoid tiny occilations in sloping corners
1401 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1403 VectorClear(ent->fields.server->velocity);
1408 //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2]);
1411 if ((blocked & 1) == 0 && bumpcount > 1)
1413 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1414 // flag ONGROUND if there's ground under it
1415 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1419 // LordHavoc: this came from QW and allows you to get out of water more easily
1420 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1421 VectorCopy(primal_velocity, ent->fields.server->velocity);
1422 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1423 ent->fields.server->velocity[2] -= gravity;
1433 static float SV_Gravity (prvm_edict_t *ent)
1437 ent_gravity = PRVM_serveredictfloat(ent, gravity);
1440 return ent_gravity * sv_gravity.value * sv.frametime;
1445 ===============================================================================
1449 ===============================================================================
1456 Does not change the entities velocity at all
1457 The trace struct is filled with the trace that has been done.
1458 Returns true if the push did not result in the entity being teleported by QC code.
1461 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1465 vec3_t original, original_velocity;
1468 VectorCopy(ent->fields.server->origin, original);
1469 VectorAdd (ent->fields.server->origin, push, end);
1471 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1472 type = MOVE_MISSILE;
1473 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1474 type = MOVE_NOMONSTERS; // only clip against bmodels
1478 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1480 while (trace->bmodelstartsolid && sv_gameplayfix_nudgeoutofsolid.integer)
1482 vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
1483 VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
1484 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1488 VectorCopy(original, ent->fields.server->origin);
1492 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1495 VectorCopy (trace->endpos, ent->fields.server->origin);
1497 VectorCopy(ent->fields.server->origin, original);
1498 VectorCopy(ent->fields.server->velocity, original_velocity);
1503 if(!trace->startsolid)
1504 if(SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, type, ent, SV_GenericHitSuperContentsMask(ent)).startsolid)
1506 Con_Printf("something eeeeevil happened\n");
1511 SV_LinkEdict_TouchAreaGrid(ent);
1513 if((ent->fields.server->solid >= SOLID_TRIGGER && trace->ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace->ent))))
1514 SV_Impact (ent, trace);
1516 return VectorCompare(ent->fields.server->origin, original) && VectorCompare(ent->fields.server->velocity, original_velocity);
1526 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1529 int pusherowner, pusherprog;
1532 float savesolid, movetime2, pushltime;
1533 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1535 int numcheckentities;
1536 static prvm_edict_t *checkentities[MAX_EDICTS];
1537 dp_model_t *pushermodel;
1538 trace_t trace, trace2;
1539 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1540 static unsigned short moved_edicts[MAX_EDICTS];
1542 if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
1544 pusher->fields.server->ltime += movetime;
1548 switch ((int) pusher->fields.server->solid)
1550 // LordHavoc: valid pusher types
1553 case SOLID_SLIDEBOX:
1554 case SOLID_CORPSE: // LordHavoc: this would be weird...
1556 // LordHavoc: no collisions
1559 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1560 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1561 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1562 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1563 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1564 pusher->fields.server->ltime += movetime;
1565 SV_LinkEdict(pusher);
1568 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1571 index = (int) pusher->fields.server->modelindex;
1572 if (index < 1 || index >= MAX_MODELS)
1574 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1577 pushermodel = SV_GetModelByIndex(index);
1578 pusherowner = pusher->fields.server->owner;
1579 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1581 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1583 movetime2 = movetime;
1584 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1585 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1586 if (moveangle[0] || moveangle[2])
1588 for (i = 0;i < 3;i++)
1592 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1593 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1597 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1598 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1602 else if (moveangle[1])
1604 for (i = 0;i < 3;i++)
1608 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1609 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1613 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1614 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1620 for (i = 0;i < 3;i++)
1624 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1625 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1629 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1630 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1635 VectorNegate (moveangle, a);
1636 AngleVectorsFLU (a, forward, left, up);
1638 VectorCopy (pusher->fields.server->origin, pushorig);
1639 VectorCopy (pusher->fields.server->angles, pushang);
1640 pushltime = pusher->fields.server->ltime;
1642 // move the pusher to its final position
1644 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1645 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1646 pusher->fields.server->ltime += movetime;
1647 SV_LinkEdict(pusher);
1649 pushermodel = SV_GetModelFromEdict(pusher);
1650 Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, pusher->fields.server->origin[0], pusher->fields.server->origin[1], pusher->fields.server->origin[2], pusher->fields.server->angles[0], pusher->fields.server->angles[1], pusher->fields.server->angles[2], 1);
1651 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1653 savesolid = pusher->fields.server->solid;
1655 // see if any solid entities are inside the final position
1658 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1659 for (e = 0;e < numcheckentities;e++)
1661 prvm_edict_t *check = checkentities[e];
1662 int movetype = (int)check->fields.server->movetype;
1667 case MOVETYPE_FOLLOW:
1668 case MOVETYPE_NOCLIP:
1669 case MOVETYPE_FAKEPUSH:
1675 if (check->fields.server->owner == pusherprog)
1678 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1681 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1683 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1684 check->priv.server->waterposition_forceupdate = true;
1686 checkcontents = SV_GenericHitSuperContentsMask(check);
1688 // if the entity is standing on the pusher, it will definitely be moved
1689 // if the entity is not standing on the pusher, but is in the pusher's
1690 // final position, move it
1691 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1693 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1694 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1695 if (!trace.startsolid)
1697 //Con_Printf("- not in solid\n");
1705 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1706 org2[0] = DotProduct (org, forward);
1707 org2[1] = DotProduct (org, left);
1708 org2[2] = DotProduct (org, up);
1709 VectorSubtract (org2, org, move);
1710 VectorAdd (move, move1, move);
1713 VectorCopy (move1, move);
1715 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1717 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1718 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1719 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1721 // physics objects need better collisions than this code can do
1722 if (movetype == MOVETYPE_PHYSICS)
1724 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1725 SV_LinkEdict(check);
1726 SV_LinkEdict_TouchAreaGrid(check);
1730 // try moving the contacted entity
1731 pusher->fields.server->solid = SOLID_NOT;
1732 if(!SV_PushEntity (&trace, check, move, true, true))
1734 // entity "check" got teleported
1735 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1736 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1737 continue; // pushed enough
1739 // FIXME: turn players specially
1740 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1741 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1742 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1744 // this trace.fraction < 1 check causes items to fall off of pushers
1745 // if they pass under or through a wall
1746 // the groundentity check causes items to fall off of ledges
1747 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1748 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1750 // if it is still inside the pusher, block
1751 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1752 if (trace.startsolid)
1754 // try moving the contacted entity a tiny bit further to account for precision errors
1756 pusher->fields.server->solid = SOLID_NOT;
1757 VectorScale(move, 1.1, move2);
1758 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1759 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1760 if(!SV_PushEntity (&trace2, check, move2, true, true))
1762 // entity "check" got teleported
1765 pusher->fields.server->solid = savesolid;
1766 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1767 if (trace.startsolid)
1769 // try moving the contacted entity a tiny bit less to account for precision errors
1770 pusher->fields.server->solid = SOLID_NOT;
1771 VectorScale(move, 0.9, move2);
1772 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1773 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1774 if(!SV_PushEntity (&trace2, check, move2, true, true))
1776 // entity "check" got teleported
1779 pusher->fields.server->solid = savesolid;
1780 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1781 if (trace.startsolid)
1783 // still inside pusher, so it's really blocked
1786 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1788 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1791 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1792 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1796 VectorCopy (pushorig, pusher->fields.server->origin);
1797 VectorCopy (pushang, pusher->fields.server->angles);
1798 pusher->fields.server->ltime = pushltime;
1799 SV_LinkEdict(pusher);
1801 // move back any entities we already moved
1802 for (i = 0;i < num_moved;i++)
1804 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1805 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1806 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1810 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1811 if (pusher->fields.server->blocked)
1813 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1814 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1815 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1822 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1823 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1824 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1833 void SV_Physics_Pusher (prvm_edict_t *ent)
1835 float thinktime, oldltime, movetime;
1837 oldltime = ent->fields.server->ltime;
1839 thinktime = ent->fields.server->nextthink;
1840 if (thinktime < ent->fields.server->ltime + sv.frametime)
1842 movetime = thinktime - ent->fields.server->ltime;
1847 movetime = sv.frametime;
1850 // advances ent->fields.server->ltime if not blocked
1851 SV_PushMove (ent, movetime);
1853 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1855 ent->fields.server->nextthink = 0;
1856 prog->globals.server->time = sv.time;
1857 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1858 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1859 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1865 ===============================================================================
1869 ===============================================================================
1872 static float unstickoffsets[] =
1874 // poutting -/+z changes first as they are least weird
1889 typedef enum unstickresult_e
1897 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1901 // if not stuck in a bmodel, just return
1902 if (!SV_TestEntityPosition(ent, vec3_origin))
1903 return UNSTICK_GOOD;
1905 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1907 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1909 VectorCopy(unstickoffsets + i, offset);
1911 //SV_LinkEdict_TouchAreaGrid(ent);
1912 return UNSTICK_UNSTUCK;
1916 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1917 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1919 for(i = 2; i <= maxunstick; ++i)
1921 VectorClear(offset);
1923 if (!SV_TestEntityPosition(ent, offset))
1926 //SV_LinkEdict_TouchAreaGrid(ent);
1927 return UNSTICK_UNSTUCK;
1930 if (!SV_TestEntityPosition(ent, offset))
1933 //SV_LinkEdict_TouchAreaGrid(ent);
1934 return UNSTICK_UNSTUCK;
1938 return UNSTICK_STUCK;
1941 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1944 switch(SV_UnstickEntityReturnOffset(ent, offset))
1948 case UNSTICK_UNSTUCK:
1949 Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), offset[0], offset[1], offset[2]);
1952 if (developer_extra.integer)
1953 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1956 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1965 This is a big hack to try and fix the rare case of getting stuck in the world
1969 void SV_CheckStuck (prvm_edict_t *ent)
1973 switch(SV_UnstickEntityReturnOffset(ent, offset))
1976 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1978 case UNSTICK_UNSTUCK:
1979 Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), offset[0], offset[1], offset[2]);
1982 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1983 if (!SV_TestEntityPosition(ent, offset))
1985 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1987 //SV_LinkEdict_TouchAreaGrid(ent);
1990 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1993 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2003 qboolean SV_CheckWater (prvm_edict_t *ent)
2006 int nNativeContents;
2009 point[0] = ent->fields.server->origin[0];
2010 point[1] = ent->fields.server->origin[1];
2011 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2013 // DRESK - Support for Entity Contents Transition Event
2014 // NOTE: Some logic needed to be slightly re-ordered
2015 // to not affect performance and allow for the feature.
2017 // Acquire Super Contents Prior to Resets
2018 cont = SV_PointSuperContents(point);
2019 // Acquire Native Contents Here
2020 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2022 // DRESK - Support for Entity Contents Transition Event
2023 if(ent->fields.server->watertype)
2024 // Entity did NOT Spawn; Check
2025 SV_CheckContentsTransition(ent, nNativeContents);
2028 ent->fields.server->waterlevel = 0;
2029 ent->fields.server->watertype = CONTENTS_EMPTY;
2030 cont = SV_PointSuperContents(point);
2031 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2033 ent->fields.server->watertype = nNativeContents;
2034 ent->fields.server->waterlevel = 1;
2035 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2036 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2038 ent->fields.server->waterlevel = 2;
2039 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2040 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2041 ent->fields.server->waterlevel = 3;
2045 return ent->fields.server->waterlevel > 1;
2054 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2057 vec3_t forward, into, side;
2059 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2060 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2062 // cut the tangential velocity
2063 i = DotProduct (stepnormal, ent->fields.server->velocity);
2064 VectorScale (stepnormal, i, into);
2065 VectorSubtract (ent->fields.server->velocity, into, side);
2066 ent->fields.server->velocity[0] = side[0] * (1 + d);
2067 ent->fields.server->velocity[1] = side[1] * (1 + d);
2073 =====================
2076 Player has come to a dead stop, possibly due to the problem with limited
2077 float precision at some angle joins in the BSP hull.
2079 Try fixing by pushing one pixel in each direction.
2081 This is a hack, but in the interest of good gameplay...
2082 ======================
2084 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2089 VectorCopy (ent->fields.server->origin, oldorg);
2092 for (i=0 ; i<8 ; i++)
2094 // try pushing a little in an axial direction
2097 case 0: dir[0] = 2; dir[1] = 0; break;
2098 case 1: dir[0] = 0; dir[1] = 2; break;
2099 case 2: dir[0] = -2; dir[1] = 0; break;
2100 case 3: dir[0] = 0; dir[1] = -2; break;
2101 case 4: dir[0] = 2; dir[1] = 2; break;
2102 case 5: dir[0] = -2; dir[1] = 2; break;
2103 case 6: dir[0] = 2; dir[1] = -2; break;
2104 case 7: dir[0] = -2; dir[1] = -2; break;
2107 SV_PushEntity (&trace, ent, dir, false, true);
2109 // retry the original move
2110 ent->fields.server->velocity[0] = oldvel[0];
2111 ent->fields.server->velocity[1] = oldvel[1];
2112 ent->fields.server->velocity[2] = 0;
2113 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2115 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2116 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2118 Con_DPrint("TryUnstick - success.\n");
2122 // go back to the original pos and try again
2123 VectorCopy (oldorg, ent->fields.server->origin);
2127 VectorClear (ent->fields.server->velocity);
2128 Con_DPrint("TryUnstick - failure.\n");
2134 =====================
2137 Only used by players
2138 ======================
2140 void SV_WalkMove (prvm_edict_t *ent)
2144 //int originalmove_clip;
2145 int originalmove_flags;
2146 int originalmove_groundentity;
2147 int hitsupercontentsmask;
2149 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2150 trace_t downtrace, trace;
2151 qboolean applygravity;
2153 // if frametime is 0 (due to client sending the same timestamp twice),
2155 if (sv.frametime <= 0)
2158 SV_CheckStuck (ent);
2160 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2162 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2164 SV_CheckVelocity(ent);
2166 // do a regular slide move unless it looks like you ran into a step
2167 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2169 VectorCopy (ent->fields.server->origin, start_origin);
2170 VectorCopy (ent->fields.server->velocity, start_velocity);
2172 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2174 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2177 // only try this if there was no floor in the way in the trace (no,
2178 // this check seems to be not REALLY necessary, because if clip & 1,
2179 // our trace will hit that thing too)
2180 VectorSet(upmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + 1);
2181 VectorSet(downmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
2182 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
2183 type = MOVE_MISSILE;
2184 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
2185 type = MOVE_NOMONSTERS; // only clip against bmodels
2188 trace = SV_TraceBox(upmove, ent->fields.server->mins, ent->fields.server->maxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2189 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2190 clip |= 1; // but we HAVE found a floor
2193 // if the move did not hit the ground at any point, we're not on ground
2195 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2197 SV_CheckVelocity(ent);
2199 SV_LinkEdict_TouchAreaGrid(ent);
2201 if(clip & 8) // teleport
2204 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2207 if (sv_nostep.integer)
2210 VectorCopy(ent->fields.server->origin, originalmove_origin);
2211 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2212 //originalmove_clip = clip;
2213 originalmove_flags = (int)ent->fields.server->flags;
2214 originalmove_groundentity = ent->fields.server->groundentity;
2216 // if move didn't block on a step, return
2219 // if move was not trying to move into the step, return
2220 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2223 if (ent->fields.server->movetype != MOVETYPE_FLY)
2225 // return if gibbed by a trigger
2226 if (ent->fields.server->movetype != MOVETYPE_WALK)
2229 // only step up while jumping if that is enabled
2230 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2231 if (!oldonground && ent->fields.server->waterlevel == 0)
2235 // try moving up and forward to go up a step
2236 // back to start pos
2237 VectorCopy (start_origin, ent->fields.server->origin);
2238 VectorCopy (start_velocity, ent->fields.server->velocity);
2241 VectorClear (upmove);
2242 upmove[2] = sv_stepheight.value;
2243 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2245 // we got teleported when upstepping... must abort the move
2250 ent->fields.server->velocity[2] = 0;
2251 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2252 ent->fields.server->velocity[2] += start_velocity[2];
2255 // we got teleported when upstepping... must abort the move
2256 // note that z velocity handling may not be what QC expects here, but we cannot help it
2260 SV_CheckVelocity(ent);
2262 SV_LinkEdict_TouchAreaGrid(ent);
2264 // check for stuckness, possibly due to the limited precision of floats
2265 // in the clipping hulls
2267 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2268 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2270 //Con_Printf("wall\n");
2271 // stepping up didn't make any progress, revert to original move
2272 VectorCopy(originalmove_origin, ent->fields.server->origin);
2273 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2274 //clip = originalmove_clip;
2275 ent->fields.server->flags = originalmove_flags;
2276 ent->fields.server->groundentity = originalmove_groundentity;
2277 // now try to unstick if needed
2278 //clip = SV_TryUnstick (ent, oldvel);
2282 //Con_Printf("step - ");
2284 // extra friction based on view angle
2285 if (clip & 2 && sv_wallfriction.integer)
2286 SV_WallFriction (ent, stepnormal);
2288 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2289 else if (!sv_gameplayfix_stepdown.integer || ent->fields.server->waterlevel >= 3 || start_velocity[2] >= (1.0 / 32.0) || !oldonground || ((int)ent->fields.server->flags & FL_ONGROUND))
2293 VectorClear (downmove);
2294 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2295 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2297 // we got teleported when downstepping... must abort the move
2301 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2303 // this has been disabled so that you can't jump when you are stepping
2304 // up while already jumping (also known as the Quake2 double jump bug)
2306 // LordHavoc: disabled this check so you can walk on monsters/players
2307 //if (ent->fields.server->solid == SOLID_BSP)
2309 //Con_Printf("onground\n");
2310 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2311 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2317 //Con_Printf("slope\n");
2318 // if the push down didn't end up on good ground, use the move without
2319 // the step up. This happens near wall / slope combinations, and can
2320 // cause the player to hop up higher on a slope too steep to climb
2321 VectorCopy(originalmove_origin, ent->fields.server->origin);
2322 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2323 //clip = originalmove_clip;
2324 ent->fields.server->flags = originalmove_flags;
2325 ent->fields.server->groundentity = originalmove_groundentity;
2328 SV_CheckVelocity(ent);
2330 SV_LinkEdict_TouchAreaGrid(ent);
2333 //============================================================================
2339 Entities that are "stuck" to another entity
2342 void SV_Physics_Follow (prvm_edict_t *ent)
2344 vec3_t vf, vr, vu, angles, v;
2348 if (!SV_RunThink (ent))
2351 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2352 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2353 if (e->fields.server->angles[0] == ent->fields.server->punchangle[0] && e->fields.server->angles[1] == ent->fields.server->punchangle[1] && e->fields.server->angles[2] == ent->fields.server->punchangle[2])
2355 // quick case for no rotation
2356 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2360 angles[0] = -ent->fields.server->punchangle[0];
2361 angles[1] = ent->fields.server->punchangle[1];
2362 angles[2] = ent->fields.server->punchangle[2];
2363 AngleVectors (angles, vf, vr, vu);
2364 v[0] = ent->fields.server->view_ofs[0] * vf[0] + ent->fields.server->view_ofs[1] * vr[0] + ent->fields.server->view_ofs[2] * vu[0];
2365 v[1] = ent->fields.server->view_ofs[0] * vf[1] + ent->fields.server->view_ofs[1] * vr[1] + ent->fields.server->view_ofs[2] * vu[1];
2366 v[2] = ent->fields.server->view_ofs[0] * vf[2] + ent->fields.server->view_ofs[1] * vr[2] + ent->fields.server->view_ofs[2] * vu[2];
2367 angles[0] = -e->fields.server->angles[0];
2368 angles[1] = e->fields.server->angles[1];
2369 angles[2] = e->fields.server->angles[2];
2370 AngleVectors (angles, vf, vr, vu);
2371 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2372 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2373 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2375 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2377 //SV_LinkEdict_TouchAreaGrid(ent);
2381 ==============================================================================
2385 ==============================================================================
2390 SV_CheckWaterTransition
2394 void SV_CheckWaterTransition (prvm_edict_t *ent)
2397 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2398 if (!ent->fields.server->watertype)
2400 // just spawned here
2401 ent->fields.server->watertype = cont;
2402 ent->fields.server->waterlevel = 1;
2406 // DRESK - Support for Entity Contents Transition Event
2407 // NOTE: Call here BEFORE updating the watertype below,
2408 // and suppress watersplash sound if a valid function
2409 // call was made to allow for custom "splash" sounds.
2410 if( !SV_CheckContentsTransition(ent, cont) )
2411 { // Contents Transition Function Invalid; Potentially Play Water Sound
2412 // check if the entity crossed into or out of water
2413 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2414 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2417 if (cont <= CONTENTS_WATER)
2419 ent->fields.server->watertype = cont;
2420 ent->fields.server->waterlevel = 1;
2424 ent->fields.server->watertype = CONTENTS_EMPTY;
2425 ent->fields.server->waterlevel = 0;
2433 Toss, bounce, and fly movement. When onground, do nothing.
2436 void SV_Physics_Toss (prvm_edict_t *ent)
2442 prvm_edict_t *groundentity;
2444 // if onground, return without moving
2445 if ((int)ent->fields.server->flags & FL_ONGROUND)
2447 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2448 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2450 // don't stick to ground if onground and moving upward
2451 ent->fields.server->flags -= FL_ONGROUND;
2453 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2455 // we can trust FL_ONGROUND if groundentity is world because it never moves
2458 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2460 // if ent was supported by a brush model on previous frame,
2461 // and groundentity is now freed, set groundentity to 0 (world)
2462 // which leaves it suspended in the air
2463 ent->fields.server->groundentity = 0;
2464 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2467 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2469 // don't slide if still touching the groundentity
2473 ent->priv.server->suspendedinairflag = false;
2475 SV_CheckVelocity (ent);
2478 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2479 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2482 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2484 movetime = sv.frametime;
2485 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2488 VectorScale (ent->fields.server->velocity, movetime, move);
2489 if(!SV_PushEntity (&trace, ent, move, true, true))
2490 return; // teleported
2491 if (ent->priv.server->free)
2493 if (trace.bmodelstartsolid)
2495 // try to unstick the entity
2496 SV_UnstickEntity(ent);
2497 if(!SV_PushEntity (&trace, ent, move, false, true))
2498 return; // teleported
2499 if (ent->priv.server->free)
2502 if (trace.fraction == 1)
2504 movetime *= 1 - min(1, trace.fraction);
2505 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2508 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2510 bouncefactor = 1.0f;
2512 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2513 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2515 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2517 float d, ent_gravity;
2521 bouncefactor = PRVM_serveredictfloat(ent, bouncefactor);
2523 bouncefactor = 0.5f;
2525 bouncestop = PRVM_serveredictfloat(ent, bouncestop);
2527 bouncestop = 60.0f / 800.0f;
2529 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2530 ent_gravity = PRVM_serveredictfloat(ent, gravity);
2533 // LordHavoc: fixed grenades not bouncing when fired down a slope
2534 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2536 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2537 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2539 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2540 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2541 VectorClear (ent->fields.server->velocity);
2542 VectorClear (ent->fields.server->avelocity);
2545 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2549 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2551 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2552 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2553 VectorClear (ent->fields.server->velocity);
2554 VectorClear (ent->fields.server->avelocity);
2557 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2562 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2563 if (trace.plane.normal[2] > 0.7)
2565 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2566 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2567 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2568 ent->priv.server->suspendedinairflag = true;
2569 VectorClear (ent->fields.server->velocity);
2570 VectorClear (ent->fields.server->avelocity);
2573 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2575 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2579 // check for in water
2580 SV_CheckWaterTransition (ent);
2584 ===============================================================================
2588 ===============================================================================
2595 Monsters freefall when they don't have a ground entity, otherwise
2596 all movement is done with discrete steps.
2598 This is also used for objects that have become still on the ground, but
2599 will fall if the floor is pulled out from under them.
2602 void SV_Physics_Step (prvm_edict_t *ent)
2604 int flags = (int)ent->fields.server->flags;
2607 // Backup Velocity in the event that movetypesteplandevent is called,
2608 // to provide a parameter with the entity's velocity at impact.
2609 vec3_t backupVelocity;
2610 VectorCopy(ent->fields.server->velocity, backupVelocity);
2611 // don't fall at all if fly/swim
2612 if (!(flags & (FL_FLY | FL_SWIM)))
2614 if (flags & FL_ONGROUND)
2616 // freefall if onground and moving upward
2617 // freefall if not standing on a world surface (it may be a lift or trap door)
2618 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2620 ent->fields.server->flags -= FL_ONGROUND;
2621 SV_CheckVelocity(ent);
2622 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2624 SV_LinkEdict_TouchAreaGrid(ent);
2625 ent->priv.server->waterposition_forceupdate = true;
2630 // freefall if not onground
2631 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2633 SV_CheckVelocity(ent);
2634 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2636 SV_LinkEdict_TouchAreaGrid(ent);
2639 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2641 // DRESK - Check for Entity Land Event Function
2642 if(PRVM_serveredictfunction(ent, movetypesteplandevent))
2643 { // Valid Function; Execute
2644 // Prepare Parameters
2645 // Assign Velocity at Impact
2646 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2647 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2648 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2650 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2651 // Execute VM Function
2652 PRVM_ExecuteProgram(PRVM_serveredictfunction(ent, movetypesteplandevent), "movetypesteplandevent: NULL function");
2655 // Check for Engine Landing Sound
2656 if(sv_sound_land.string)
2657 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2659 ent->priv.server->waterposition_forceupdate = true;
2664 if (!SV_RunThink(ent))
2667 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2669 ent->priv.server->waterposition_forceupdate = false;
2670 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2671 SV_CheckWaterTransition(ent);
2675 //============================================================================
2677 static void SV_Physics_Entity (prvm_edict_t *ent)
2679 // don't run think/move on newly spawned projectiles as it messes up
2680 // movement interpolation and rocket trails, and is inconsistent with
2681 // respect to entities spawned in the same frame
2682 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2683 // but if it spawns a lower numbered ent, it doesn't - this never moves
2684 // ents in the first frame regardless)
2685 qboolean runmove = ent->priv.server->move;
2686 ent->priv.server->move = true;
2687 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2689 switch ((int) ent->fields.server->movetype)
2692 case MOVETYPE_FAKEPUSH:
2693 SV_Physics_Pusher (ent);
2696 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2697 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2700 case MOVETYPE_FOLLOW:
2701 SV_Physics_Follow (ent);
2703 case MOVETYPE_NOCLIP:
2704 if (SV_RunThink(ent))
2707 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2708 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2713 SV_Physics_Step (ent);
2716 if (SV_RunThink (ent))
2720 case MOVETYPE_BOUNCE:
2721 case MOVETYPE_BOUNCEMISSILE:
2722 case MOVETYPE_FLYMISSILE:
2725 if (SV_RunThink (ent))
2726 SV_Physics_Toss (ent);
2728 case MOVETYPE_PHYSICS:
2729 if (SV_RunThink(ent))
2732 SV_LinkEdict_TouchAreaGrid(ent);
2736 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2741 void SV_Physics_ClientMove(void)
2744 ent = host_client->edict;
2746 // call player physics, this needs the proper frametime
2747 prog->globals.server->frametime = sv.frametime;
2750 // call standard client pre-think, with frametime = 0
2751 prog->globals.server->time = sv.time;
2752 prog->globals.server->frametime = 0;
2753 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2754 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2755 prog->globals.server->frametime = sv.frametime;
2757 // make sure the velocity is sane (not a NaN)
2758 SV_CheckVelocity(ent);
2760 // perform MOVETYPE_WALK behavior
2763 // call standard player post-think, with frametime = 0
2764 prog->globals.server->time = sv.time;
2765 prog->globals.server->frametime = 0;
2766 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2767 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2768 prog->globals.server->frametime = sv.frametime;
2770 if(ent->fields.server->fixangle)
2772 // angle fixing was requested by physics code...
2773 // so store the current angles for later use
2774 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2775 host_client->fixangle_angles_set = TRUE;
2777 // and clear fixangle for the next frame
2778 ent->fields.server->fixangle = 0;
2782 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2784 // don't do physics on disconnected clients, FrikBot relies on this
2785 if (!host_client->spawned)
2788 // make sure the velocity is sane (not a NaN)
2789 SV_CheckVelocity(ent);
2791 // don't run physics here if running asynchronously
2792 if (host_client->clmovement_inputtimeout <= 0)
2795 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2798 // make sure the velocity is still sane (not a NaN)
2799 SV_CheckVelocity(ent);
2801 // call standard client pre-think
2802 prog->globals.server->time = sv.time;
2803 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2804 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2806 // make sure the velocity is still sane (not a NaN)
2807 SV_CheckVelocity(ent);
2810 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2812 // don't do physics on disconnected clients, FrikBot relies on this
2813 if (!host_client->spawned)
2816 // make sure the velocity is sane (not a NaN)
2817 SV_CheckVelocity(ent);
2819 // call standard player post-think
2820 prog->globals.server->time = sv.time;
2821 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2822 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2824 // make sure the velocity is still sane (not a NaN)
2825 SV_CheckVelocity(ent);
2827 if(ent->fields.server->fixangle)
2829 // angle fixing was requested by physics code...
2830 // so store the current angles for later use
2831 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2832 host_client->fixangle_angles_set = TRUE;
2834 // and clear fixangle for the next frame
2835 ent->fields.server->fixangle = 0;
2838 // decrement the countdown variable used to decide when to go back to
2839 // synchronous physics
2840 if (host_client->clmovement_inputtimeout > sv.frametime)
2841 host_client->clmovement_inputtimeout -= sv.frametime;
2843 host_client->clmovement_inputtimeout = 0;
2846 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2848 // don't do physics on disconnected clients, FrikBot relies on this
2849 if (!host_client->spawned)
2851 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2855 // make sure the velocity is sane (not a NaN)
2856 SV_CheckVelocity(ent);
2858 switch ((int) ent->fields.server->movetype)
2861 case MOVETYPE_FAKEPUSH:
2862 SV_Physics_Pusher (ent);
2865 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2866 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2869 case MOVETYPE_FOLLOW:
2870 SV_Physics_Follow (ent);
2872 case MOVETYPE_NOCLIP:
2875 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2876 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2879 SV_Physics_Step (ent);
2883 // don't run physics here if running asynchronously
2884 if (host_client->clmovement_inputtimeout <= 0)
2888 case MOVETYPE_BOUNCE:
2889 case MOVETYPE_BOUNCEMISSILE:
2890 case MOVETYPE_FLYMISSILE:
2893 SV_Physics_Toss (ent);
2899 case MOVETYPE_PHYSICS:
2903 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2907 SV_CheckVelocity (ent);
2910 SV_LinkEdict_TouchAreaGrid(ent);
2912 SV_CheckVelocity (ent);
2921 void SV_Physics (void)
2926 // let the progs know that a new frame has started
2927 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2928 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2929 prog->globals.server->time = sv.time;
2930 prog->globals.server->frametime = sv.frametime;
2931 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2933 // run physics engine
2934 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2937 // treat each object in turn
2940 // if force_retouch, relink all the entities
2941 if (prog->globals.server->force_retouch > 0)
2942 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2943 if (!ent->priv.server->free)
2944 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2946 if (sv_gameplayfix_consistentplayerprethink.integer)
2948 // run physics on the client entities in 3 stages
2949 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2950 if (!ent->priv.server->free)
2951 SV_Physics_ClientEntity_PreThink(ent);
2953 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2954 if (!ent->priv.server->free)
2955 SV_Physics_ClientEntity(ent);
2957 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2958 if (!ent->priv.server->free)
2959 SV_Physics_ClientEntity_PostThink(ent);
2963 // run physics on the client entities
2964 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2966 if (!ent->priv.server->free)
2968 SV_Physics_ClientEntity_PreThink(ent);
2969 SV_Physics_ClientEntity(ent);
2970 SV_Physics_ClientEntity_PostThink(ent);
2975 // run physics on all the non-client entities
2976 if (!sv_freezenonclients.integer)
2978 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2979 if (!ent->priv.server->free)
2980 SV_Physics_Entity(ent);
2981 // make a second pass to see if any ents spawned this frame and make
2982 // sure they run their move/think
2983 if (sv_gameplayfix_delayprojectiles.integer < 0)
2984 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2985 if (!ent->priv.server->move && !ent->priv.server->free)
2986 SV_Physics_Entity(ent);
2989 if (prog->globals.server->force_retouch > 0)
2990 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2992 // LordHavoc: endframe support
2993 if (PRVM_serverfunction(EndFrame))
2995 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2996 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2997 prog->globals.server->time = sv.time;
2998 PRVM_ExecuteProgram (PRVM_serverfunction(EndFrame), "QC function EndFrame is missing");
3001 // decrement prog->num_edicts if the highest number entities died
3002 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3004 if (!sv_freezenonclients.integer)
3005 sv.time += sv.frametime;