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 static 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 static prvm_edict_t *touchedicts[MAX_EDICTS];
270 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
274 if (VectorCompare(start, pEnd))
275 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
277 if(collision_endposnudge.value > 0)
279 // TRICK: make the trace 1 qu longer!
280 VectorSubtract(pEnd, start, end);
281 len = VectorNormalizeLength(end);
282 VectorMA(pEnd, collision_endposnudge.value, end, end);
285 VectorCopy(pEnd, end);
287 if (VectorCompare(start, end))
288 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
291 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
293 VectorCopy(start, clipstart);
294 VectorCopy(end, clipend);
295 VectorClear(clipmins2);
296 VectorClear(clipmaxs2);
297 #if COLLISIONPARANOID >= 3
298 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
302 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask);
303 cliptrace.bmodelstartsolid = cliptrace.startsolid;
304 if (cliptrace.startsolid || cliptrace.fraction < 1)
305 cliptrace.ent = prog->edicts;
306 if (type == MOVE_WORLDONLY)
309 if (type == MOVE_MISSILE)
311 // LordHavoc: modified this, was = -15, now -= 15
312 for (i = 0;i < 3;i++)
319 // create the bounding box of the entire move
320 for (i = 0;i < 3;i++)
322 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
323 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
326 // debug override to test against everything
327 if (sv_debugmove.integer)
329 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
330 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
333 // if the passedict is world, make it NULL (to avoid two checks each time)
334 if (passedict == prog->edicts)
336 // precalculate prog value for passedict for comparisons
337 passedictprog = PRVM_EDICT_TO_PROG(passedict);
338 // precalculate passedict's owner edict pointer for comparisons
339 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
342 // because this uses World_EntitiestoBox, we know all entity boxes overlap
343 // the clip region, so we can skip culling checks in the loop below
344 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
345 if (numtouchedicts > MAX_EDICTS)
347 // this never happens
348 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
349 numtouchedicts = MAX_EDICTS;
351 for (i = 0;i < numtouchedicts;i++)
353 touch = touchedicts[i];
355 if (touch->fields.server->solid < SOLID_BBOX)
357 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
362 // don't clip against self
363 if (passedict == touch)
365 // don't clip owned entities against owner
366 if (traceowner == touch)
368 // don't clip owner against owned entities
369 if (passedictprog == touch->fields.server->owner)
371 // don't clip points against points (they can't collide)
372 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
376 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
378 // might interact, so do an exact clip
380 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
382 model = SV_GetModelFromEdict(touch);
383 pitchsign = SV_GetPitchSign(touch);
386 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);
388 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
389 Matrix4x4_Invert_Simple(&imatrix, &matrix);
390 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
391 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
392 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
393 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
394 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);
396 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);
398 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
402 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
403 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
404 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
414 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
415 #if COLLISIONPARANOID >= 1
416 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 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)
421 #if COLLISIONPARANOID >= 1
422 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)
424 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)
428 vec3_t hullmins, hullmaxs;
429 int i, bodysupercontents;
433 prvm_edict_t *traceowner, *touch;
435 // bounding box of entire move area
436 vec3_t clipboxmins, clipboxmaxs;
437 // size of the moving object
438 vec3_t clipmins, clipmaxs;
439 // size when clipping against monsters
440 vec3_t clipmins2, clipmaxs2;
441 // start and end origin of move
442 vec3_t clipstart, clipend;
445 // matrices to transform into/out of other entity's space
446 matrix4x4_t matrix, imatrix;
447 // model of other entity
449 // list of entities to test for collisions
451 static prvm_edict_t *touchedicts[MAX_EDICTS];
452 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
456 if (VectorCompare(mins, maxs))
458 vec3_t shiftstart, shiftend;
459 VectorAdd(start, mins, shiftstart);
460 VectorAdd(pEnd, mins, shiftend);
461 if (VectorCompare(start, pEnd))
462 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
464 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
465 VectorSubtract(trace.endpos, mins, trace.endpos);
469 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
471 // TRICK: make the trace 1 qu longer!
472 VectorSubtract(pEnd, start, end);
473 len = VectorNormalizeLength(end);
474 VectorMA(pEnd, collision_endposnudge.value, end, end);
477 VectorCopy(pEnd, end);
479 if (VectorCompare(mins, maxs))
481 vec3_t shiftstart, shiftend;
482 VectorAdd(start, mins, shiftstart);
483 VectorAdd(end, mins, shiftend);
484 if (VectorCompare(start, end))
485 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
487 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
488 VectorSubtract(trace.endpos, mins, trace.endpos);
493 VectorCopy(start, clipstart);
494 VectorCopy(end, clipend);
495 VectorCopy(mins, clipmins);
496 VectorCopy(maxs, clipmaxs);
497 VectorCopy(mins, clipmins2);
498 VectorCopy(maxs, clipmaxs2);
499 #if COLLISIONPARANOID >= 3
500 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
504 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
505 cliptrace.bmodelstartsolid = cliptrace.startsolid;
506 if (cliptrace.startsolid || cliptrace.fraction < 1)
507 cliptrace.ent = prog->edicts;
508 if (type == MOVE_WORLDONLY)
511 if (type == MOVE_MISSILE)
513 // LordHavoc: modified this, was = -15, now -= 15
514 for (i = 0;i < 3;i++)
521 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
522 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
523 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
526 VectorCopy(clipmins, hullmins);
527 VectorCopy(clipmaxs, hullmaxs);
530 // create the bounding box of the entire move
531 for (i = 0;i < 3;i++)
533 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
534 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
537 // debug override to test against everything
538 if (sv_debugmove.integer)
540 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
541 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
544 // if the passedict is world, make it NULL (to avoid two checks each time)
545 if (passedict == prog->edicts)
547 // precalculate prog value for passedict for comparisons
548 passedictprog = PRVM_EDICT_TO_PROG(passedict);
549 // figure out whether this is a point trace for comparisons
550 pointtrace = VectorCompare(clipmins, clipmaxs);
551 // precalculate passedict's owner edict pointer for comparisons
552 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
555 // because this uses World_EntitiestoBox, we know all entity boxes overlap
556 // the clip region, so we can skip culling checks in the loop below
557 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
558 if (numtouchedicts > MAX_EDICTS)
560 // this never happens
561 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
562 numtouchedicts = MAX_EDICTS;
564 for (i = 0;i < numtouchedicts;i++)
566 touch = touchedicts[i];
568 if (touch->fields.server->solid < SOLID_BBOX)
570 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
575 // don't clip against self
576 if (passedict == touch)
578 // don't clip owned entities against owner
579 if (traceowner == touch)
581 // don't clip owner against owned entities
582 if (passedictprog == touch->fields.server->owner)
584 // don't clip points against points (they can't collide)
585 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
589 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
591 // might interact, so do an exact clip
593 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
595 model = SV_GetModelFromEdict(touch);
596 pitchsign = SV_GetPitchSign(touch);
599 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);
601 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
602 Matrix4x4_Invert_Simple(&imatrix, &matrix);
603 VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
604 VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
605 VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
606 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
607 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);
609 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);
611 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
615 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
616 if(!VectorCompare(start, pEnd) && collision_endposnudge.value > 0)
617 Collision_ShortenTrace(&cliptrace, len / (len + collision_endposnudge.value), pEnd);
622 #if COLLISIONPARANOID >= 1
623 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)
628 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
631 VectorCopy(trace.endpos, temp);
632 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
633 #if COLLISIONPARANOID < 3
634 if (trace.startsolid || endstuck)
636 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" : "");
642 int SV_PointSuperContents(const vec3_t point)
644 int supercontents = 0;
648 // matrices to transform into/out of other entity's space
649 matrix4x4_t matrix, imatrix;
650 // model of other entity
653 // list of entities to test for collisions
655 static prvm_edict_t *touchedicts[MAX_EDICTS];
657 // get world supercontents at this point
658 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
659 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
661 // if sv_gameplayfix_swiminbmodels is off we're done
662 if (!sv_gameplayfix_swiminbmodels.integer)
663 return supercontents;
665 // get list of entities at this point
666 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
667 if (numtouchedicts > MAX_EDICTS)
669 // this never happens
670 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
671 numtouchedicts = MAX_EDICTS;
673 for (i = 0;i < numtouchedicts;i++)
675 touch = touchedicts[i];
677 // we only care about SOLID_BSP for pointcontents
678 if (touch->fields.server->solid != SOLID_BSP)
681 // might interact, so do an exact clip
682 model = SV_GetModelFromEdict(touch);
683 if (!model || !model->PointSuperContents)
685 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);
686 Matrix4x4_Invert_Simple(&imatrix, &matrix);
687 Matrix4x4_Transform(&imatrix, point, transformed);
688 frame = (int)touch->fields.server->frame;
689 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
692 return supercontents;
696 ===============================================================================
698 Linking entities into the world culling system
700 ===============================================================================
703 void SV_LinkEdict_TouchAreaGrid_Call(prvm_edict_t *touch, prvm_edict_t *ent)
706 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
707 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
708 prog->globals.server->time = sv.time;
709 prog->globals.server->trace_allsolid = false;
710 prog->globals.server->trace_startsolid = false;
711 prog->globals.server->trace_fraction = 1;
712 prog->globals.server->trace_inwater = false;
713 prog->globals.server->trace_inopen = true;
714 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
715 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
716 prog->globals.server->trace_plane_dist = 0;
717 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
718 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
720 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
722 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
724 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
726 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
729 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
731 int i, numtouchedicts, old_self, old_other;
733 static prvm_edict_t *touchedicts[MAX_EDICTS];
735 if (ent == prog->edicts)
736 return; // don't add the world
738 if (ent->priv.server->free)
741 if (ent->fields.server->solid == SOLID_NOT)
744 // build a list of edicts to touch, because the link loop can be corrupted
745 // by IncreaseEdicts called during touch functions
746 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
747 if (numtouchedicts > MAX_EDICTS)
749 // this never happens
750 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
751 numtouchedicts = MAX_EDICTS;
754 old_self = prog->globals.server->self;
755 old_other = prog->globals.server->other;
756 for (i = 0;i < numtouchedicts;i++)
758 touch = touchedicts[i];
759 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
761 SV_LinkEdict_TouchAreaGrid_Call(touch, ent);
764 prog->globals.server->self = old_self;
765 prog->globals.server->other = old_other;
768 static void RotateBBox(const vec3_t mins, const vec3_t maxs, const vec3_t angles, vec3_t rotatedmins, vec3_t rotatedmaxs)
772 Matrix4x4_CreateFromQuakeEntity(&m, 0, 0, 0, angles[PITCH], angles[YAW], angles[ROLL], 1.0);
774 v[0] = mins[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
775 VectorCopy(u, rotatedmins); VectorCopy(u, rotatedmaxs);
776 v[0] = maxs[0]; v[1] = mins[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
777 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
778 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
779 v[0] = mins[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
780 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
781 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
782 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = mins[2]; Matrix4x4_Transform(&m, v, u);
783 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
784 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
785 v[0] = mins[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
786 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
787 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
788 v[0] = maxs[0]; v[1] = mins[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
789 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
790 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
791 v[0] = mins[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
792 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
793 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
794 v[0] = maxs[0]; v[1] = maxs[1]; v[2] = maxs[2]; Matrix4x4_Transform(&m, v, u);
795 if(rotatedmins[0] > u[0]) rotatedmins[0] = u[0]; if(rotatedmins[1] > u[1]) rotatedmins[1] = u[1]; if(rotatedmins[2] > u[2]) rotatedmins[2] = u[2];
796 if(rotatedmaxs[0] < u[0]) rotatedmaxs[0] = u[0]; if(rotatedmaxs[1] < u[1]) rotatedmaxs[1] = u[1]; if(rotatedmaxs[2] < u[2]) rotatedmaxs[2] = u[2];
805 void SV_LinkEdict (prvm_edict_t *ent)
811 if (ent == prog->edicts)
812 return; // don't add the world
814 if (ent->priv.server->free)
817 modelindex = (int)ent->fields.server->modelindex;
818 if (modelindex < 0 || modelindex >= MAX_MODELS)
820 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
823 model = SV_GetModelByIndex(modelindex);
825 VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
826 VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
827 VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
831 if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
833 // TODO maybe should do this for rotating SOLID_BSP too? Would behave better with rotating doors
834 // TODO special handling for spheres?
835 RotateBBox(ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->angles, mins, maxs);
836 VectorAdd(ent->fields.server->origin, mins, mins);
837 VectorAdd(ent->fields.server->origin, maxs, maxs);
839 else if (ent->fields.server->solid == SOLID_BSP)
843 if (!model->TraceBox)
844 Con_DPrintf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
846 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
848 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
849 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
851 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
853 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
854 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
858 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
859 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
864 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
865 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
866 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
871 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
872 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
876 // to make items easier to pick up and allow them to be grabbed off
877 // of shelves, the abs sizes are expanded
879 if ((int)ent->fields.server->flags & FL_ITEM)
890 // because movement is clipped an epsilon away from an actual edge,
891 // we must fully check even when bounding boxes don't quite touch
900 VectorCopy(mins, ent->fields.server->absmin);
901 VectorCopy(maxs, ent->fields.server->absmax);
903 World_LinkEdict(&sv.world, ent, mins, maxs);
907 ===============================================================================
911 ===============================================================================
916 SV_TestEntityPosition
918 returns true if the entity is in solid currently
921 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
926 contents = SV_GenericHitSuperContentsMask(ent);
927 VectorAdd(ent->fields.server->origin, offset, org);
928 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
929 if (trace.startsupercontents & contents)
933 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
935 // q1bsp/hlbsp use hulls and if the entity does not exactly match
936 // a hull size it is incorrectly tested, so this code tries to
937 // 'fix' it slightly...
938 // FIXME: this breaks entities larger than the hull size
941 VectorAdd(org, ent->fields.server->mins, m1);
942 VectorAdd(org, ent->fields.server->maxs, m2);
943 VectorSubtract(m2, m1, s);
944 #define EPSILON (1.0f / 32.0f)
945 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
946 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
947 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
948 for (i = 0;i < 8;i++)
950 v[0] = (i & 1) ? m2[0] : m1[0];
951 v[1] = (i & 2) ? m2[1] : m1[1];
952 v[2] = (i & 4) ? m2[2] : m1[2];
953 if (SV_PointSuperContents(v) & contents)
958 // if the trace found a better position for the entity, move it there
959 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
962 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
963 VectorCopy(trace.endpos, ent->fields.server->origin);
965 // verify if the endpos is REALLY outside solid
966 VectorCopy(trace.endpos, org);
967 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
969 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
971 VectorCopy(org, ent->fields.server->origin);
982 void SV_CheckAllEnts (void)
987 // see if any solid entities are inside the final position
988 check = PRVM_NEXT_EDICT(prog->edicts);
989 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
991 if (check->priv.server->free)
993 if (check->fields.server->movetype == MOVETYPE_PUSH
994 || check->fields.server->movetype == MOVETYPE_NONE
995 || check->fields.server->movetype == MOVETYPE_FOLLOW
996 || check->fields.server->movetype == MOVETYPE_NOCLIP)
999 if (SV_TestEntityPosition (check, vec3_origin))
1000 Con_Print("entity in invalid position\n");
1004 // DRESK - Support for Entity Contents Transition Event
1007 SV_CheckContentsTransition
1009 returns true if entity had a valid contentstransition function call
1012 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
1014 int bValidFunctionCall;
1015 prvm_eval_t *contentstransition;
1017 // Default Valid Function Call to False
1018 bValidFunctionCall = false;
1020 if(ent->fields.server->watertype != nContents)
1021 { // Changed Contents
1022 // Acquire Contents Transition Function from QC
1023 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
1025 if(contentstransition->function)
1026 { // Valid Function; Execute
1027 // Assign Valid Function
1028 bValidFunctionCall = true;
1029 // Prepare Parameters (Original Contents, New Contents)
1030 // Original Contents
1031 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
1033 PRVM_G_FLOAT(OFS_PARM1) = nContents;
1035 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1036 // Execute VM Function
1037 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
1041 // Return if Function Call was Valid
1042 return bValidFunctionCall;
1051 void SV_CheckVelocity (prvm_edict_t *ent)
1059 for (i=0 ; i<3 ; i++)
1061 if (IS_NAN(ent->fields.server->velocity[i]))
1063 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1064 ent->fields.server->velocity[i] = 0;
1066 if (IS_NAN(ent->fields.server->origin[i]))
1068 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1069 ent->fields.server->origin[i] = 0;
1073 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1074 // player_run/player_stand1 does not horribly malfunction if the
1075 // velocity becomes a denormalized float
1076 if (VectorLength2(ent->fields.server->velocity) < 0.0001)
1077 VectorClear(ent->fields.server->velocity);
1079 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1080 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1081 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1083 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1084 ent->fields.server->velocity[0] *= wishspeed;
1085 ent->fields.server->velocity[1] *= wishspeed;
1086 ent->fields.server->velocity[2] *= wishspeed;
1094 Runs thinking code if time. There is some play in the exact time the think
1095 function will be called, because it is called before any movement is done
1096 in a frame. Not used for pushmove objects, because they must be exact.
1097 Returns false if the entity removed itself.
1100 qboolean SV_RunThink (prvm_edict_t *ent)
1104 // don't let things stay in the past.
1105 // it is possible to start that way by a trigger with a local time.
1106 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1109 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1111 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1112 ent->fields.server->nextthink = 0;
1113 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1114 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1115 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1116 // mods often set nextthink to time to cause a think every frame,
1117 // we don't want to loop in that case, so exit if the new nextthink is
1118 // <= the time the qc was told, also exit if it is past the end of the
1120 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1123 return !ent->priv.server->free;
1130 Two entities have touched, so run their touch functions
1131 returns true if the impact kept the origin of the touching entity intact
1134 extern void VM_SetTraceGlobals(const trace_t *trace);
1135 extern sizebuf_t vm_tempstringsbuf;
1136 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1138 int restorevm_tempstringsbuf_cursize;
1139 int old_self, old_other;
1141 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1144 old_self = prog->globals.server->self;
1145 old_other = prog->globals.server->other;
1146 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1148 VectorCopy(e1->fields.server->origin, org);
1150 VM_SetTraceGlobals(trace);
1152 prog->globals.server->time = sv.time;
1153 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1155 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1156 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1157 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1160 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1162 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1163 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1164 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1165 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1166 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1167 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1168 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1170 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1172 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1174 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1176 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1179 prog->globals.server->self = old_self;
1180 prog->globals.server->other = old_other;
1181 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1183 return VectorCompare(e1->fields.server->origin, org);
1191 Slide off of the impacting object
1192 returns the blocked flags (1 = floor, 2 = step / wall)
1195 #define STOP_EPSILON 0.1
1196 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1201 backoff = -DotProduct (in, normal) * overbounce;
1202 VectorMA(in, backoff, normal, out);
1204 for (i = 0;i < 3;i++)
1205 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1214 The basic solid body movement clip that slides along multiple planes
1215 Returns the clipflags if the velocity was modified (hit something solid)
1219 8 = teleported by touch method
1220 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1223 static float SV_Gravity (prvm_edict_t *ent);
1224 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1225 #define MAX_CLIP_PLANES 5
1226 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask, float stepheight)
1228 int blocked, bumpcount;
1229 int i, j, numplanes;
1230 float d, time_left, gravity;
1231 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1241 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1243 gravity = SV_Gravity(ent) * 0.5f;
1244 ent->fields.server->velocity[2] -= gravity;
1248 applygravity = false;
1249 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1253 VectorCopy(ent->fields.server->velocity, original_velocity);
1254 VectorCopy(ent->fields.server->velocity, primal_velocity);
1257 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1259 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1262 VectorScale(ent->fields.server->velocity, time_left, push);
1263 if(!SV_PushEntity(&trace, ent, push, false, false))
1265 // we got teleported by a touch function
1266 // let's abort the move
1271 if (trace.fraction == 1)
1273 if (trace.plane.normal[2])
1275 if (trace.plane.normal[2] > 0.7)
1282 Con_Printf ("SV_FlyMove: !trace.ent");
1283 trace.ent = prog->edicts;
1286 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1287 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1290 else if (stepheight)
1292 // step - handle it immediately
1298 //Con_Printf("step %f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1299 VectorSet(steppush, 0, 0, stepheight);
1300 VectorCopy(ent->fields.server->origin, org);
1301 SV_PushEntity(&steptrace, ent, steppush, false, false);
1302 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1303 SV_PushEntity(&steptrace2, ent, push, false, false);
1304 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1305 VectorSet(steppush, 0, 0, org[2] - ent->fields.server->origin[2]);
1306 SV_PushEntity(&steptrace3, ent, steppush, false, false);
1307 //Con_Printf("%f %f %f : ", ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
1308 // accept the new position if it made some progress...
1309 if (fabs(ent->fields.server->origin[0] - org[0]) >= 0.03125 || fabs(ent->fields.server->origin[1] - org[1]) >= 0.03125)
1311 //Con_Printf("accepted (delta %f %f %f)\n", ent->fields.server->origin[0] - org[0], ent->fields.server->origin[1] - org[1], ent->fields.server->origin[2] - org[2]);
1313 VectorCopy(ent->fields.server->origin, trace.endpos);
1314 time_left *= 1 - trace.fraction;
1320 //Con_Printf("REJECTED (delta %f %f %f)\n", ent->fields.server->origin[0] - org[0], ent->fields.server->origin[1] - org[1], ent->fields.server->origin[2] - org[2]);
1321 VectorCopy(org, ent->fields.server->origin);
1326 // step - return it to caller
1328 // save the trace for player extrafriction
1330 VectorCopy(trace.plane.normal, stepnormal);
1332 if (trace.fraction >= 0.001)
1334 // actually covered some distance
1335 VectorCopy(ent->fields.server->velocity, original_velocity);
1339 time_left *= 1 - trace.fraction;
1341 // clipped to another plane
1342 if (numplanes >= MAX_CLIP_PLANES)
1344 // this shouldn't really happen
1345 VectorClear(ent->fields.server->velocity);
1351 for (i = 0;i < numplanes;i++)
1352 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1356 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1361 VectorCopy(trace.plane.normal, planes[numplanes]);
1364 // modify original_velocity so it parallels all of the clip planes
1365 for (i = 0;i < numplanes;i++)
1367 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1368 for (j = 0;j < numplanes;j++)
1373 if (DotProduct(new_velocity, planes[j]) < 0)
1383 // go along this plane
1384 VectorCopy(new_velocity, ent->fields.server->velocity);
1388 // go along the crease
1391 VectorClear(ent->fields.server->velocity);
1395 CrossProduct(planes[0], planes[1], dir);
1396 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1397 VectorNormalize(dir);
1398 d = DotProduct(dir, ent->fields.server->velocity);
1399 VectorScale(dir, d, ent->fields.server->velocity);
1402 // if current velocity is against the original velocity,
1403 // stop dead to avoid tiny occilations in sloping corners
1404 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1406 VectorClear(ent->fields.server->velocity);
1411 //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]);
1414 if ((blocked & 1) == 0 && bumpcount > 1)
1416 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1417 // flag ONGROUND if there's ground under it
1418 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1422 // LordHavoc: this came from QW and allows you to get out of water more easily
1423 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1424 VectorCopy(primal_velocity, ent->fields.server->velocity);
1425 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1426 ent->fields.server->velocity[2] -= gravity;
1436 static float SV_Gravity (prvm_edict_t *ent)
1441 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1442 if (val!=0 && val->_float)
1443 ent_gravity = val->_float;
1446 return ent_gravity * sv_gravity.value * sv.frametime;
1451 ===============================================================================
1455 ===============================================================================
1462 Does not change the entities velocity at all
1463 The trace struct is filled with the trace that has been done.
1464 Returns true if the push did not result in the entity being teleported by QC code.
1467 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1474 VectorCopy(ent->fields.server->origin, original);
1475 VectorAdd (ent->fields.server->origin, push, end);
1477 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1478 type = MOVE_MISSILE;
1479 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1480 type = MOVE_NOMONSTERS; // only clip against bmodels
1484 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1486 while (trace->bmodelstartsolid && sv_gameplayfix_nudgeoutofsolid.integer)
1488 vec_t nudge = -trace->startdepth + sv_gameplayfix_nudgeoutofsolid_bias.value;
1489 VectorMA(ent->fields.server->origin, nudge, trace->startdepthnormal, ent->fields.server->origin);
1490 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1494 VectorCopy(original, ent->fields.server->origin);
1498 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1502 VectorCopy (trace->endpos, ent->fields.server->origin);
1506 if(!trace->startsolid)
1507 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)
1509 Con_Printf("something eeeeevil happened\n");
1514 SV_LinkEdict_TouchAreaGrid(ent);
1516 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))))
1517 return SV_Impact (ent, trace);
1529 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1532 int pusherowner, pusherprog;
1535 float savesolid, movetime2, pushltime;
1536 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1538 int numcheckentities;
1539 static prvm_edict_t *checkentities[MAX_EDICTS];
1540 dp_model_t *pushermodel;
1541 trace_t trace, trace2;
1542 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1543 static unsigned short moved_edicts[MAX_EDICTS];
1545 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])
1547 pusher->fields.server->ltime += movetime;
1551 switch ((int) pusher->fields.server->solid)
1553 // LordHavoc: valid pusher types
1556 case SOLID_SLIDEBOX:
1557 case SOLID_CORPSE: // LordHavoc: this would be weird...
1559 // LordHavoc: no collisions
1562 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1563 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1564 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1565 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1566 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1567 pusher->fields.server->ltime += movetime;
1568 SV_LinkEdict(pusher);
1571 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1574 index = (int) pusher->fields.server->modelindex;
1575 if (index < 1 || index >= MAX_MODELS)
1577 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1580 pushermodel = SV_GetModelByIndex(index);
1581 pusherowner = pusher->fields.server->owner;
1582 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1584 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1586 movetime2 = movetime;
1587 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1588 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1589 if (moveangle[0] || moveangle[2])
1591 for (i = 0;i < 3;i++)
1595 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1596 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1600 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1601 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1605 else if (moveangle[1])
1607 for (i = 0;i < 3;i++)
1611 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1612 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1616 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1617 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1623 for (i = 0;i < 3;i++)
1627 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1628 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1632 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1633 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1638 VectorNegate (moveangle, a);
1639 AngleVectorsFLU (a, forward, left, up);
1641 VectorCopy (pusher->fields.server->origin, pushorig);
1642 VectorCopy (pusher->fields.server->angles, pushang);
1643 pushltime = pusher->fields.server->ltime;
1645 // move the pusher to its final position
1647 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1648 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1649 pusher->fields.server->ltime += movetime;
1650 SV_LinkEdict(pusher);
1652 pushermodel = SV_GetModelFromEdict(pusher);
1653 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);
1654 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1656 savesolid = pusher->fields.server->solid;
1658 // see if any solid entities are inside the final position
1661 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1662 for (e = 0;e < numcheckentities;e++)
1664 prvm_edict_t *check = checkentities[e];
1665 int movetype = (int)check->fields.server->movetype;
1670 case MOVETYPE_FOLLOW:
1671 case MOVETYPE_NOCLIP:
1672 case MOVETYPE_FAKEPUSH:
1678 if (check->fields.server->owner == pusherprog)
1681 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1684 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1686 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1687 check->priv.server->waterposition_forceupdate = true;
1689 checkcontents = SV_GenericHitSuperContentsMask(check);
1691 // if the entity is standing on the pusher, it will definitely be moved
1692 // if the entity is not standing on the pusher, but is in the pusher's
1693 // final position, move it
1694 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1696 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);
1697 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1698 if (!trace.startsolid)
1700 //Con_Printf("- not in solid\n");
1708 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1709 org2[0] = DotProduct (org, forward);
1710 org2[1] = DotProduct (org, left);
1711 org2[2] = DotProduct (org, up);
1712 VectorSubtract (org2, org, move);
1713 VectorAdd (move, move1, move);
1716 VectorCopy (move1, move);
1718 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1720 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1721 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1722 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1724 // physics objects need better collisions than this code can do
1725 if (movetype == MOVETYPE_PHYSICS)
1727 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1728 SV_LinkEdict(check);
1729 SV_LinkEdict_TouchAreaGrid(check);
1733 // try moving the contacted entity
1734 pusher->fields.server->solid = SOLID_NOT;
1735 if(!SV_PushEntity (&trace, check, move, true, true))
1737 // entity "check" got teleported
1738 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1739 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1740 continue; // pushed enough
1742 // FIXME: turn players specially
1743 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1744 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1745 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1747 // this trace.fraction < 1 check causes items to fall off of pushers
1748 // if they pass under or through a wall
1749 // the groundentity check causes items to fall off of ledges
1750 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1751 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1753 // if it is still inside the pusher, block
1754 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);
1755 if (trace.startsolid)
1757 // try moving the contacted entity a tiny bit further to account for precision errors
1759 pusher->fields.server->solid = SOLID_NOT;
1760 VectorScale(move, 1.1, move2);
1761 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1762 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1763 if(!SV_PushEntity (&trace2, check, move2, true, true))
1765 // entity "check" got teleported
1768 pusher->fields.server->solid = savesolid;
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 less to account for precision errors
1773 pusher->fields.server->solid = SOLID_NOT;
1774 VectorScale(move, 0.9, move2);
1775 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1776 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1777 if(!SV_PushEntity (&trace2, check, move2, true, true))
1779 // entity "check" got teleported
1782 pusher->fields.server->solid = savesolid;
1783 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);
1784 if (trace.startsolid)
1786 // still inside pusher, so it's really blocked
1789 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1791 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1794 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1795 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1799 VectorCopy (pushorig, pusher->fields.server->origin);
1800 VectorCopy (pushang, pusher->fields.server->angles);
1801 pusher->fields.server->ltime = pushltime;
1802 SV_LinkEdict(pusher);
1804 // move back any entities we already moved
1805 for (i = 0;i < num_moved;i++)
1807 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1808 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1809 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1813 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1814 if (pusher->fields.server->blocked)
1816 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1817 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1818 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1825 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1826 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1827 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1836 void SV_Physics_Pusher (prvm_edict_t *ent)
1838 float thinktime, oldltime, movetime;
1840 oldltime = ent->fields.server->ltime;
1842 thinktime = ent->fields.server->nextthink;
1843 if (thinktime < ent->fields.server->ltime + sv.frametime)
1845 movetime = thinktime - ent->fields.server->ltime;
1850 movetime = sv.frametime;
1853 // advances ent->fields.server->ltime if not blocked
1854 SV_PushMove (ent, movetime);
1856 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1858 ent->fields.server->nextthink = 0;
1859 prog->globals.server->time = sv.time;
1860 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1861 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1862 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1868 ===============================================================================
1872 ===============================================================================
1875 static float unstickoffsets[] =
1877 // poutting -/+z changes first as they are least weird
1892 typedef enum unstickresult_e
1900 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1904 // if not stuck in a bmodel, just return
1905 if (!SV_TestEntityPosition(ent, vec3_origin))
1906 return UNSTICK_GOOD;
1908 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1910 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1912 VectorCopy(unstickoffsets + i, offset);
1914 //SV_LinkEdict_TouchAreaGrid(ent);
1915 return UNSTICK_UNSTUCK;
1919 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1920 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1922 for(i = 2; i <= maxunstick; ++i)
1924 VectorClear(offset);
1926 if (!SV_TestEntityPosition(ent, offset))
1929 //SV_LinkEdict_TouchAreaGrid(ent);
1930 return UNSTICK_UNSTUCK;
1933 if (!SV_TestEntityPosition(ent, offset))
1936 //SV_LinkEdict_TouchAreaGrid(ent);
1937 return UNSTICK_UNSTUCK;
1941 return UNSTICK_STUCK;
1944 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1947 switch(SV_UnstickEntityReturnOffset(ent, offset))
1951 case UNSTICK_UNSTUCK:
1952 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]);
1955 if (developer_extra.integer)
1956 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1959 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1968 This is a big hack to try and fix the rare case of getting stuck in the world
1972 void SV_CheckStuck (prvm_edict_t *ent)
1976 switch(SV_UnstickEntityReturnOffset(ent, offset))
1979 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1981 case UNSTICK_UNSTUCK:
1982 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]);
1985 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1986 if (!SV_TestEntityPosition(ent, offset))
1988 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1990 //SV_LinkEdict_TouchAreaGrid(ent);
1993 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1996 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
2006 qboolean SV_CheckWater (prvm_edict_t *ent)
2009 int nNativeContents;
2012 point[0] = ent->fields.server->origin[0];
2013 point[1] = ent->fields.server->origin[1];
2014 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
2016 // DRESK - Support for Entity Contents Transition Event
2017 // NOTE: Some logic needed to be slightly re-ordered
2018 // to not affect performance and allow for the feature.
2020 // Acquire Super Contents Prior to Resets
2021 cont = SV_PointSuperContents(point);
2022 // Acquire Native Contents Here
2023 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
2025 // DRESK - Support for Entity Contents Transition Event
2026 if(ent->fields.server->watertype)
2027 // Entity did NOT Spawn; Check
2028 SV_CheckContentsTransition(ent, nNativeContents);
2031 ent->fields.server->waterlevel = 0;
2032 ent->fields.server->watertype = CONTENTS_EMPTY;
2033 cont = SV_PointSuperContents(point);
2034 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2036 ent->fields.server->watertype = nNativeContents;
2037 ent->fields.server->waterlevel = 1;
2038 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2039 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2041 ent->fields.server->waterlevel = 2;
2042 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2043 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2044 ent->fields.server->waterlevel = 3;
2048 return ent->fields.server->waterlevel > 1;
2057 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2060 vec3_t forward, into, side;
2062 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2063 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2065 // cut the tangential velocity
2066 i = DotProduct (stepnormal, ent->fields.server->velocity);
2067 VectorScale (stepnormal, i, into);
2068 VectorSubtract (ent->fields.server->velocity, into, side);
2069 ent->fields.server->velocity[0] = side[0] * (1 + d);
2070 ent->fields.server->velocity[1] = side[1] * (1 + d);
2076 =====================
2079 Player has come to a dead stop, possibly due to the problem with limited
2080 float precision at some angle joins in the BSP hull.
2082 Try fixing by pushing one pixel in each direction.
2084 This is a hack, but in the interest of good gameplay...
2085 ======================
2087 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2092 VectorCopy (ent->fields.server->origin, oldorg);
2095 for (i=0 ; i<8 ; i++)
2097 // try pushing a little in an axial direction
2100 case 0: dir[0] = 2; dir[1] = 0; break;
2101 case 1: dir[0] = 0; dir[1] = 2; break;
2102 case 2: dir[0] = -2; dir[1] = 0; break;
2103 case 3: dir[0] = 0; dir[1] = -2; break;
2104 case 4: dir[0] = 2; dir[1] = 2; break;
2105 case 5: dir[0] = -2; dir[1] = 2; break;
2106 case 6: dir[0] = 2; dir[1] = -2; break;
2107 case 7: dir[0] = -2; dir[1] = -2; break;
2110 SV_PushEntity (&trace, ent, dir, false, true);
2112 // retry the original move
2113 ent->fields.server->velocity[0] = oldvel[0];
2114 ent->fields.server->velocity[1] = oldvel[1];
2115 ent->fields.server->velocity[2] = 0;
2116 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2118 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2119 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2121 Con_DPrint("TryUnstick - success.\n");
2125 // go back to the original pos and try again
2126 VectorCopy (oldorg, ent->fields.server->origin);
2130 VectorClear (ent->fields.server->velocity);
2131 Con_DPrint("TryUnstick - failure.\n");
2137 =====================
2140 Only used by players
2141 ======================
2143 void SV_WalkMove (prvm_edict_t *ent)
2147 //int originalmove_clip;
2148 int originalmove_flags;
2149 int originalmove_groundentity;
2150 int hitsupercontentsmask;
2152 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2153 trace_t downtrace, trace;
2154 qboolean applygravity;
2156 // if frametime is 0 (due to client sending the same timestamp twice),
2158 if (sv.frametime <= 0)
2161 SV_CheckStuck (ent);
2163 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2165 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2167 SV_CheckVelocity(ent);
2169 // do a regular slide move unless it looks like you ran into a step
2170 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2172 VectorCopy (ent->fields.server->origin, start_origin);
2173 VectorCopy (ent->fields.server->velocity, start_velocity);
2175 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask, sv_gameplayfix_stepmultipletimes.integer ? sv_stepheight.value : 0);
2177 if(sv_gameplayfix_downtracesupportsongroundflag.integer)
2180 // only try this if there was no floor in the way in the trace (no,
2181 // this check seems to be not REALLY necessary, because if clip & 1,
2182 // our trace will hit that thing too)
2183 VectorSet(upmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + 1);
2184 VectorSet(downmove, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
2185 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
2186 type = MOVE_MISSILE;
2187 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
2188 type = MOVE_NOMONSTERS; // only clip against bmodels
2191 trace = SV_TraceBox(upmove, ent->fields.server->mins, ent->fields.server->maxs, downmove, type, ent, SV_GenericHitSuperContentsMask(ent));
2192 if(trace.fraction < 1 && trace.plane.normal[2] > 0.7)
2193 clip |= 1; // but we HAVE found a floor
2196 // if the move did not hit the ground at any point, we're not on ground
2198 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2200 SV_CheckVelocity(ent);
2202 SV_LinkEdict_TouchAreaGrid(ent);
2204 if(clip & 8) // teleport
2207 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2210 if (sv_nostep.integer)
2213 VectorCopy(ent->fields.server->origin, originalmove_origin);
2214 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2215 //originalmove_clip = clip;
2216 originalmove_flags = (int)ent->fields.server->flags;
2217 originalmove_groundentity = ent->fields.server->groundentity;
2219 // if move didn't block on a step, return
2222 // if move was not trying to move into the step, return
2223 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2226 if (ent->fields.server->movetype != MOVETYPE_FLY)
2228 // return if gibbed by a trigger
2229 if (ent->fields.server->movetype != MOVETYPE_WALK)
2232 // only step up while jumping if that is enabled
2233 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2234 if (!oldonground && ent->fields.server->waterlevel == 0)
2238 // try moving up and forward to go up a step
2239 // back to start pos
2240 VectorCopy (start_origin, ent->fields.server->origin);
2241 VectorCopy (start_velocity, ent->fields.server->velocity);
2244 VectorClear (upmove);
2245 upmove[2] = sv_stepheight.value;
2246 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2248 // we got teleported when upstepping... must abort the move
2253 ent->fields.server->velocity[2] = 0;
2254 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask, 0);
2255 ent->fields.server->velocity[2] += start_velocity[2];
2258 // we got teleported when upstepping... must abort the move
2259 // note that z velocity handling may not be what QC expects here, but we cannot help it
2263 SV_CheckVelocity(ent);
2265 SV_LinkEdict_TouchAreaGrid(ent);
2267 // check for stuckness, possibly due to the limited precision of floats
2268 // in the clipping hulls
2270 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2271 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2273 //Con_Printf("wall\n");
2274 // stepping up didn't make any progress, revert to original move
2275 VectorCopy(originalmove_origin, ent->fields.server->origin);
2276 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2277 //clip = originalmove_clip;
2278 ent->fields.server->flags = originalmove_flags;
2279 ent->fields.server->groundentity = originalmove_groundentity;
2280 // now try to unstick if needed
2281 //clip = SV_TryUnstick (ent, oldvel);
2285 //Con_Printf("step - ");
2287 // extra friction based on view angle
2288 if (clip & 2 && sv_wallfriction.integer)
2289 SV_WallFriction (ent, stepnormal);
2291 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2292 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))
2296 VectorClear (downmove);
2297 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2298 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2300 // we got teleported when downstepping... must abort the move
2304 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2306 // this has been disabled so that you can't jump when you are stepping
2307 // up while already jumping (also known as the Quake2 double jump bug)
2309 // LordHavoc: disabled this check so you can walk on monsters/players
2310 //if (ent->fields.server->solid == SOLID_BSP)
2312 //Con_Printf("onground\n");
2313 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2314 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2320 //Con_Printf("slope\n");
2321 // if the push down didn't end up on good ground, use the move without
2322 // the step up. This happens near wall / slope combinations, and can
2323 // cause the player to hop up higher on a slope too steep to climb
2324 VectorCopy(originalmove_origin, ent->fields.server->origin);
2325 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2326 //clip = originalmove_clip;
2327 ent->fields.server->flags = originalmove_flags;
2328 ent->fields.server->groundentity = originalmove_groundentity;
2331 SV_CheckVelocity(ent);
2333 SV_LinkEdict_TouchAreaGrid(ent);
2336 //============================================================================
2342 Entities that are "stuck" to another entity
2345 void SV_Physics_Follow (prvm_edict_t *ent)
2347 vec3_t vf, vr, vu, angles, v;
2351 if (!SV_RunThink (ent))
2354 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2355 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2356 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])
2358 // quick case for no rotation
2359 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2363 angles[0] = -ent->fields.server->punchangle[0];
2364 angles[1] = ent->fields.server->punchangle[1];
2365 angles[2] = ent->fields.server->punchangle[2];
2366 AngleVectors (angles, vf, vr, vu);
2367 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];
2368 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];
2369 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];
2370 angles[0] = -e->fields.server->angles[0];
2371 angles[1] = e->fields.server->angles[1];
2372 angles[2] = e->fields.server->angles[2];
2373 AngleVectors (angles, vf, vr, vu);
2374 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2375 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2376 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2378 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2380 //SV_LinkEdict_TouchAreaGrid(ent);
2384 ==============================================================================
2388 ==============================================================================
2393 SV_CheckWaterTransition
2397 void SV_CheckWaterTransition (prvm_edict_t *ent)
2400 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2401 if (!ent->fields.server->watertype)
2403 // just spawned here
2404 ent->fields.server->watertype = cont;
2405 ent->fields.server->waterlevel = 1;
2409 // DRESK - Support for Entity Contents Transition Event
2410 // NOTE: Call here BEFORE updating the watertype below,
2411 // and suppress watersplash sound if a valid function
2412 // call was made to allow for custom "splash" sounds.
2413 if( !SV_CheckContentsTransition(ent, cont) )
2414 { // Contents Transition Function Invalid; Potentially Play Water Sound
2415 // check if the entity crossed into or out of water
2416 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2417 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2420 if (cont <= CONTENTS_WATER)
2422 ent->fields.server->watertype = cont;
2423 ent->fields.server->waterlevel = 1;
2427 ent->fields.server->watertype = CONTENTS_EMPTY;
2428 ent->fields.server->waterlevel = 0;
2436 Toss, bounce, and fly movement. When onground, do nothing.
2439 void SV_Physics_Toss (prvm_edict_t *ent)
2445 prvm_edict_t *groundentity;
2447 // if onground, return without moving
2448 if ((int)ent->fields.server->flags & FL_ONGROUND)
2450 groundentity = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
2451 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2453 // don't stick to ground if onground and moving upward
2454 ent->fields.server->flags -= FL_ONGROUND;
2456 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2458 // we can trust FL_ONGROUND if groundentity is world because it never moves
2461 else if (ent->priv.server->suspendedinairflag && groundentity->priv.server->free)
2463 // if ent was supported by a brush model on previous frame,
2464 // and groundentity is now freed, set groundentity to 0 (world)
2465 // which leaves it suspended in the air
2466 ent->fields.server->groundentity = 0;
2467 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2470 else if (BoxesOverlap(ent->priv.server->cullmins, ent->priv.server->cullmaxs, groundentity->priv.server->cullmins, groundentity->priv.server->cullmaxs))
2472 // don't slide if still touching the groundentity
2476 ent->priv.server->suspendedinairflag = false;
2478 SV_CheckVelocity (ent);
2481 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2482 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2485 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2487 movetime = sv.frametime;
2488 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2491 VectorScale (ent->fields.server->velocity, movetime, move);
2492 if(!SV_PushEntity (&trace, ent, move, true, true))
2493 return; // teleported
2494 if (ent->priv.server->free)
2496 if (trace.bmodelstartsolid)
2498 // try to unstick the entity
2499 SV_UnstickEntity(ent);
2500 if(!SV_PushEntity (&trace, ent, move, false, true))
2501 return; // teleported
2502 if (ent->priv.server->free)
2505 if (trace.fraction == 1)
2507 movetime *= 1 - min(1, trace.fraction);
2508 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2511 float bouncefactor = 1.0f;
2512 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2513 if (val!=0 && val->_float)
2514 bouncefactor = val->_float;
2516 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2517 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2519 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2521 float d, ent_gravity;
2523 float bouncefactor = 0.5f;
2524 float bouncestop = 60.0f / 800.0f;
2526 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2527 if (val!=0 && val->_float)
2528 bouncefactor = val->_float;
2530 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2531 if (val!=0 && val->_float)
2532 bouncestop = val->_float;
2534 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2535 // LordHavoc: fixed grenades not bouncing when fired down a slope
2536 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2537 if (val!=0 && val->_float)
2538 ent_gravity = val->_float;
2541 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2543 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2544 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2546 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2547 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2548 VectorClear (ent->fields.server->velocity);
2549 VectorClear (ent->fields.server->avelocity);
2552 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2556 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2558 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2559 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2560 VectorClear (ent->fields.server->velocity);
2561 VectorClear (ent->fields.server->avelocity);
2564 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2569 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2570 if (trace.plane.normal[2] > 0.7)
2572 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2573 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2574 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2575 ent->priv.server->suspendedinairflag = true;
2576 VectorClear (ent->fields.server->velocity);
2577 VectorClear (ent->fields.server->avelocity);
2580 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2582 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2586 // check for in water
2587 SV_CheckWaterTransition (ent);
2591 ===============================================================================
2595 ===============================================================================
2602 Monsters freefall when they don't have a ground entity, otherwise
2603 all movement is done with discrete steps.
2605 This is also used for objects that have become still on the ground, but
2606 will fall if the floor is pulled out from under them.
2609 void SV_Physics_Step (prvm_edict_t *ent)
2611 int flags = (int)ent->fields.server->flags;
2614 // Backup Velocity in the event that movetypesteplandevent is called,
2615 // to provide a parameter with the entity's velocity at impact.
2616 prvm_eval_t *movetypesteplandevent;
2617 vec3_t backupVelocity;
2618 VectorCopy(ent->fields.server->velocity, backupVelocity);
2619 // don't fall at all if fly/swim
2620 if (!(flags & (FL_FLY | FL_SWIM)))
2622 if (flags & FL_ONGROUND)
2624 // freefall if onground and moving upward
2625 // freefall if not standing on a world surface (it may be a lift or trap door)
2626 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2628 ent->fields.server->flags -= FL_ONGROUND;
2629 SV_CheckVelocity(ent);
2630 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2632 SV_LinkEdict_TouchAreaGrid(ent);
2633 ent->priv.server->waterposition_forceupdate = true;
2638 // freefall if not onground
2639 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2641 SV_CheckVelocity(ent);
2642 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent), 0);
2644 SV_LinkEdict_TouchAreaGrid(ent);
2647 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2649 // DRESK - Check for Entity Land Event Function
2650 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2652 if(movetypesteplandevent->function)
2653 { // Valid Function; Execute
2654 // Prepare Parameters
2655 // Assign Velocity at Impact
2656 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2657 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2658 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2660 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2661 // Execute VM Function
2662 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2665 // Check for Engine Landing Sound
2666 if(sv_sound_land.string)
2667 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2669 ent->priv.server->waterposition_forceupdate = true;
2674 if (!SV_RunThink(ent))
2677 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2679 ent->priv.server->waterposition_forceupdate = false;
2680 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2681 SV_CheckWaterTransition(ent);
2685 //============================================================================
2687 static void SV_Physics_Entity (prvm_edict_t *ent)
2689 // don't run think/move on newly spawned projectiles as it messes up
2690 // movement interpolation and rocket trails, and is inconsistent with
2691 // respect to entities spawned in the same frame
2692 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2693 // but if it spawns a lower numbered ent, it doesn't - this never moves
2694 // ents in the first frame regardless)
2695 qboolean runmove = ent->priv.server->move;
2696 ent->priv.server->move = true;
2697 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2699 switch ((int) ent->fields.server->movetype)
2702 case MOVETYPE_FAKEPUSH:
2703 SV_Physics_Pusher (ent);
2706 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2707 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2710 case MOVETYPE_FOLLOW:
2711 SV_Physics_Follow (ent);
2713 case MOVETYPE_NOCLIP:
2714 if (SV_RunThink(ent))
2717 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2718 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2723 SV_Physics_Step (ent);
2726 if (SV_RunThink (ent))
2730 case MOVETYPE_BOUNCE:
2731 case MOVETYPE_BOUNCEMISSILE:
2732 case MOVETYPE_FLYMISSILE:
2735 if (SV_RunThink (ent))
2736 SV_Physics_Toss (ent);
2738 case MOVETYPE_PHYSICS:
2739 if (SV_RunThink(ent))
2742 SV_LinkEdict_TouchAreaGrid(ent);
2746 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2751 void SV_Physics_ClientMove(void)
2754 ent = host_client->edict;
2756 // call player physics, this needs the proper frametime
2757 prog->globals.server->frametime = sv.frametime;
2760 // call standard client pre-think, with frametime = 0
2761 prog->globals.server->time = sv.time;
2762 prog->globals.server->frametime = 0;
2763 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2764 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2765 prog->globals.server->frametime = sv.frametime;
2767 // make sure the velocity is sane (not a NaN)
2768 SV_CheckVelocity(ent);
2770 // perform MOVETYPE_WALK behavior
2773 // call standard player post-think, with frametime = 0
2774 prog->globals.server->time = sv.time;
2775 prog->globals.server->frametime = 0;
2776 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2777 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2778 prog->globals.server->frametime = sv.frametime;
2780 if(ent->fields.server->fixangle)
2782 // angle fixing was requested by physics code...
2783 // so store the current angles for later use
2784 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2785 host_client->fixangle_angles_set = TRUE;
2787 // and clear fixangle for the next frame
2788 ent->fields.server->fixangle = 0;
2792 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2794 // don't do physics on disconnected clients, FrikBot relies on this
2795 if (!host_client->spawned)
2798 // make sure the velocity is sane (not a NaN)
2799 SV_CheckVelocity(ent);
2801 // don't run physics here if running asynchronously
2802 if (host_client->clmovement_inputtimeout <= 0)
2805 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2808 // make sure the velocity is still sane (not a NaN)
2809 SV_CheckVelocity(ent);
2811 // call standard client pre-think
2812 prog->globals.server->time = sv.time;
2813 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2814 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2816 // make sure the velocity is still sane (not a NaN)
2817 SV_CheckVelocity(ent);
2820 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2822 // don't do physics on disconnected clients, FrikBot relies on this
2823 if (!host_client->spawned)
2826 // make sure the velocity is sane (not a NaN)
2827 SV_CheckVelocity(ent);
2829 // call standard player post-think
2830 prog->globals.server->time = sv.time;
2831 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2832 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2834 // make sure the velocity is still sane (not a NaN)
2835 SV_CheckVelocity(ent);
2837 if(ent->fields.server->fixangle)
2839 // angle fixing was requested by physics code...
2840 // so store the current angles for later use
2841 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2842 host_client->fixangle_angles_set = TRUE;
2844 // and clear fixangle for the next frame
2845 ent->fields.server->fixangle = 0;
2848 // decrement the countdown variable used to decide when to go back to
2849 // synchronous physics
2850 if (host_client->clmovement_inputtimeout > sv.frametime)
2851 host_client->clmovement_inputtimeout -= sv.frametime;
2853 host_client->clmovement_inputtimeout = 0;
2856 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2858 // don't do physics on disconnected clients, FrikBot relies on this
2859 if (!host_client->spawned)
2861 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2865 // make sure the velocity is sane (not a NaN)
2866 SV_CheckVelocity(ent);
2868 switch ((int) ent->fields.server->movetype)
2871 case MOVETYPE_FAKEPUSH:
2872 SV_Physics_Pusher (ent);
2875 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2876 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2879 case MOVETYPE_FOLLOW:
2880 SV_Physics_Follow (ent);
2882 case MOVETYPE_NOCLIP:
2885 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2886 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2889 SV_Physics_Step (ent);
2893 // don't run physics here if running asynchronously
2894 if (host_client->clmovement_inputtimeout <= 0)
2898 case MOVETYPE_BOUNCE:
2899 case MOVETYPE_BOUNCEMISSILE:
2900 case MOVETYPE_FLYMISSILE:
2903 SV_Physics_Toss (ent);
2909 case MOVETYPE_PHYSICS:
2913 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2917 SV_CheckVelocity (ent);
2920 SV_LinkEdict_TouchAreaGrid(ent);
2922 SV_CheckVelocity (ent);
2931 void SV_Physics (void)
2936 // let the progs know that a new frame has started
2937 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2938 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2939 prog->globals.server->time = sv.time;
2940 prog->globals.server->frametime = sv.frametime;
2941 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2943 // run physics engine
2944 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2947 // treat each object in turn
2950 // if force_retouch, relink all the entities
2951 if (prog->globals.server->force_retouch > 0)
2952 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2953 if (!ent->priv.server->free)
2954 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2956 if (sv_gameplayfix_consistentplayerprethink.integer)
2958 // run physics on the client entities in 3 stages
2959 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2960 if (!ent->priv.server->free)
2961 SV_Physics_ClientEntity_PreThink(ent);
2963 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2964 if (!ent->priv.server->free)
2965 SV_Physics_ClientEntity(ent);
2967 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2968 if (!ent->priv.server->free)
2969 SV_Physics_ClientEntity_PostThink(ent);
2973 // run physics on the client entities
2974 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2976 if (!ent->priv.server->free)
2978 SV_Physics_ClientEntity_PreThink(ent);
2979 SV_Physics_ClientEntity(ent);
2980 SV_Physics_ClientEntity_PostThink(ent);
2985 // run physics on all the non-client entities
2986 if (!sv_freezenonclients.integer)
2988 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2989 if (!ent->priv.server->free)
2990 SV_Physics_Entity(ent);
2991 // make a second pass to see if any ents spawned this frame and make
2992 // sure they run their move/think
2993 if (sv_gameplayfix_delayprojectiles.integer < 0)
2994 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2995 if (!ent->priv.server->move && !ent->priv.server->free)
2996 SV_Physics_Entity(ent);
2999 if (prog->globals.server->force_retouch > 0)
3000 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
3002 // LordHavoc: endframe support
3003 if (prog->funcoffsets.EndFrame)
3005 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
3006 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
3007 prog->globals.server->time = sv.time;
3008 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
3011 // decrement prog->num_edicts if the highest number entities died
3012 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
3014 if (!sv_freezenonclients.integer)
3015 sv.time += sv.frametime;