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_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float) & 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)
77 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
78 if (val && val->_float)
79 return (int)val->_float;
80 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
82 if ((int)passedict->fields.server->flags & FL_MONSTER)
83 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
85 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
87 else if (passedict->fields.server->solid == SOLID_CORPSE)
88 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
89 else if (passedict->fields.server->solid == SOLID_TRIGGER)
90 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
92 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
95 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
103 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
105 int i, bodysupercontents;
108 prvm_edict_t *traceowner, *touch;
110 // bounding box of entire move area
111 vec3_t clipboxmins, clipboxmaxs;
112 // size when clipping against monsters
113 vec3_t clipmins2, clipmaxs2;
114 // start and end origin of move
118 // matrices to transform into/out of other entity's space
119 matrix4x4_t matrix, imatrix;
120 // model of other entity
122 // list of entities to test for collisions
124 prvm_edict_t *touchedicts[MAX_EDICTS];
126 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
128 VectorCopy(start, clipstart);
129 VectorClear(clipmins2);
130 VectorClear(clipmaxs2);
131 #if COLLISIONPARANOID >= 3
132 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
136 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
137 cliptrace.bmodelstartsolid = cliptrace.startsolid;
138 if (cliptrace.startsolid || cliptrace.fraction < 1)
139 cliptrace.ent = prog->edicts;
140 if (type == MOVE_WORLDONLY)
143 if (type == MOVE_MISSILE)
145 // LordHavoc: modified this, was = -15, now -= 15
146 for (i = 0;i < 3;i++)
153 // create the bounding box of the entire move
154 for (i = 0;i < 3;i++)
156 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
157 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
160 // debug override to test against everything
161 if (sv_debugmove.integer)
163 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
164 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
167 // if the passedict is world, make it NULL (to avoid two checks each time)
168 if (passedict == prog->edicts)
170 // precalculate prog value for passedict for comparisons
171 passedictprog = PRVM_EDICT_TO_PROG(passedict);
172 // precalculate passedict's owner edict pointer for comparisons
173 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
176 // because this uses World_EntitiestoBox, we know all entity boxes overlap
177 // the clip region, so we can skip culling checks in the loop below
178 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
179 if (numtouchedicts > MAX_EDICTS)
181 // this never happens
182 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
183 numtouchedicts = MAX_EDICTS;
185 for (i = 0;i < numtouchedicts;i++)
187 touch = touchedicts[i];
189 if (touch->fields.server->solid < SOLID_BBOX)
191 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
196 // don't clip against self
197 if (passedict == touch)
199 // don't clip owned entities against owner
200 if (traceowner == touch)
202 // don't clip owner against owned entities
203 if (passedictprog == touch->fields.server->owner)
205 // don't clip points against points (they can't collide)
206 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
210 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
212 // might interact, so do an exact clip
214 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
216 model = SV_GetModelFromEdict(touch);
217 pitchsign = SV_GetPitchSign(touch);
220 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);
222 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
223 Matrix4x4_Invert_Simple(&imatrix, &matrix);
224 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
225 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
226 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
227 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
228 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);
230 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);
232 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
244 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
245 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
247 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
250 int i, bodysupercontents;
253 prvm_edict_t *traceowner, *touch;
255 // bounding box of entire move area
256 vec3_t clipboxmins, clipboxmaxs;
257 // size when clipping against monsters
258 vec3_t clipmins2, clipmaxs2;
259 // start and end origin of move
260 vec3_t clipstart, clipend;
263 // matrices to transform into/out of other entity's space
264 matrix4x4_t matrix, imatrix;
265 // model of other entity
267 // list of entities to test for collisions
269 prvm_edict_t *touchedicts[MAX_EDICTS];
270 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
274 if(!VectorCompare(start, pEnd))
276 // TRICK: make the trace 1 qu longer!
277 VectorSubtract(pEnd, start, end);
278 len = VectorNormalizeLength(end);
279 VectorAdd(pEnd, end, end);
282 VectorCopy(pEnd, end);
285 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
287 if (VectorCompare(start, end))
288 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
290 VectorCopy(start, clipstart);
291 VectorCopy(end, clipend);
292 VectorClear(clipmins2);
293 VectorClear(clipmaxs2);
294 #if COLLISIONPARANOID >= 3
295 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
299 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask);
300 cliptrace.bmodelstartsolid = cliptrace.startsolid;
301 if (cliptrace.startsolid || cliptrace.fraction < 1)
302 cliptrace.ent = prog->edicts;
303 if (type == MOVE_WORLDONLY)
306 if (type == MOVE_MISSILE)
308 // LordHavoc: modified this, was = -15, now -= 15
309 for (i = 0;i < 3;i++)
316 // create the bounding box of the entire move
317 for (i = 0;i < 3;i++)
319 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
320 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
323 // debug override to test against everything
324 if (sv_debugmove.integer)
326 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
327 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
330 // if the passedict is world, make it NULL (to avoid two checks each time)
331 if (passedict == prog->edicts)
333 // precalculate prog value for passedict for comparisons
334 passedictprog = PRVM_EDICT_TO_PROG(passedict);
335 // precalculate passedict's owner edict pointer for comparisons
336 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
339 // because this uses World_EntitiestoBox, we know all entity boxes overlap
340 // the clip region, so we can skip culling checks in the loop below
341 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
342 if (numtouchedicts > MAX_EDICTS)
344 // this never happens
345 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
346 numtouchedicts = MAX_EDICTS;
348 for (i = 0;i < numtouchedicts;i++)
350 touch = touchedicts[i];
352 if (touch->fields.server->solid < SOLID_BBOX)
354 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
359 // don't clip against self
360 if (passedict == touch)
362 // don't clip owned entities against owner
363 if (traceowner == touch)
365 // don't clip owner against owned entities
366 if (passedictprog == touch->fields.server->owner)
368 // don't clip points against points (they can't collide)
369 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
373 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
375 // might interact, so do an exact clip
377 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
379 model = SV_GetModelFromEdict(touch);
380 pitchsign = SV_GetPitchSign(touch);
383 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);
385 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
386 Matrix4x4_Invert_Simple(&imatrix, &matrix);
387 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
388 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
389 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
390 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
391 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);
393 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);
395 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
399 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
400 if(!VectorCompare(start, pEnd))
401 Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd);
411 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
412 #if COLLISIONPARANOID >= 1
413 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)
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)
418 #if COLLISIONPARANOID >= 1
419 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)
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)
425 vec3_t hullmins, hullmaxs;
426 int i, bodysupercontents;
430 prvm_edict_t *traceowner, *touch;
432 // bounding box of entire move area
433 vec3_t clipboxmins, clipboxmaxs;
434 // size of the moving object
435 vec3_t clipmins, clipmaxs;
436 // size when clipping against monsters
437 vec3_t clipmins2, clipmaxs2;
438 // start and end origin of move
439 vec3_t clipstart, clipend;
442 // matrices to transform into/out of other entity's space
443 matrix4x4_t matrix, imatrix;
444 // model of other entity
446 // list of entities to test for collisions
448 prvm_edict_t *touchedicts[MAX_EDICTS];
449 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
453 if(!VectorCompare(start, pEnd))
455 // TRICK: make the trace 1 qu longer!
456 VectorSubtract(pEnd, start, end);
457 len = VectorNormalizeLength(end);
458 VectorAdd(pEnd, end, end);
461 VectorCopy(pEnd, end);
464 if (VectorCompare(mins, maxs))
466 vec3_t shiftstart, shiftend;
467 VectorAdd(start, mins, shiftstart);
468 VectorAdd(end, mins, shiftend);
469 if (VectorCompare(start, end))
470 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
472 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
473 VectorSubtract(trace.endpos, mins, trace.endpos);
477 VectorCopy(start, clipstart);
478 VectorCopy(end, clipend);
479 VectorCopy(mins, clipmins);
480 VectorCopy(maxs, clipmaxs);
481 VectorCopy(mins, clipmins2);
482 VectorCopy(maxs, clipmaxs2);
483 #if COLLISIONPARANOID >= 3
484 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
488 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
489 cliptrace.bmodelstartsolid = cliptrace.startsolid;
490 if (cliptrace.startsolid || cliptrace.fraction < 1)
491 cliptrace.ent = prog->edicts;
492 if (type == MOVE_WORLDONLY)
495 if (type == MOVE_MISSILE)
497 // LordHavoc: modified this, was = -15, now -= 15
498 for (i = 0;i < 3;i++)
505 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
506 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
507 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
510 VectorCopy(clipmins, hullmins);
511 VectorCopy(clipmaxs, hullmaxs);
514 // create the bounding box of the entire move
515 for (i = 0;i < 3;i++)
517 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
518 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
521 // debug override to test against everything
522 if (sv_debugmove.integer)
524 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
525 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
528 // if the passedict is world, make it NULL (to avoid two checks each time)
529 if (passedict == prog->edicts)
531 // precalculate prog value for passedict for comparisons
532 passedictprog = PRVM_EDICT_TO_PROG(passedict);
533 // figure out whether this is a point trace for comparisons
534 pointtrace = VectorCompare(clipmins, clipmaxs);
535 // precalculate passedict's owner edict pointer for comparisons
536 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
539 // because this uses World_EntitiestoBox, we know all entity boxes overlap
540 // the clip region, so we can skip culling checks in the loop below
541 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
542 if (numtouchedicts > MAX_EDICTS)
544 // this never happens
545 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
546 numtouchedicts = MAX_EDICTS;
548 for (i = 0;i < numtouchedicts;i++)
550 touch = touchedicts[i];
552 if (touch->fields.server->solid < SOLID_BBOX)
554 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
559 // don't clip against self
560 if (passedict == touch)
562 // don't clip owned entities against owner
563 if (traceowner == touch)
565 // don't clip owner against owned entities
566 if (passedictprog == touch->fields.server->owner)
568 // don't clip points against points (they can't collide)
569 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
573 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
575 // might interact, so do an exact clip
577 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
579 model = SV_GetModelFromEdict(touch);
580 pitchsign = SV_GetPitchSign(touch);
583 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);
585 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
586 Matrix4x4_Invert_Simple(&imatrix, &matrix);
587 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
588 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
589 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
590 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
591 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);
593 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);
595 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
599 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
600 if(!VectorCompare(start, pEnd))
601 Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd);
606 #if COLLISIONPARANOID >= 1
607 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)
612 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
615 VectorCopy(trace.endpos, temp);
616 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
617 #if COLLISIONPARANOID < 3
618 if (trace.startsolid || endstuck)
620 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" : "");
626 int SV_PointSuperContents(const vec3_t point)
628 int supercontents = 0;
632 // matrices to transform into/out of other entity's space
633 matrix4x4_t matrix, imatrix;
634 // model of other entity
637 // list of entities to test for collisions
639 prvm_edict_t *touchedicts[MAX_EDICTS];
641 // get world supercontents at this point
642 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
643 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
645 // if sv_gameplayfix_swiminbmodels is off we're done
646 if (!sv_gameplayfix_swiminbmodels.integer)
647 return supercontents;
649 // get list of entities at this point
650 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
651 if (numtouchedicts > MAX_EDICTS)
653 // this never happens
654 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
655 numtouchedicts = MAX_EDICTS;
657 for (i = 0;i < numtouchedicts;i++)
659 touch = touchedicts[i];
661 // we only care about SOLID_BSP for pointcontents
662 if (touch->fields.server->solid != SOLID_BSP)
665 // might interact, so do an exact clip
666 model = SV_GetModelFromEdict(touch);
667 if (!model || !model->PointSuperContents)
669 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);
670 Matrix4x4_Invert_Simple(&imatrix, &matrix);
671 Matrix4x4_Transform(&imatrix, point, transformed);
672 frame = (int)touch->fields.server->frame;
673 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
676 return supercontents;
680 ===============================================================================
682 Linking entities into the world culling system
684 ===============================================================================
687 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
690 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
691 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
692 prog->globals.server->time = sv.time;
693 prog->globals.server->trace_allsolid = false;
694 prog->globals.server->trace_startsolid = false;
695 prog->globals.server->trace_fraction = 1;
696 prog->globals.server->trace_inwater = false;
697 prog->globals.server->trace_inopen = true;
698 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
699 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
700 prog->globals.server->trace_plane_dist = 0;
701 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
702 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
704 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
706 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
708 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
710 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
713 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
715 int i, numtouchedicts, old_self, old_other;
716 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
718 if (ent == prog->edicts)
719 return; // don't add the world
721 if (ent->priv.server->free)
724 if (ent->fields.server->solid == SOLID_NOT)
727 // build a list of edicts to touch, because the link loop can be corrupted
728 // by IncreaseEdicts called during touch functions
729 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
730 if (numtouchedicts > MAX_EDICTS)
732 // this never happens
733 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
734 numtouchedicts = MAX_EDICTS;
737 old_self = prog->globals.server->self;
738 old_other = prog->globals.server->other;
739 for (i = 0;i < numtouchedicts;i++)
741 touch = touchedicts[i];
742 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
744 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
747 prog->globals.server->self = old_self;
748 prog->globals.server->other = old_other;
751 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
755 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
757 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
758 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
759 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
760 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];
761 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];
762 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
763 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];
764 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];
765 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
766 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];
767 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];
768 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
769 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];
770 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];
771 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
772 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];
773 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];
774 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
775 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];
776 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];
777 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
778 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];
779 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 void SV_LinkEdict (prvm_edict_t *ent)
794 if (ent == prog->edicts)
795 return; // don't add the world
797 if (ent->priv.server->free)
800 modelindex = (int)ent->fields.server->modelindex;
801 if (modelindex < 0 || modelindex >= MAX_MODELS)
803 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
806 model = SV_GetModelByIndex(modelindex);
808 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
809 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
810 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
814 if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
816 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
817 // TODO special handling for spheres?
818 RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs);
819 VectorAdd(ent->fields.server->origin, mins, mins);
820 VectorAdd(ent->fields.server->origin, maxs, maxs);
822 else if (ent->fields.server->solid == SOLID_BSP)
826 if (!model->TraceBox && developer.integer >= 1)
827 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
829 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
831 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
832 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
834 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
836 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
837 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
841 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
842 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
847 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
848 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
849 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
854 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
855 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
859 // to make items easier to pick up and allow them to be grabbed off
860 // of shelves, the abs sizes are expanded
862 if ((int)ent->fields.server->flags & FL_ITEM)
873 // because movement is clipped an epsilon away from an actual edge,
874 // we must fully check even when bounding boxes don't quite touch
883 VectorCopy(mins, ent->fields.server->absmin);
884 VectorCopy(maxs, ent->fields.server->absmax);
886 World_LinkEdict(&sv.world, ent, mins, maxs);
890 ===============================================================================
894 ===============================================================================
899 SV_TestEntityPosition
901 returns true if the entity is in solid currently
904 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
909 contents = SV_GenericHitSuperContentsMask(ent);
910 VectorAdd(ent->fields.server->origin, offset, org);
911 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
912 if (trace.startsupercontents & contents)
916 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
918 // q1bsp/hlbsp use hulls and if the entity does not exactly match
919 // a hull size it is incorrectly tested, so this code tries to
920 // 'fix' it slightly...
921 // FIXME: this breaks entities larger than the hull size
924 VectorAdd(org, ent->fields.server->mins, m1);
925 VectorAdd(org, ent->fields.server->maxs, m2);
926 VectorSubtract(m2, m1, s);
927 #define EPSILON (1.0f / 32.0f)
928 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
929 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
930 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
931 for (i = 0;i < 8;i++)
933 v[0] = (i & 1) ? m2[0] : m1[0];
934 v[1] = (i & 2) ? m2[1] : m1[1];
935 v[2] = (i & 4) ? m2[2] : m1[2];
936 if (SV_PointSuperContents(v) & contents)
941 // if the trace found a better position for the entity, move it there
942 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
945 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
946 VectorCopy(trace.endpos, ent->fields.server->origin);
948 // verify if the endpos is REALLY outside solid
949 VectorCopy(trace.endpos, org);
950 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
952 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
954 VectorCopy(org, ent->fields.server->origin);
965 void SV_CheckAllEnts (void)
970 // see if any solid entities are inside the final position
971 check = PRVM_NEXT_EDICT(prog->edicts);
972 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
974 if (check->priv.server->free)
976 if (check->fields.server->movetype == MOVETYPE_PUSH
977 || check->fields.server->movetype == MOVETYPE_NONE
978 || check->fields.server->movetype == MOVETYPE_FOLLOW
979 || check->fields.server->movetype == MOVETYPE_NOCLIP)
982 if (SV_TestEntityPosition (check, vec3_origin))
983 Con_Print("entity in invalid position\n");
987 // DRESK - Support for Entity Contents Transition Event
990 SV_CheckContentsTransition
992 returns true if entity had a valid contentstransition function call
995 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
997 int bValidFunctionCall;
998 prvm_eval_t *contentstransition;
1000 // Default Valid Function Call to False
1001 bValidFunctionCall = false;
1003 if(ent->fields.server->watertype != nContents)
1004 { // Changed Contents
1005 // Acquire Contents Transition Function from QC
1006 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
1008 if(contentstransition->function)
1009 { // Valid Function; Execute
1010 // Assign Valid Function
1011 bValidFunctionCall = true;
1012 // Prepare Parameters (Original Contents, New Contents)
1013 // Original Contents
1014 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
1016 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1018 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1019 // Execute VM Function
1020 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
1024 // Return if Function Call was Valid
1025 return bValidFunctionCall;
1034 void SV_CheckVelocity (prvm_edict_t *ent)
1042 for (i=0 ; i<3 ; i++)
1044 if (IS_NAN(ent->fields.server->velocity[i]))
1046 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1047 ent->fields.server->velocity[i] = 0;
1049 if (IS_NAN(ent->fields.server->origin[i]))
1051 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1052 ent->fields.server->origin[i] = 0;
1056 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1057 // player_run/player_stand1 does not horribly malfunction if the
1058 // velocity becomes a denormalized float
1059 if (VectorLength2(ent->fields.server->velocity) < 0.0001)
1060 VectorClear(ent->fields.server->velocity);
1062 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1063 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1064 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1066 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1067 ent->fields.server->velocity[0] *= wishspeed;
1068 ent->fields.server->velocity[1] *= wishspeed;
1069 ent->fields.server->velocity[2] *= wishspeed;
1077 Runs thinking code if time. There is some play in the exact time the think
1078 function will be called, because it is called before any movement is done
1079 in a frame. Not used for pushmove objects, because they must be exact.
1080 Returns false if the entity removed itself.
1083 qboolean SV_RunThink (prvm_edict_t *ent)
1087 // don't let things stay in the past.
1088 // it is possible to start that way by a trigger with a local time.
1089 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1092 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1094 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1095 ent->fields.server->nextthink = 0;
1096 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1097 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1098 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1099 // mods often set nextthink to time to cause a think every frame,
1100 // we don't want to loop in that case, so exit if the new nextthink is
1101 // <= the time the qc was told, also exit if it is past the end of the
1103 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1106 return !ent->priv.server->free;
1113 Two entities have touched, so run their touch functions
1114 returns true if the impact kept the origin of the touching entity intact
1117 extern void VM_SetTraceGlobals(const trace_t *trace);
1118 extern sizebuf_t vm_tempstringsbuf;
1119 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1121 int restorevm_tempstringsbuf_cursize;
1122 int old_self, old_other;
1124 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1127 old_self = prog->globals.server->self;
1128 old_other = prog->globals.server->other;
1129 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1131 VectorCopy(e1->fields.server->origin, org);
1133 VM_SetTraceGlobals(trace);
1135 prog->globals.server->time = sv.time;
1136 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1138 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1139 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1140 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1143 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1145 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1146 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1147 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1148 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1149 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1150 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1151 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1153 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1155 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1157 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1159 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1162 prog->globals.server->self = old_self;
1163 prog->globals.server->other = old_other;
1164 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1166 return VectorCompare(e1->fields.server->origin, org);
1174 Slide off of the impacting object
1175 returns the blocked flags (1 = floor, 2 = step / wall)
1178 #define STOP_EPSILON 0.1
1179 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1184 backoff = -DotProduct (in, normal) * overbounce;
1185 VectorMA(in, backoff, normal, out);
1187 for (i = 0;i < 3;i++)
1188 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1197 The basic solid body movement clip that slides along multiple planes
1198 Returns the clipflags if the velocity was modified (hit something solid)
1202 8 = teleported by touch method
1203 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1206 static float SV_Gravity (prvm_edict_t *ent);
1207 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1208 #define MAX_CLIP_PLANES 5
1209 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
1211 int blocked, bumpcount;
1212 int i, j, numplanes;
1213 float d, time_left, gravity;
1214 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1224 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1226 gravity = SV_Gravity(ent) * 0.5f;
1227 ent->fields.server->velocity[2] -= gravity;
1231 applygravity = false;
1232 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1236 VectorCopy(ent->fields.server->velocity, original_velocity);
1237 VectorCopy(ent->fields.server->velocity, primal_velocity);
1240 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1242 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1245 VectorScale(ent->fields.server->velocity, time_left, push);
1247 VectorAdd(ent->fields.server->origin, push, end);
1249 if(!SV_PushEntity(&trace, ent, push, false, false))
1251 // we got teleported by a touch function
1252 // let's abort the move
1258 //if (trace.fraction < 0.002)
1263 VectorCopy(ent->fields.server->origin, start);
1264 start[2] += 3;//0.03125;
1265 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1266 end[2] += 3;//0.03125;
1267 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1268 if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
1270 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
1276 for (i = 0;i < numplanes;i++)
1278 VectorCopy(ent->fields.server->origin, start);
1279 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1280 VectorMA(start, 3, planes[i], start);
1281 VectorMA(end, 3, planes[i], end);
1282 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1283 if (trace.fraction < testtrace.fraction)
1286 VectorCopy(start, ent->fields.server->origin);
1291 // VectorAdd(ent->fields.server->origin, planes[j], start);
1297 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
1298 if (trace.fraction < 1)
1299 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
1304 if (trace.bmodelstartsolid)
1306 // LordHavoc: note: this code is what makes entities stick in place
1307 // if embedded in world only (you can walk through other objects if
1309 // entity is trapped in another solid
1310 VectorClear(ent->fields.server->velocity);
1315 if (trace.fraction == 1)
1317 if (trace.plane.normal[2])
1319 if (trace.plane.normal[2] > 0.7)
1326 Con_Printf ("SV_FlyMove: !trace.ent");
1327 trace.ent = prog->edicts;
1330 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1331 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1338 // save the trace for player extrafriction
1340 VectorCopy(trace.plane.normal, stepnormal);
1342 if (trace.fraction >= 0.001)
1344 // actually covered some distance
1345 VectorCopy(ent->fields.server->velocity, original_velocity);
1349 time_left *= 1 - trace.fraction;
1351 // clipped to another plane
1352 if (numplanes >= MAX_CLIP_PLANES)
1354 // this shouldn't really happen
1355 VectorClear(ent->fields.server->velocity);
1361 for (i = 0;i < numplanes;i++)
1362 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1366 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1371 VectorCopy(trace.plane.normal, planes[numplanes]);
1374 if (sv_newflymove.integer)
1375 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1378 // modify original_velocity so it parallels all of the clip planes
1379 for (i = 0;i < numplanes;i++)
1381 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1382 for (j = 0;j < numplanes;j++)
1387 if (DotProduct(new_velocity, planes[j]) < 0)
1397 // go along this plane
1398 VectorCopy(new_velocity, ent->fields.server->velocity);
1402 // go along the crease
1405 VectorClear(ent->fields.server->velocity);
1409 CrossProduct(planes[0], planes[1], dir);
1410 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1411 VectorNormalize(dir);
1412 d = DotProduct(dir, ent->fields.server->velocity);
1413 VectorScale(dir, d, ent->fields.server->velocity);
1417 // if current velocity is against the original velocity,
1418 // stop dead to avoid tiny occilations in sloping corners
1419 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1421 VectorClear(ent->fields.server->velocity);
1426 //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]);
1429 if ((blocked & 1) == 0 && bumpcount > 1)
1431 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1432 // flag ONGROUND if there's ground under it
1433 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1437 // LordHavoc: this came from QW and allows you to get out of water more easily
1438 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1439 VectorCopy(primal_velocity, ent->fields.server->velocity);
1440 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1441 ent->fields.server->velocity[2] -= gravity;
1451 static float SV_Gravity (prvm_edict_t *ent)
1456 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1457 if (val!=0 && val->_float)
1458 ent_gravity = val->_float;
1461 return ent_gravity * sv_gravity.value * sv.frametime;
1466 ===============================================================================
1470 ===============================================================================
1477 Does not change the entities velocity at all
1478 The trace struct is filled with the trace that has been done.
1479 Returns true if the push did not result in the entity being teleported by QC code.
1482 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1489 VectorCopy(ent->fields.server->origin, original);
1490 VectorAdd (ent->fields.server->origin, push, end);
1492 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1493 type = MOVE_MISSILE;
1494 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1495 type = MOVE_NOMONSTERS; // only clip against bmodels
1499 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1501 while (trace->startsolid && sv_gameplayfix_nudgeoutofsolid.integer)
1503 vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
1504 VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
1505 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1509 VectorCopy(original, ent->fields.server->origin);
1513 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1517 VectorCopy (trace->endpos, ent->fields.server->origin);
1521 if(!trace->startsolid)
1522 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)
1524 Con_Printf("something eeeeevil happened\n");
1529 SV_LinkEdict_TouchAreaGrid(ent);
1531 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))))
1532 return SV_Impact (ent, trace);
1544 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1547 int pusherowner, pusherprog;
1550 float savesolid, movetime2, pushltime;
1551 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1553 int numcheckentities;
1554 static prvm_edict_t *checkentities[MAX_EDICTS];
1555 dp_model_t *pushermodel;
1556 trace_t trace, trace2;
1557 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1558 unsigned short moved_edicts[MAX_EDICTS];
1560 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])
1562 pusher->fields.server->ltime += movetime;
1566 switch ((int) pusher->fields.server->solid)
1568 // LordHavoc: valid pusher types
1571 case SOLID_SLIDEBOX:
1572 case SOLID_CORPSE: // LordHavoc: this would be weird...
1574 // LordHavoc: no collisions
1577 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1578 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1579 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1580 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1581 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1582 pusher->fields.server->ltime += movetime;
1583 SV_LinkEdict(pusher);
1586 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1589 index = (int) pusher->fields.server->modelindex;
1590 if (index < 1 || index >= MAX_MODELS)
1592 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1595 pushermodel = SV_GetModelByIndex(index);
1596 pusherowner = pusher->fields.server->owner;
1597 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1599 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1601 movetime2 = movetime;
1602 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1603 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1604 if (moveangle[0] || moveangle[2])
1606 for (i = 0;i < 3;i++)
1610 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1611 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1615 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1616 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1620 else if (moveangle[1])
1622 for (i = 0;i < 3;i++)
1626 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1627 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1631 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1632 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1638 for (i = 0;i < 3;i++)
1642 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1643 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1647 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1648 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1653 VectorNegate (moveangle, a);
1654 AngleVectorsFLU (a, forward, left, up);
1656 VectorCopy (pusher->fields.server->origin, pushorig);
1657 VectorCopy (pusher->fields.server->angles, pushang);
1658 pushltime = pusher->fields.server->ltime;
1660 // move the pusher to its final position
1662 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1663 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1664 pusher->fields.server->ltime += movetime;
1665 SV_LinkEdict(pusher);
1667 pushermodel = SV_GetModelFromEdict(pusher);
1668 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);
1669 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1671 savesolid = pusher->fields.server->solid;
1673 // see if any solid entities are inside the final position
1676 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1677 for (e = 0;e < numcheckentities;e++)
1679 prvm_edict_t *check = checkentities[e];
1680 int movetype = (int)check->fields.server->movetype;
1685 case MOVETYPE_FOLLOW:
1686 case MOVETYPE_NOCLIP:
1687 case MOVETYPE_FAKEPUSH:
1693 if (check->fields.server->owner == pusherprog)
1696 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1699 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1701 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1702 check->priv.server->waterposition_forceupdate = true;
1704 checkcontents = SV_GenericHitSuperContentsMask(check);
1706 // if the entity is standing on the pusher, it will definitely be moved
1707 // if the entity is not standing on the pusher, but is in the pusher's
1708 // final position, move it
1709 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1711 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);
1712 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1713 if (!trace.startsolid)
1715 //Con_Printf("- not in solid\n");
1723 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1724 org2[0] = DotProduct (org, forward);
1725 org2[1] = DotProduct (org, left);
1726 org2[2] = DotProduct (org, up);
1727 VectorSubtract (org2, org, move);
1728 VectorAdd (move, move1, move);
1731 VectorCopy (move1, move);
1733 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1735 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1736 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1737 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1739 // physics objects need better collisions than this code can do
1740 if (movetype == MOVETYPE_PHYSICS)
1742 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1743 SV_LinkEdict(check);
1744 SV_LinkEdict_TouchAreaGrid(check);
1748 // try moving the contacted entity
1749 pusher->fields.server->solid = SOLID_NOT;
1750 if(!SV_PushEntity (&trace, check, move, true, true))
1752 // entity "check" got teleported
1753 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1754 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1755 continue; // pushed enough
1757 // FIXME: turn players specially
1758 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1759 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1760 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1762 // this trace.fraction < 1 check causes items to fall off of pushers
1763 // if they pass under or through a wall
1764 // the groundentity check causes items to fall off of ledges
1765 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1766 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1768 // if it is still inside the pusher, block
1769 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);
1770 if (trace.startsolid)
1772 // try moving the contacted entity a tiny bit further to account for precision errors
1774 pusher->fields.server->solid = SOLID_NOT;
1775 VectorScale(move, 1.1, move2);
1776 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1777 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1778 if(!SV_PushEntity (&trace2, check, move2, true, true))
1780 // entity "check" got teleported
1783 pusher->fields.server->solid = savesolid;
1784 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);
1785 if (trace.startsolid)
1787 // try moving the contacted entity a tiny bit less to account for precision errors
1788 pusher->fields.server->solid = SOLID_NOT;
1789 VectorScale(move, 0.9, move2);
1790 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1791 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1792 if(!SV_PushEntity (&trace2, check, move2, true, true))
1794 // entity "check" got teleported
1797 pusher->fields.server->solid = savesolid;
1798 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);
1799 if (trace.startsolid)
1801 // still inside pusher, so it's really blocked
1804 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1806 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1809 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1810 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1814 VectorCopy (pushorig, pusher->fields.server->origin);
1815 VectorCopy (pushang, pusher->fields.server->angles);
1816 pusher->fields.server->ltime = pushltime;
1817 SV_LinkEdict(pusher);
1819 // move back any entities we already moved
1820 for (i = 0;i < num_moved;i++)
1822 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1823 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1824 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1828 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1829 if (pusher->fields.server->blocked)
1831 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1832 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1833 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1840 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1841 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1842 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1851 void SV_Physics_Pusher (prvm_edict_t *ent)
1853 float thinktime, oldltime, movetime;
1855 oldltime = ent->fields.server->ltime;
1857 thinktime = ent->fields.server->nextthink;
1858 if (thinktime < ent->fields.server->ltime + sv.frametime)
1860 movetime = thinktime - ent->fields.server->ltime;
1865 movetime = sv.frametime;
1868 // advances ent->fields.server->ltime if not blocked
1869 SV_PushMove (ent, movetime);
1871 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1873 ent->fields.server->nextthink = 0;
1874 prog->globals.server->time = sv.time;
1875 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1876 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1877 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1883 ===============================================================================
1887 ===============================================================================
1890 static float unstickoffsets[] =
1892 // poutting -/+z changes first as they are least weird
1907 typedef enum unstickresult_e
1915 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1919 // if not stuck in a bmodel, just return
1920 if (!SV_TestEntityPosition(ent, vec3_origin))
1921 return UNSTICK_GOOD;
1923 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1925 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1927 VectorCopy(unstickoffsets + i, offset);
1929 //SV_LinkEdict_TouchAreaGrid(ent);
1930 return UNSTICK_UNSTUCK;
1934 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1935 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1937 for(i = 2; i <= maxunstick; ++i)
1939 VectorClear(offset);
1941 if (!SV_TestEntityPosition(ent, offset))
1944 //SV_LinkEdict_TouchAreaGrid(ent);
1945 return UNSTICK_UNSTUCK;
1948 if (!SV_TestEntityPosition(ent, offset))
1951 //SV_LinkEdict_TouchAreaGrid(ent);
1952 return UNSTICK_UNSTUCK;
1956 return UNSTICK_STUCK;
1959 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1962 switch(SV_UnstickEntityReturnOffset(ent, offset))
1966 case UNSTICK_UNSTUCK:
1967 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]);
1970 if (developer.integer >= 100)
1971 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1974 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1983 This is a big hack to try and fix the rare case of getting stuck in the world
1987 void SV_CheckStuck (prvm_edict_t *ent)
1991 switch(SV_UnstickEntityReturnOffset(ent, offset))
1994 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1996 case UNSTICK_UNSTUCK:
1997 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]);
2000 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
2001 if (!SV_TestEntityPosition(ent, offset))
2003 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2005 //SV_LinkEdict_TouchAreaGrid(ent);
2008 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
2011 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2021 qboolean SV_CheckWater (prvm_edict_t *ent)
2024 int nNativeContents;
2027 point[0] = ent->fields.server->origin[0];
2028 point[1] = ent->fields.server->origin[1];
2029 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2031 // DRESK - Support for Entity Contents Transition Event
2032 // NOTE: Some logic needed to be slightly re-ordered
2033 // to not affect performance and allow for the feature.
2035 // Acquire Super Contents Prior to Resets
2036 cont = SV_PointSuperContents(point);
2037 // Acquire Native Contents Here
2038 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2040 // DRESK - Support for Entity Contents Transition Event
2041 if(ent->fields.server->watertype)
2042 // Entity did NOT Spawn; Check
2043 SV_CheckContentsTransition(ent, nNativeContents);
2046 ent->fields.server->waterlevel = 0;
2047 ent->fields.server->watertype = CONTENTS_EMPTY;
2048 cont = SV_PointSuperContents(point);
2049 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2051 ent->fields.server->watertype = nNativeContents;
2052 ent->fields.server->waterlevel = 1;
2053 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2054 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2056 ent->fields.server->waterlevel = 2;
2057 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2058 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2059 ent->fields.server->waterlevel = 3;
2063 return ent->fields.server->waterlevel > 1;
2072 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2075 vec3_t forward, into, side;
2077 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2078 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2080 // cut the tangential velocity
2081 i = DotProduct (stepnormal, ent->fields.server->velocity);
2082 VectorScale (stepnormal, i, into);
2083 VectorSubtract (ent->fields.server->velocity, into, side);
2084 ent->fields.server->velocity[0] = side[0] * (1 + d);
2085 ent->fields.server->velocity[1] = side[1] * (1 + d);
2091 =====================
2094 Player has come to a dead stop, possibly due to the problem with limited
2095 float precision at some angle joins in the BSP hull.
2097 Try fixing by pushing one pixel in each direction.
2099 This is a hack, but in the interest of good gameplay...
2100 ======================
2102 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2107 VectorCopy (ent->fields.server->origin, oldorg);
2110 for (i=0 ; i<8 ; i++)
2112 // try pushing a little in an axial direction
2115 case 0: dir[0] = 2; dir[1] = 0; break;
2116 case 1: dir[0] = 0; dir[1] = 2; break;
2117 case 2: dir[0] = -2; dir[1] = 0; break;
2118 case 3: dir[0] = 0; dir[1] = -2; break;
2119 case 4: dir[0] = 2; dir[1] = 2; break;
2120 case 5: dir[0] = -2; dir[1] = 2; break;
2121 case 6: dir[0] = 2; dir[1] = -2; break;
2122 case 7: dir[0] = -2; dir[1] = -2; break;
2125 SV_PushEntity (&trace, ent, dir, false, true);
2127 // retry the original move
2128 ent->fields.server->velocity[0] = oldvel[0];
2129 ent->fields.server->velocity[1] = oldvel[1];
2130 ent->fields.server->velocity[2] = 0;
2131 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2133 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2134 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2136 Con_DPrint("TryUnstick - success.\n");
2140 // go back to the original pos and try again
2141 VectorCopy (oldorg, ent->fields.server->origin);
2145 VectorClear (ent->fields.server->velocity);
2146 Con_DPrint("TryUnstick - failure.\n");
2152 =====================
2155 Only used by players
2156 ======================
2158 void SV_WalkMove (prvm_edict_t *ent)
2160 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2161 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2162 trace_t downtrace, trace;
2163 qboolean applygravity;
2165 // if frametime is 0 (due to client sending the same timestamp twice),
2167 if (sv.frametime <= 0)
2170 SV_CheckStuck (ent);
2172 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2174 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2176 SV_CheckVelocity(ent);
2178 // do a regular slide move unless it looks like you ran into a step
2179 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2181 VectorCopy (ent->fields.server->origin, start_origin);
2182 VectorCopy (ent->fields.server->velocity, start_velocity);
2184 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2186 // if the move did not hit the ground at any point, we're not on ground
2188 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2190 SV_CheckVelocity(ent);
2192 SV_LinkEdict_TouchAreaGrid(ent);
2194 if(clip & 8) // teleport
2197 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2200 if (sv_nostep.integer)
2203 VectorCopy(ent->fields.server->origin, originalmove_origin);
2204 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2205 originalmove_clip = clip;
2206 originalmove_flags = (int)ent->fields.server->flags;
2207 originalmove_groundentity = ent->fields.server->groundentity;
2209 // if move didn't block on a step, return
2212 // if move was not trying to move into the step, return
2213 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2216 if (ent->fields.server->movetype != MOVETYPE_FLY)
2218 // return if gibbed by a trigger
2219 if (ent->fields.server->movetype != MOVETYPE_WALK)
2222 // only step up while jumping if that is enabled
2223 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2224 if (!oldonground && ent->fields.server->waterlevel == 0)
2228 // try moving up and forward to go up a step
2229 // back to start pos
2230 VectorCopy (start_origin, ent->fields.server->origin);
2231 VectorCopy (start_velocity, ent->fields.server->velocity);
2234 VectorClear (upmove);
2235 upmove[2] = sv_stepheight.value;
2236 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2238 // we got teleported when upstepping... must abort the move
2243 ent->fields.server->velocity[2] = 0;
2244 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2245 ent->fields.server->velocity[2] += start_velocity[2];
2248 // we got teleported when upstepping... must abort the move
2249 // note that z velocity handling may not be what QC expects here, but we cannot help it
2253 SV_CheckVelocity(ent);
2255 SV_LinkEdict_TouchAreaGrid(ent);
2257 // check for stuckness, possibly due to the limited precision of floats
2258 // in the clipping hulls
2260 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2261 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2263 //Con_Printf("wall\n");
2264 // stepping up didn't make any progress, revert to original move
2265 VectorCopy(originalmove_origin, ent->fields.server->origin);
2266 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2267 //clip = originalmove_clip;
2268 ent->fields.server->flags = originalmove_flags;
2269 ent->fields.server->groundentity = originalmove_groundentity;
2270 // now try to unstick if needed
2271 //clip = SV_TryUnstick (ent, oldvel);
2275 //Con_Printf("step - ");
2277 // extra friction based on view angle
2278 if (clip & 2 && sv_wallfriction.integer)
2279 SV_WallFriction (ent, stepnormal);
2281 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2282 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))
2286 VectorClear (downmove);
2287 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2288 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2290 // we got teleported when downstepping... must abort the move
2294 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2296 // this has been disabled so that you can't jump when you are stepping
2297 // up while already jumping (also known as the Quake2 double jump bug)
2299 // LordHavoc: disabled this check so you can walk on monsters/players
2300 //if (ent->fields.server->solid == SOLID_BSP)
2302 //Con_Printf("onground\n");
2303 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2304 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2310 //Con_Printf("slope\n");
2311 // if the push down didn't end up on good ground, use the move without
2312 // the step up. This happens near wall / slope combinations, and can
2313 // cause the player to hop up higher on a slope too steep to climb
2314 VectorCopy(originalmove_origin, ent->fields.server->origin);
2315 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2316 //clip = originalmove_clip;
2317 ent->fields.server->flags = originalmove_flags;
2318 ent->fields.server->groundentity = originalmove_groundentity;
2321 SV_CheckVelocity(ent);
2323 SV_LinkEdict_TouchAreaGrid(ent);
2326 //============================================================================
2332 Entities that are "stuck" to another entity
2335 void SV_Physics_Follow (prvm_edict_t *ent)
2337 vec3_t vf, vr, vu, angles, v;
2341 if (!SV_RunThink (ent))
2344 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2345 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2346 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])
2348 // quick case for no rotation
2349 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2353 angles[0] = -ent->fields.server->punchangle[0];
2354 angles[1] = ent->fields.server->punchangle[1];
2355 angles[2] = ent->fields.server->punchangle[2];
2356 AngleVectors (angles, vf, vr, vu);
2357 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];
2358 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];
2359 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];
2360 angles[0] = -e->fields.server->angles[0];
2361 angles[1] = e->fields.server->angles[1];
2362 angles[2] = e->fields.server->angles[2];
2363 AngleVectors (angles, vf, vr, vu);
2364 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2365 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2366 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2368 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2370 //SV_LinkEdict_TouchAreaGrid(ent);
2374 ==============================================================================
2378 ==============================================================================
2383 SV_CheckWaterTransition
2387 void SV_CheckWaterTransition (prvm_edict_t *ent)
2390 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2391 if (!ent->fields.server->watertype)
2393 // just spawned here
2394 ent->fields.server->watertype = cont;
2395 ent->fields.server->waterlevel = 1;
2399 // DRESK - Support for Entity Contents Transition Event
2400 // NOTE: Call here BEFORE updating the watertype below,
2401 // and suppress watersplash sound if a valid function
2402 // call was made to allow for custom "splash" sounds.
2403 if( !SV_CheckContentsTransition(ent, cont) )
2404 { // Contents Transition Function Invalid; Potentially Play Water Sound
2405 // check if the entity crossed into or out of water
2406 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2407 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2410 if (cont <= CONTENTS_WATER)
2412 ent->fields.server->watertype = cont;
2413 ent->fields.server->waterlevel = 1;
2417 ent->fields.server->watertype = CONTENTS_EMPTY;
2418 ent->fields.server->waterlevel = 0;
2426 Toss, bounce, and fly movement. When onground, do nothing.
2429 void SV_Physics_Toss (prvm_edict_t *ent)
2435 prvm_edict_t *groundentity;
2437 // if onground, return without moving
2438 if ((int)ent->fields.server->flags & FL_ONGROUND)
2440 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2441 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2443 // don't stick to ground if onground and moving upward
2444 ent->fields.server->flags -= FL_ONGROUND;
2446 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2448 // we can trust FL_ONGROUND if groundentity is world because it never moves
2451 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2453 // if ent was supported by a brush model on previous frame,
2454 // and groundentity is now freed, set groundentity to 0 (world)
2455 // which leaves it suspended in the air
2456 ent->fields.server->groundentity = 0;
2457 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2460 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2462 // don't slide if still touching the groundentity
2466 ent->priv.server->suspendedinairflag = false;
2468 SV_CheckVelocity (ent);
2471 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2472 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2475 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2477 movetime = sv.frametime;
2478 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2481 VectorScale (ent->fields.server->velocity, movetime, move);
2482 if(!SV_PushEntity (&trace, ent, move, true, true))
2483 return; // teleported
2484 if (ent->priv.server->free)
2486 if (trace.bmodelstartsolid)
2488 // try to unstick the entity
2489 SV_UnstickEntity(ent);
2490 if(!SV_PushEntity (&trace, ent, move, false, true))
2491 return; // teleported
2492 if (ent->priv.server->free)
2495 if (trace.fraction == 1)
2497 movetime *= 1 - min(1, trace.fraction);
2498 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2501 float bouncefactor = 1.0f;
2502 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2503 if (val!=0 && val->_float)
2504 bouncefactor = val->_float;
2506 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2507 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2509 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2511 float d, ent_gravity;
2513 float bouncefactor = 0.5f;
2514 float bouncestop = 60.0f / 800.0f;
2516 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2517 if (val!=0 && val->_float)
2518 bouncefactor = val->_float;
2520 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2521 if (val!=0 && val->_float)
2522 bouncestop = val->_float;
2524 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2525 // LordHavoc: fixed grenades not bouncing when fired down a slope
2526 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2527 if (val!=0 && val->_float)
2528 ent_gravity = val->_float;
2531 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2533 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2534 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2536 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2537 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2538 VectorClear (ent->fields.server->velocity);
2539 VectorClear (ent->fields.server->avelocity);
2542 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2546 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2548 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2549 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2550 VectorClear (ent->fields.server->velocity);
2551 VectorClear (ent->fields.server->avelocity);
2554 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2559 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2560 if (trace.plane.normal[2] > 0.7)
2562 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2563 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2564 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2565 ent->priv.server->suspendedinairflag = true;
2566 VectorClear (ent->fields.server->velocity);
2567 VectorClear (ent->fields.server->avelocity);
2570 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2572 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2576 // check for in water
2577 SV_CheckWaterTransition (ent);
2581 ===============================================================================
2585 ===============================================================================
2592 Monsters freefall when they don't have a ground entity, otherwise
2593 all movement is done with discrete steps.
2595 This is also used for objects that have become still on the ground, but
2596 will fall if the floor is pulled out from under them.
2599 void SV_Physics_Step (prvm_edict_t *ent)
2601 int flags = (int)ent->fields.server->flags;
2604 // Backup Velocity in the event that movetypesteplandevent is called,
2605 // to provide a parameter with the entity's velocity at impact.
2606 prvm_eval_t *movetypesteplandevent;
2607 vec3_t backupVelocity;
2608 VectorCopy(ent->fields.server->velocity, backupVelocity);
2609 // don't fall at all if fly/swim
2610 if (!(flags & (FL_FLY | FL_SWIM)))
2612 if (flags & FL_ONGROUND)
2614 // freefall if onground and moving upward
2615 // freefall if not standing on a world surface (it may be a lift or trap door)
2616 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2618 ent->fields.server->flags -= FL_ONGROUND;
2619 SV_CheckVelocity(ent);
2620 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2622 SV_LinkEdict_TouchAreaGrid(ent);
2623 ent->priv.server->waterposition_forceupdate = true;
2628 // freefall if not onground
2629 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2631 SV_CheckVelocity(ent);
2632 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2634 SV_LinkEdict_TouchAreaGrid(ent);
2637 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2639 // DRESK - Check for Entity Land Event Function
2640 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2642 if(movetypesteplandevent->function)
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(movetypesteplandevent->function, "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 (prog->funcoffsets.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 (prog->funcoffsets.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;