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);
47 ===============================================================================
51 ===============================================================================
54 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
59 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
60 if (val && val->_float)
61 return (int)val->_float;
62 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
64 if ((int)passedict->fields.server->flags & FL_MONSTER)
65 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
67 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
69 else if (passedict->fields.server->solid == SOLID_CORPSE)
70 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
71 else if (passedict->fields.server->solid == SOLID_TRIGGER)
72 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
74 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
77 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
85 trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
87 int i, bodysupercontents;
90 prvm_edict_t *traceowner, *touch;
92 // bounding box of entire move area
93 vec3_t clipboxmins, clipboxmaxs;
94 // size when clipping against monsters
95 vec3_t clipmins2, clipmaxs2;
96 // start and end origin of move
100 // matrices to transform into/out of other entity's space
101 matrix4x4_t matrix, imatrix;
102 // model of other entity
104 // list of entities to test for collisions
106 prvm_edict_t *touchedicts[MAX_EDICTS];
108 VectorCopy(start, clipstart);
109 VectorClear(clipmins2);
110 VectorClear(clipmaxs2);
111 #if COLLISIONPARANOID >= 3
112 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
116 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
117 cliptrace.bmodelstartsolid = cliptrace.startsolid;
118 if (cliptrace.startsolid || cliptrace.fraction < 1)
119 cliptrace.ent = prog->edicts;
120 if (type == MOVE_WORLDONLY)
123 if (type == MOVE_MISSILE)
125 // LordHavoc: modified this, was = -15, now -= 15
126 for (i = 0;i < 3;i++)
133 // create the bounding box of the entire move
134 for (i = 0;i < 3;i++)
136 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
137 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
140 // debug override to test against everything
141 if (sv_debugmove.integer)
143 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
144 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
147 // if the passedict is world, make it NULL (to avoid two checks each time)
148 if (passedict == prog->edicts)
150 // precalculate prog value for passedict for comparisons
151 passedictprog = PRVM_EDICT_TO_PROG(passedict);
152 // precalculate passedict's owner edict pointer for comparisons
153 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
156 // because this uses World_EntitiestoBox, we know all entity boxes overlap
157 // the clip region, so we can skip culling checks in the loop below
158 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
159 if (numtouchedicts > MAX_EDICTS)
161 // this never happens
162 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
163 numtouchedicts = MAX_EDICTS;
165 for (i = 0;i < numtouchedicts;i++)
167 touch = touchedicts[i];
169 if (touch->fields.server->solid < SOLID_BBOX)
171 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
176 // don't clip against self
177 if (passedict == touch)
179 // don't clip owned entities against owner
180 if (traceowner == touch)
182 // don't clip owner against owned entities
183 if (passedictprog == touch->fields.server->owner)
185 // don't clip points against points (they can't collide)
186 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
190 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
192 // might interact, so do an exact clip
194 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
196 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
197 // if the modelindex is 0, it shouldn't be SOLID_BSP!
198 if (modelindex > 0 && modelindex < MAX_MODELS)
199 model = sv.models[(int)touch->fields.server->modelindex];
202 ((modelindex = (int)touch->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[(int)touch->fields.server->modelindex]))
204 model->type == mod_alias
207 (((unsigned char)PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
209 ((gamemode == GAME_TENEBRAE) && ((unsigned int)touch->fields.server->effects & (16 | 32)))
215 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);
217 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
218 Matrix4x4_Invert_Simple(&imatrix, &matrix);
219 if ((int)touch->fields.server->flags & FL_MONSTER)
220 Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
222 Collision_ClipPointToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
224 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
236 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
237 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
239 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
242 int i, bodysupercontents;
245 prvm_edict_t *traceowner, *touch;
247 // bounding box of entire move area
248 vec3_t clipboxmins, clipboxmaxs;
249 // size when clipping against monsters
250 vec3_t clipmins2, clipmaxs2;
251 // start and end origin of move
252 vec3_t clipstart, clipend;
255 // matrices to transform into/out of other entity's space
256 matrix4x4_t matrix, imatrix;
257 // model of other entity
259 // list of entities to test for collisions
261 prvm_edict_t *touchedicts[MAX_EDICTS];
262 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
266 if(!VectorCompare(start, pEnd))
268 // TRICK: make the trace 1 qu longer!
269 VectorSubtract(pEnd, start, end);
270 len = VectorNormalizeLength(end);
271 VectorAdd(pEnd, end, end);
274 VectorCopy(pEnd, end);
277 if (VectorCompare(start, end))
278 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
280 VectorCopy(start, clipstart);
281 VectorCopy(end, clipend);
282 VectorClear(clipmins2);
283 VectorClear(clipmaxs2);
284 #if COLLISIONPARANOID >= 3
285 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
289 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask);
290 cliptrace.bmodelstartsolid = cliptrace.startsolid;
291 if (cliptrace.startsolid || cliptrace.fraction < 1)
292 cliptrace.ent = prog->edicts;
293 if (type == MOVE_WORLDONLY)
296 if (type == MOVE_MISSILE)
298 // LordHavoc: modified this, was = -15, now -= 15
299 for (i = 0;i < 3;i++)
306 // create the bounding box of the entire move
307 for (i = 0;i < 3;i++)
309 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
310 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
313 // debug override to test against everything
314 if (sv_debugmove.integer)
316 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
317 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
320 // if the passedict is world, make it NULL (to avoid two checks each time)
321 if (passedict == prog->edicts)
323 // precalculate prog value for passedict for comparisons
324 passedictprog = PRVM_EDICT_TO_PROG(passedict);
325 // precalculate passedict's owner edict pointer for comparisons
326 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
329 // because this uses World_EntitiestoBox, we know all entity boxes overlap
330 // the clip region, so we can skip culling checks in the loop below
331 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
332 if (numtouchedicts > MAX_EDICTS)
334 // this never happens
335 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
336 numtouchedicts = MAX_EDICTS;
338 for (i = 0;i < numtouchedicts;i++)
340 touch = touchedicts[i];
342 if (touch->fields.server->solid < SOLID_BBOX)
344 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
349 // don't clip against self
350 if (passedict == touch)
352 // don't clip owned entities against owner
353 if (traceowner == touch)
355 // don't clip owner against owned entities
356 if (passedictprog == touch->fields.server->owner)
358 // don't clip points against points (they can't collide)
359 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
363 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
365 // might interact, so do an exact clip
367 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
369 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
370 // if the modelindex is 0, it shouldn't be SOLID_BSP!
371 if (modelindex > 0 && modelindex < MAX_MODELS)
372 model = sv.models[(int)touch->fields.server->modelindex];
375 ((modelindex = (int)touch->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[(int)touch->fields.server->modelindex]))
377 model->type == mod_alias
380 (((unsigned char)PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
382 ((gamemode == GAME_TENEBRAE) && ((unsigned int)touch->fields.server->effects & (16 | 32)))
388 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);
390 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
391 Matrix4x4_Invert_Simple(&imatrix, &matrix);
392 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
393 Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
395 Collision_ClipLineToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
397 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
401 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
402 if(!VectorCompare(start, pEnd))
403 Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd);
413 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
414 #if COLLISIONPARANOID >= 1
415 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
417 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
420 #if COLLISIONPARANOID >= 1
421 trace_t SV_TraceBox_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
423 trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
427 vec3_t hullmins, hullmaxs;
428 int i, bodysupercontents;
432 prvm_edict_t *traceowner, *touch;
434 // bounding box of entire move area
435 vec3_t clipboxmins, clipboxmaxs;
436 // size of the moving object
437 vec3_t clipmins, clipmaxs;
438 // size when clipping against monsters
439 vec3_t clipmins2, clipmaxs2;
440 // start and end origin of move
441 vec3_t clipstart, clipend;
444 // matrices to transform into/out of other entity's space
445 matrix4x4_t matrix, imatrix;
446 // model of other entity
448 // list of entities to test for collisions
450 prvm_edict_t *touchedicts[MAX_EDICTS];
451 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
455 if(!VectorCompare(start, pEnd))
457 // TRICK: make the trace 1 qu longer!
458 VectorSubtract(pEnd, start, end);
459 len = VectorNormalizeLength(end);
460 VectorAdd(pEnd, end, end);
463 VectorCopy(pEnd, end);
466 if (VectorCompare(mins, maxs))
468 vec3_t shiftstart, shiftend;
469 VectorAdd(start, mins, shiftstart);
470 VectorAdd(end, mins, shiftend);
471 if (VectorCompare(start, end))
472 return SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
475 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
476 VectorSubtract(trace.endpos, mins, trace.endpos);
481 VectorCopy(start, clipstart);
482 VectorCopy(end, clipend);
483 VectorCopy(mins, clipmins);
484 VectorCopy(maxs, clipmaxs);
485 VectorCopy(mins, clipmins2);
486 VectorCopy(maxs, clipmaxs2);
487 #if COLLISIONPARANOID >= 3
488 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
492 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
493 cliptrace.bmodelstartsolid = cliptrace.startsolid;
494 if (cliptrace.startsolid || cliptrace.fraction < 1)
495 cliptrace.ent = prog->edicts;
496 if (type == MOVE_WORLDONLY)
499 if (type == MOVE_MISSILE)
501 // LordHavoc: modified this, was = -15, now -= 15
502 for (i = 0;i < 3;i++)
509 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
510 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
511 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
514 VectorCopy(clipmins, hullmins);
515 VectorCopy(clipmaxs, hullmaxs);
518 // create the bounding box of the entire move
519 for (i = 0;i < 3;i++)
521 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
522 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
525 // debug override to test against everything
526 if (sv_debugmove.integer)
528 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
529 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
532 // if the passedict is world, make it NULL (to avoid two checks each time)
533 if (passedict == prog->edicts)
535 // precalculate prog value for passedict for comparisons
536 passedictprog = PRVM_EDICT_TO_PROG(passedict);
537 // figure out whether this is a point trace for comparisons
538 pointtrace = VectorCompare(clipmins, clipmaxs);
539 // precalculate passedict's owner edict pointer for comparisons
540 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
543 // because this uses World_EntitiestoBox, we know all entity boxes overlap
544 // the clip region, so we can skip culling checks in the loop below
545 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
546 if (numtouchedicts > MAX_EDICTS)
548 // this never happens
549 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
550 numtouchedicts = MAX_EDICTS;
552 for (i = 0;i < numtouchedicts;i++)
554 touch = touchedicts[i];
556 if (touch->fields.server->solid < SOLID_BBOX)
558 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
563 // don't clip against self
564 if (passedict == touch)
566 // don't clip owned entities against owner
567 if (traceowner == touch)
569 // don't clip owner against owned entities
570 if (passedictprog == touch->fields.server->owner)
572 // don't clip points against points (they can't collide)
573 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
577 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
579 // might interact, so do an exact clip
581 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
583 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
584 // if the modelindex is 0, it shouldn't be SOLID_BSP!
585 if (modelindex > 0 && modelindex < MAX_MODELS)
586 model = sv.models[(int)touch->fields.server->modelindex];
589 ((modelindex = (int)touch->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[(int)touch->fields.server->modelindex]))
591 model->type == mod_alias
594 (((unsigned char)PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
596 ((gamemode == GAME_TENEBRAE) && ((unsigned int)touch->fields.server->effects & (16 | 32)))
602 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);
604 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
605 Matrix4x4_Invert_Simple(&imatrix, &matrix);
606 if ((int)touch->fields.server->flags & FL_MONSTER)
607 Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
609 Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, 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))
617 Collision_ShortenTrace(&cliptrace, len / (len + 1), 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
652 unsigned int modelindex;
654 // list of entities to test for collisions
656 prvm_edict_t *touchedicts[MAX_EDICTS];
658 // get world supercontents at this point
659 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
660 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
662 // if sv_gameplayfix_swiminbmodels is off we're done
663 if (!sv_gameplayfix_swiminbmodels.integer)
664 return supercontents;
666 // get list of entities at this point
667 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
668 if (numtouchedicts > MAX_EDICTS)
670 // this never happens
671 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
672 numtouchedicts = MAX_EDICTS;
674 for (i = 0;i < numtouchedicts;i++)
676 touch = touchedicts[i];
678 // we only care about SOLID_BSP for pointcontents
679 if (touch->fields.server->solid != SOLID_BSP)
682 // might interact, so do an exact clip
683 modelindex = (unsigned int)touch->fields.server->modelindex;
684 if (modelindex >= MAX_MODELS)
686 model = sv.models[(int)touch->fields.server->modelindex];
687 if (!model || !model->PointSuperContents)
689 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);
690 Matrix4x4_Invert_Simple(&imatrix, &matrix);
691 Matrix4x4_Transform(&imatrix, point, transformed);
692 frame = (int)touch->fields.server->frame;
693 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
696 return supercontents;
700 ===============================================================================
702 Linking entities into the world culling system
704 ===============================================================================
707 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
709 int i, numtouchedicts, old_self, old_other;
710 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
712 // build a list of edicts to touch, because the link loop can be corrupted
713 // by IncreaseEdicts called during touch functions
714 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
715 if (numtouchedicts > MAX_EDICTS)
717 // this never happens
718 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
719 numtouchedicts = MAX_EDICTS;
722 old_self = prog->globals.server->self;
723 old_other = prog->globals.server->other;
724 for (i = 0;i < numtouchedicts;i++)
726 touch = touchedicts[i];
727 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
730 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
731 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
732 prog->globals.server->time = sv.time;
733 prog->globals.server->trace_allsolid = false;
734 prog->globals.server->trace_startsolid = false;
735 prog->globals.server->trace_fraction = 1;
736 prog->globals.server->trace_inwater = false;
737 prog->globals.server->trace_inopen = true;
738 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
739 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
740 prog->globals.server->trace_plane_dist = 0;
741 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
742 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
744 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
746 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
748 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
750 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
753 prog->globals.server->self = old_self;
754 prog->globals.server->other = old_other;
763 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
768 if (ent == prog->edicts)
769 return; // don't add the world
771 if (ent->priv.server->free)
776 if (ent->fields.server->solid == SOLID_BSP)
778 int modelindex = (int)ent->fields.server->modelindex;
779 if (modelindex < 0 || modelindex >= MAX_MODELS)
781 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
784 model = sv.models[modelindex];
787 if (!model->TraceBox && developer.integer >= 1)
788 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
790 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
792 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
793 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
795 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
797 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
798 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
802 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
803 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
808 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
809 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
810 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
815 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
816 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
820 // to make items easier to pick up and allow them to be grabbed off
821 // of shelves, the abs sizes are expanded
823 if ((int)ent->fields.server->flags & FL_ITEM)
834 // because movement is clipped an epsilon away from an actual edge,
835 // we must fully check even when bounding boxes don't quite touch
844 VectorCopy(mins, ent->fields.server->absmin);
845 VectorCopy(maxs, ent->fields.server->absmax);
847 World_LinkEdict(&sv.world, ent, mins, maxs);
849 // if touch_triggers, call touch on all entities overlapping this box
850 if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
851 SV_LinkEdict_TouchAreaGrid(ent);
855 ===============================================================================
859 ===============================================================================
864 SV_TestEntityPosition
866 returns true if the entity is in solid currently
869 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
874 contents = SV_GenericHitSuperContentsMask(ent);
875 VectorAdd(ent->fields.server->origin, offset, org);
876 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
877 if (trace.startsupercontents & contents)
881 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
883 // q1bsp/hlbsp use hulls and if the entity does not exactly match
884 // a hull size it is incorrectly tested, so this code tries to
885 // 'fix' it slightly...
886 // FIXME: this breaks entities larger than the hull size
889 VectorAdd(org, ent->fields.server->mins, m1);
890 VectorAdd(org, ent->fields.server->maxs, m2);
891 VectorSubtract(m2, m1, s);
892 #define EPSILON (1.0f / 32.0f)
893 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
894 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
895 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
896 for (i = 0;i < 8;i++)
898 v[0] = (i & 1) ? m2[0] : m1[0];
899 v[1] = (i & 2) ? m2[1] : m1[1];
900 v[2] = (i & 4) ? m2[2] : m1[2];
901 if (SV_PointSuperContents(v) & contents)
906 // if the trace found a better position for the entity, move it there
907 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
910 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
911 VectorCopy(trace.endpos, ent->fields.server->origin);
913 // verify if the endpos is REALLY outside solid
914 VectorCopy(trace.endpos, org);
915 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
917 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
919 VectorCopy(org, ent->fields.server->origin);
930 void SV_CheckAllEnts (void)
935 // see if any solid entities are inside the final position
936 check = PRVM_NEXT_EDICT(prog->edicts);
937 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
939 if (check->priv.server->free)
941 if (check->fields.server->movetype == MOVETYPE_PUSH
942 || check->fields.server->movetype == MOVETYPE_NONE
943 || check->fields.server->movetype == MOVETYPE_FOLLOW
944 || check->fields.server->movetype == MOVETYPE_NOCLIP)
947 if (SV_TestEntityPosition (check, vec3_origin))
948 Con_Print("entity in invalid position\n");
952 // DRESK - Support for Entity Contents Transition Event
955 SV_CheckContentsTransition
957 returns true if entity had a valid contentstransition function call
960 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
962 int bValidFunctionCall;
963 prvm_eval_t *contentstransition;
965 // Default Valid Function Call to False
966 bValidFunctionCall = false;
968 if(ent->fields.server->watertype != nContents)
969 { // Changed Contents
970 // Acquire Contents Transition Function from QC
971 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
973 if(contentstransition->function)
974 { // Valid Function; Execute
975 // Assign Valid Function
976 bValidFunctionCall = true;
977 // Prepare Parameters (Original Contents, New Contents)
979 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
981 PRVM_G_FLOAT(OFS_PARM1) = nContents;
983 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
984 // Execute VM Function
985 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
989 // Return if Function Call was Valid
990 return bValidFunctionCall;
999 void SV_CheckVelocity (prvm_edict_t *ent)
1007 for (i=0 ; i<3 ; i++)
1009 if (IS_NAN(ent->fields.server->velocity[i]))
1011 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1012 ent->fields.server->velocity[i] = 0;
1014 if (IS_NAN(ent->fields.server->origin[i]))
1016 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1017 ent->fields.server->origin[i] = 0;
1021 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1022 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1023 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1025 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1026 ent->fields.server->velocity[0] *= wishspeed;
1027 ent->fields.server->velocity[1] *= wishspeed;
1028 ent->fields.server->velocity[2] *= wishspeed;
1036 Runs thinking code if time. There is some play in the exact time the think
1037 function will be called, because it is called before any movement is done
1038 in a frame. Not used for pushmove objects, because they must be exact.
1039 Returns false if the entity removed itself.
1042 qboolean SV_RunThink (prvm_edict_t *ent)
1046 // don't let things stay in the past.
1047 // it is possible to start that way by a trigger with a local time.
1048 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1051 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1053 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1054 ent->fields.server->nextthink = 0;
1055 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1056 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1057 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1058 // mods often set nextthink to time to cause a think every frame,
1059 // we don't want to loop in that case, so exit if the new nextthink is
1060 // <= the time the qc was told, also exit if it is past the end of the
1062 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1065 return !ent->priv.server->free;
1072 Two entities have touched, so run their touch functions
1073 returns true if the impact kept the origin of the touching entity intact
1076 extern void VM_SetTraceGlobals(const trace_t *trace);
1077 extern sizebuf_t vm_tempstringsbuf;
1078 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1080 int restorevm_tempstringsbuf_cursize;
1081 int old_self, old_other;
1083 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1086 old_self = prog->globals.server->self;
1087 old_other = prog->globals.server->other;
1088 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1090 VectorCopy(e1->fields.server->origin, org);
1092 VM_SetTraceGlobals(trace);
1094 prog->globals.server->time = sv.time;
1095 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1097 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1098 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1099 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1102 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1104 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1105 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1106 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1107 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1108 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1109 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1110 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1112 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1114 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1116 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1118 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1121 prog->globals.server->self = old_self;
1122 prog->globals.server->other = old_other;
1123 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1125 return VectorCompare(e1->fields.server->origin, org);
1133 Slide off of the impacting object
1134 returns the blocked flags (1 = floor, 2 = step / wall)
1137 #define STOP_EPSILON 0.1
1138 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1143 backoff = -DotProduct (in, normal) * overbounce;
1144 VectorMA(in, backoff, normal, out);
1146 for (i = 0;i < 3;i++)
1147 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1156 The basic solid body movement clip that slides along multiple planes
1157 Returns the clipflags if the velocity was modified (hit something solid)
1161 8 = teleported by touch method
1162 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1165 static float SV_Gravity (prvm_edict_t *ent);
1166 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1167 #define MAX_CLIP_PLANES 5
1168 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
1170 int blocked, bumpcount;
1171 int i, j, numplanes;
1172 float d, time_left, gravity;
1173 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1183 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1185 gravity = SV_Gravity(ent) * 0.5f;
1186 ent->fields.server->velocity[2] -= gravity;
1190 applygravity = false;
1191 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1195 VectorCopy(ent->fields.server->velocity, original_velocity);
1196 VectorCopy(ent->fields.server->velocity, primal_velocity);
1199 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1201 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1204 VectorScale(ent->fields.server->velocity, time_left, push);
1206 VectorAdd(ent->fields.server->origin, push, end);
1208 if(!SV_PushEntity(&trace, ent, push, false, false))
1210 // we got teleported by a touch function
1211 // let's abort the move
1217 //if (trace.fraction < 0.002)
1222 VectorCopy(ent->fields.server->origin, start);
1223 start[2] += 3;//0.03125;
1224 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1225 end[2] += 3;//0.03125;
1226 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1227 if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
1229 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
1235 for (i = 0;i < numplanes;i++)
1237 VectorCopy(ent->fields.server->origin, start);
1238 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1239 VectorMA(start, 3, planes[i], start);
1240 VectorMA(end, 3, planes[i], end);
1241 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1242 if (trace.fraction < testtrace.fraction)
1245 VectorCopy(start, ent->fields.server->origin);
1250 // VectorAdd(ent->fields.server->origin, planes[j], start);
1256 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
1257 if (trace.fraction < 1)
1258 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
1263 if (trace.bmodelstartsolid)
1265 // LordHavoc: note: this code is what makes entities stick in place
1266 // if embedded in world only (you can walk through other objects if
1268 // entity is trapped in another solid
1269 VectorClear(ent->fields.server->velocity);
1274 if (trace.fraction == 1)
1276 if (trace.plane.normal[2])
1278 if (trace.plane.normal[2] > 0.7)
1285 Con_Printf ("SV_FlyMove: !trace.ent");
1286 trace.ent = prog->edicts;
1289 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1290 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1297 // save the trace for player extrafriction
1299 VectorCopy(trace.plane.normal, stepnormal);
1301 if (trace.fraction >= 0.001)
1303 // actually covered some distance
1304 VectorCopy(ent->fields.server->velocity, original_velocity);
1308 time_left *= 1 - trace.fraction;
1310 // clipped to another plane
1311 if (numplanes >= MAX_CLIP_PLANES)
1313 // this shouldn't really happen
1314 VectorClear(ent->fields.server->velocity);
1320 for (i = 0;i < numplanes;i++)
1321 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1325 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1330 VectorCopy(trace.plane.normal, planes[numplanes]);
1333 if (sv_newflymove.integer)
1334 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1337 // modify original_velocity so it parallels all of the clip planes
1338 for (i = 0;i < numplanes;i++)
1340 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1341 for (j = 0;j < numplanes;j++)
1346 if (DotProduct(new_velocity, planes[j]) < 0)
1356 // go along this plane
1357 VectorCopy(new_velocity, ent->fields.server->velocity);
1361 // go along the crease
1364 VectorClear(ent->fields.server->velocity);
1368 CrossProduct(planes[0], planes[1], dir);
1369 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1370 VectorNormalize(dir);
1371 d = DotProduct(dir, ent->fields.server->velocity);
1372 VectorScale(dir, d, ent->fields.server->velocity);
1376 // if current velocity is against the original velocity,
1377 // stop dead to avoid tiny occilations in sloping corners
1378 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1380 VectorClear(ent->fields.server->velocity);
1385 //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]);
1388 if ((blocked & 1) == 0 && bumpcount > 1)
1390 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1391 // flag ONGROUND if there's ground under it
1392 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1396 // LordHavoc: this came from QW and allows you to get out of water more easily
1397 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1398 VectorCopy(primal_velocity, ent->fields.server->velocity);
1399 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1400 ent->fields.server->velocity[2] -= gravity;
1410 static float SV_Gravity (prvm_edict_t *ent)
1415 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1416 if (val!=0 && val->_float)
1417 ent_gravity = val->_float;
1420 return ent_gravity * sv_gravity.value * sv.frametime;
1425 ===============================================================================
1429 ===============================================================================
1436 Does not change the entities velocity at all
1437 The trace struct is filled with the trace that has been done.
1438 Returns true if the push did not result in the entity being teleported by QC code.
1441 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1447 VectorAdd (ent->fields.server->origin, push, end);
1449 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1450 type = MOVE_MISSILE;
1451 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1452 type = MOVE_NOMONSTERS; // only clip against bmodels
1456 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1457 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1460 VectorCopy (trace->endpos, ent->fields.server->origin);
1463 if(!trace->startsolid)
1464 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)
1466 Con_Printf("something eeeeevil happened\n");
1470 impact = (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)));
1474 SV_LinkEdict (ent, dolink);
1475 return SV_Impact (ent, trace);
1478 SV_LinkEdict (ent, true);
1490 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1493 int pusherowner, pusherprog;
1496 float savesolid, movetime2, pushltime;
1497 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1499 int numcheckentities;
1500 static prvm_edict_t *checkentities[MAX_EDICTS];
1501 dp_model_t *pushermodel;
1502 trace_t trace, trace2;
1503 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1504 unsigned short moved_edicts[MAX_EDICTS];
1506 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])
1508 pusher->fields.server->ltime += movetime;
1512 switch ((int) pusher->fields.server->solid)
1514 // LordHavoc: valid pusher types
1517 case SOLID_SLIDEBOX:
1518 case SOLID_CORPSE: // LordHavoc: this would be weird...
1520 // LordHavoc: no collisions
1523 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1524 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1525 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1526 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1527 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1528 pusher->fields.server->ltime += movetime;
1529 SV_LinkEdict (pusher, false);
1532 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1535 index = (int) pusher->fields.server->modelindex;
1536 if (index < 1 || index >= MAX_MODELS)
1538 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1541 pushermodel = sv.models[index];
1542 pusherowner = pusher->fields.server->owner;
1543 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1545 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1547 movetime2 = movetime;
1548 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1549 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1550 if (moveangle[0] || moveangle[2])
1552 for (i = 0;i < 3;i++)
1556 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1557 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1561 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1562 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1566 else if (moveangle[1])
1568 for (i = 0;i < 3;i++)
1572 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1573 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1577 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1578 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1584 for (i = 0;i < 3;i++)
1588 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1589 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1593 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1594 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1599 VectorNegate (moveangle, a);
1600 AngleVectorsFLU (a, forward, left, up);
1602 VectorCopy (pusher->fields.server->origin, pushorig);
1603 VectorCopy (pusher->fields.server->angles, pushang);
1604 pushltime = pusher->fields.server->ltime;
1606 // move the pusher to its final position
1608 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1609 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1610 pusher->fields.server->ltime += movetime;
1611 SV_LinkEdict (pusher, false);
1614 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1615 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1616 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);
1617 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1619 savesolid = pusher->fields.server->solid;
1621 // see if any solid entities are inside the final position
1624 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1625 for (e = 0;e < numcheckentities;e++)
1627 prvm_edict_t *check = checkentities[e];
1628 if (check->fields.server->movetype == MOVETYPE_NONE
1629 || check->fields.server->movetype == MOVETYPE_PUSH
1630 || check->fields.server->movetype == MOVETYPE_FOLLOW
1631 || check->fields.server->movetype == MOVETYPE_NOCLIP
1632 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1635 if (check->fields.server->owner == pusherprog)
1638 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1641 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1643 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1644 check->priv.server->waterposition_forceupdate = true;
1646 checkcontents = SV_GenericHitSuperContentsMask(check);
1648 // if the entity is standing on the pusher, it will definitely be moved
1649 // if the entity is not standing on the pusher, but is in the pusher's
1650 // final position, move it
1651 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1653 Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, 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);
1654 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1655 if (!trace.startsolid)
1657 //Con_Printf("- not in solid\n");
1665 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1666 org2[0] = DotProduct (org, forward);
1667 org2[1] = DotProduct (org, left);
1668 org2[2] = DotProduct (org, up);
1669 VectorSubtract (org2, org, move);
1670 VectorAdd (move, move1, move);
1673 VectorCopy (move1, move);
1675 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1677 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1678 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1679 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1681 // try moving the contacted entity
1682 pusher->fields.server->solid = SOLID_NOT;
1683 if(!SV_PushEntity (&trace, check, move, true, true))
1685 // entity "check" got teleported
1686 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1687 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1688 continue; // pushed enough
1690 // FIXME: turn players specially
1691 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1692 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1693 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1695 // this trace.fraction < 1 check causes items to fall off of pushers
1696 // if they pass under or through a wall
1697 // the groundentity check causes items to fall off of ledges
1698 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1699 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1701 // if it is still inside the pusher, block
1702 Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, 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);
1703 if (trace.startsolid)
1705 // try moving the contacted entity a tiny bit further to account for precision errors
1707 pusher->fields.server->solid = SOLID_NOT;
1708 VectorScale(move, 1.1, move2);
1709 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1710 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1711 if(!SV_PushEntity (&trace2, check, move2, true, true))
1713 // entity "check" got teleported
1716 pusher->fields.server->solid = savesolid;
1717 Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, 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);
1718 if (trace.startsolid)
1720 // try moving the contacted entity a tiny bit less to account for precision errors
1721 pusher->fields.server->solid = SOLID_NOT;
1722 VectorScale(move, 0.9, move2);
1723 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1724 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1725 if(!SV_PushEntity (&trace2, check, move2, true, true))
1727 // entity "check" got teleported
1730 pusher->fields.server->solid = savesolid;
1731 Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, 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);
1732 if (trace.startsolid)
1734 // still inside pusher, so it's really blocked
1737 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1739 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1742 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1743 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1747 VectorCopy (pushorig, pusher->fields.server->origin);
1748 VectorCopy (pushang, pusher->fields.server->angles);
1749 pusher->fields.server->ltime = pushltime;
1750 SV_LinkEdict (pusher, false);
1752 // move back any entities we already moved
1753 for (i = 0;i < num_moved;i++)
1755 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1756 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1757 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1758 SV_LinkEdict (ed, false);
1761 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1762 if (pusher->fields.server->blocked)
1764 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1765 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1766 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1773 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1774 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1775 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1784 void SV_Physics_Pusher (prvm_edict_t *ent)
1786 float thinktime, oldltime, movetime;
1788 oldltime = ent->fields.server->ltime;
1790 thinktime = ent->fields.server->nextthink;
1791 if (thinktime < ent->fields.server->ltime + sv.frametime)
1793 movetime = thinktime - ent->fields.server->ltime;
1798 movetime = sv.frametime;
1801 // advances ent->fields.server->ltime if not blocked
1802 SV_PushMove (ent, movetime);
1804 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1806 ent->fields.server->nextthink = 0;
1807 prog->globals.server->time = sv.time;
1808 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1809 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1810 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1816 ===============================================================================
1820 ===============================================================================
1823 static float unstickoffsets[] =
1825 // poutting -/+z changes first as they are least weird
1840 typedef enum unstickresult_e
1848 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1852 // if not stuck in a bmodel, just return
1853 if (!SV_TestEntityPosition(ent, vec3_origin))
1854 return UNSTICK_GOOD;
1856 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1858 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1860 VectorCopy(unstickoffsets + i, offset);
1861 SV_LinkEdict (ent, true);
1862 return UNSTICK_UNSTUCK;
1866 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1867 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1869 for(i = 2; i <= maxunstick; ++i)
1871 VectorClear(offset);
1873 if (!SV_TestEntityPosition(ent, offset))
1875 SV_LinkEdict (ent, true);
1876 return UNSTICK_UNSTUCK;
1879 if (!SV_TestEntityPosition(ent, offset))
1881 SV_LinkEdict (ent, true);
1882 return UNSTICK_UNSTUCK;
1886 return UNSTICK_STUCK;
1889 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1892 switch(SV_UnstickEntityReturnOffset(ent, offset))
1896 case UNSTICK_UNSTUCK:
1897 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]);
1900 if (developer.integer >= 100)
1901 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1904 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1913 This is a big hack to try and fix the rare case of getting stuck in the world
1917 void SV_CheckStuck (prvm_edict_t *ent)
1921 switch(SV_UnstickEntityReturnOffset(ent, offset))
1924 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1926 case UNSTICK_UNSTUCK:
1927 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]);
1930 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1931 if (!SV_TestEntityPosition(ent, offset))
1933 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1934 SV_LinkEdict (ent, true);
1937 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1940 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1950 qboolean SV_CheckWater (prvm_edict_t *ent)
1953 int nNativeContents;
1956 point[0] = ent->fields.server->origin[0];
1957 point[1] = ent->fields.server->origin[1];
1958 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1960 // DRESK - Support for Entity Contents Transition Event
1961 // NOTE: Some logic needed to be slightly re-ordered
1962 // to not affect performance and allow for the feature.
1964 // Acquire Super Contents Prior to Resets
1965 cont = SV_PointSuperContents(point);
1966 // Acquire Native Contents Here
1967 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1969 // DRESK - Support for Entity Contents Transition Event
1970 if(ent->fields.server->watertype)
1971 // Entity did NOT Spawn; Check
1972 SV_CheckContentsTransition(ent, nNativeContents);
1975 ent->fields.server->waterlevel = 0;
1976 ent->fields.server->watertype = CONTENTS_EMPTY;
1977 cont = SV_PointSuperContents(point);
1978 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1980 ent->fields.server->watertype = nNativeContents;
1981 ent->fields.server->waterlevel = 1;
1982 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1983 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1985 ent->fields.server->waterlevel = 2;
1986 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1987 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1988 ent->fields.server->waterlevel = 3;
1992 return ent->fields.server->waterlevel > 1;
2001 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2004 vec3_t forward, into, side;
2006 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2007 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2009 // cut the tangential velocity
2010 i = DotProduct (stepnormal, ent->fields.server->velocity);
2011 VectorScale (stepnormal, i, into);
2012 VectorSubtract (ent->fields.server->velocity, into, side);
2013 ent->fields.server->velocity[0] = side[0] * (1 + d);
2014 ent->fields.server->velocity[1] = side[1] * (1 + d);
2020 =====================
2023 Player has come to a dead stop, possibly due to the problem with limited
2024 float precision at some angle joins in the BSP hull.
2026 Try fixing by pushing one pixel in each direction.
2028 This is a hack, but in the interest of good gameplay...
2029 ======================
2031 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2036 VectorCopy (ent->fields.server->origin, oldorg);
2039 for (i=0 ; i<8 ; i++)
2041 // try pushing a little in an axial direction
2044 case 0: dir[0] = 2; dir[1] = 0; break;
2045 case 1: dir[0] = 0; dir[1] = 2; break;
2046 case 2: dir[0] = -2; dir[1] = 0; break;
2047 case 3: dir[0] = 0; dir[1] = -2; break;
2048 case 4: dir[0] = 2; dir[1] = 2; break;
2049 case 5: dir[0] = -2; dir[1] = 2; break;
2050 case 6: dir[0] = 2; dir[1] = -2; break;
2051 case 7: dir[0] = -2; dir[1] = -2; break;
2054 SV_PushEntity (&trace, ent, dir, false, true);
2056 // retry the original move
2057 ent->fields.server->velocity[0] = oldvel[0];
2058 ent->fields.server->velocity[1] = oldvel[1];
2059 ent->fields.server->velocity[2] = 0;
2060 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2062 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2063 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2065 Con_DPrint("TryUnstick - success.\n");
2069 // go back to the original pos and try again
2070 VectorCopy (oldorg, ent->fields.server->origin);
2074 VectorClear (ent->fields.server->velocity);
2075 Con_DPrint("TryUnstick - failure.\n");
2081 =====================
2084 Only used by players
2085 ======================
2087 void SV_WalkMove (prvm_edict_t *ent)
2089 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2090 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2091 trace_t downtrace, trace;
2092 qboolean applygravity;
2094 // if frametime is 0 (due to client sending the same timestamp twice),
2096 if (sv.frametime <= 0)
2099 SV_CheckStuck (ent);
2101 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2103 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2105 SV_CheckVelocity(ent);
2107 // do a regular slide move unless it looks like you ran into a step
2108 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2110 VectorCopy (ent->fields.server->origin, start_origin);
2111 VectorCopy (ent->fields.server->velocity, start_velocity);
2113 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2115 // if the move did not hit the ground at any point, we're not on ground
2117 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2119 SV_CheckVelocity(ent);
2120 SV_LinkEdict (ent, true);
2122 if(clip & 8) // teleport
2125 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2128 if (sv_nostep.integer)
2131 VectorCopy(ent->fields.server->origin, originalmove_origin);
2132 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2133 originalmove_clip = clip;
2134 originalmove_flags = (int)ent->fields.server->flags;
2135 originalmove_groundentity = ent->fields.server->groundentity;
2137 // if move didn't block on a step, return
2140 // if move was not trying to move into the step, return
2141 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2144 if (ent->fields.server->movetype != MOVETYPE_FLY)
2146 // return if gibbed by a trigger
2147 if (ent->fields.server->movetype != MOVETYPE_WALK)
2150 // only step up while jumping if that is enabled
2151 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2152 if (!oldonground && ent->fields.server->waterlevel == 0)
2156 // try moving up and forward to go up a step
2157 // back to start pos
2158 VectorCopy (start_origin, ent->fields.server->origin);
2159 VectorCopy (start_velocity, ent->fields.server->velocity);
2162 VectorClear (upmove);
2163 upmove[2] = sv_stepheight.value;
2164 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2166 // we got teleported when upstepping... must abort the move
2171 ent->fields.server->velocity[2] = 0;
2172 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2173 ent->fields.server->velocity[2] += start_velocity[2];
2176 // we got teleported when upstepping... must abort the move
2177 // note that z velocity handling may not be what QC expects here, but we cannot help it
2181 SV_CheckVelocity(ent);
2182 SV_LinkEdict (ent, true);
2184 // check for stuckness, possibly due to the limited precision of floats
2185 // in the clipping hulls
2187 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2188 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2190 //Con_Printf("wall\n");
2191 // stepping up didn't make any progress, revert to original move
2192 VectorCopy(originalmove_origin, ent->fields.server->origin);
2193 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2194 //clip = originalmove_clip;
2195 ent->fields.server->flags = originalmove_flags;
2196 ent->fields.server->groundentity = originalmove_groundentity;
2197 // now try to unstick if needed
2198 //clip = SV_TryUnstick (ent, oldvel);
2202 //Con_Printf("step - ");
2204 // extra friction based on view angle
2205 if (clip & 2 && sv_wallfriction.integer)
2206 SV_WallFriction (ent, stepnormal);
2208 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2209 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))
2213 VectorClear (downmove);
2214 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2215 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2217 // we got teleported when downstepping... must abort the move
2221 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2223 // this has been disabled so that you can't jump when you are stepping
2224 // up while already jumping (also known as the Quake2 double jump bug)
2226 // LordHavoc: disabled this check so you can walk on monsters/players
2227 //if (ent->fields.server->solid == SOLID_BSP)
2229 //Con_Printf("onground\n");
2230 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2231 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2237 //Con_Printf("slope\n");
2238 // if the push down didn't end up on good ground, use the move without
2239 // the step up. This happens near wall / slope combinations, and can
2240 // cause the player to hop up higher on a slope too steep to climb
2241 VectorCopy(originalmove_origin, ent->fields.server->origin);
2242 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2243 //clip = originalmove_clip;
2244 ent->fields.server->flags = originalmove_flags;
2245 ent->fields.server->groundentity = originalmove_groundentity;
2248 SV_CheckVelocity(ent);
2249 SV_LinkEdict (ent, true);
2252 //============================================================================
2258 Entities that are "stuck" to another entity
2261 void SV_Physics_Follow (prvm_edict_t *ent)
2263 vec3_t vf, vr, vu, angles, v;
2267 if (!SV_RunThink (ent))
2270 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2271 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2272 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])
2274 // quick case for no rotation
2275 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2279 angles[0] = -ent->fields.server->punchangle[0];
2280 angles[1] = ent->fields.server->punchangle[1];
2281 angles[2] = ent->fields.server->punchangle[2];
2282 AngleVectors (angles, vf, vr, vu);
2283 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];
2284 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];
2285 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];
2286 angles[0] = -e->fields.server->angles[0];
2287 angles[1] = e->fields.server->angles[1];
2288 angles[2] = e->fields.server->angles[2];
2289 AngleVectors (angles, vf, vr, vu);
2290 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2291 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2292 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2294 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2295 SV_LinkEdict (ent, true);
2299 ==============================================================================
2303 ==============================================================================
2308 SV_CheckWaterTransition
2312 void SV_CheckWaterTransition (prvm_edict_t *ent)
2315 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2316 if (!ent->fields.server->watertype)
2318 // just spawned here
2319 ent->fields.server->watertype = cont;
2320 ent->fields.server->waterlevel = 1;
2324 // DRESK - Support for Entity Contents Transition Event
2325 // NOTE: Call here BEFORE updating the watertype below,
2326 // and suppress watersplash sound if a valid function
2327 // call was made to allow for custom "splash" sounds.
2328 if( !SV_CheckContentsTransition(ent, cont) )
2329 { // Contents Transition Function Invalid; Potentially Play Water Sound
2330 // check if the entity crossed into or out of water
2331 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2332 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2335 if (cont <= CONTENTS_WATER)
2337 ent->fields.server->watertype = cont;
2338 ent->fields.server->waterlevel = 1;
2342 ent->fields.server->watertype = CONTENTS_EMPTY;
2343 ent->fields.server->waterlevel = 0;
2351 Toss, bounce, and fly movement. When onground, do nothing.
2354 void SV_Physics_Toss (prvm_edict_t *ent)
2361 // if onground, return without moving
2362 if ((int)ent->fields.server->flags & FL_ONGROUND)
2364 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2366 // don't stick to ground if onground and moving upward
2367 ent->fields.server->flags -= FL_ONGROUND;
2369 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2371 // we can trust FL_ONGROUND if groundentity is world because it never moves
2374 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2376 // if ent was supported by a brush model on previous frame,
2377 // and groundentity is now freed, set groundentity to 0 (world)
2378 // which leaves it suspended in the air
2379 ent->fields.server->groundentity = 0;
2380 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2384 ent->priv.server->suspendedinairflag = false;
2386 SV_CheckVelocity (ent);
2389 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2390 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2393 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2395 movetime = sv.frametime;
2396 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2399 VectorScale (ent->fields.server->velocity, movetime, move);
2400 if(!SV_PushEntity (&trace, ent, move, true, true))
2401 return; // teleported
2402 if (ent->priv.server->free)
2404 if (trace.bmodelstartsolid)
2406 // try to unstick the entity
2407 SV_UnstickEntity(ent);
2408 if(!SV_PushEntity (&trace, ent, move, false, true))
2409 return; // teleported
2410 if (ent->priv.server->free)
2413 if (trace.fraction == 1)
2415 movetime *= 1 - min(1, trace.fraction);
2416 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2418 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
2419 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2421 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2423 float d, ent_gravity;
2425 float bouncefactor = 0.5f;
2426 float bouncestop = 60.0f / 800.0f;
2428 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2429 if (val!=0 && val->_float)
2430 bouncefactor = val->_float;
2432 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2433 if (val!=0 && val->_float)
2434 bouncestop = val->_float;
2436 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2437 // LordHavoc: fixed grenades not bouncing when fired down a slope
2438 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2439 if (val!=0 && val->_float)
2440 ent_gravity = val->_float;
2443 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2445 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2446 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2448 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2449 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2450 VectorClear (ent->fields.server->velocity);
2451 VectorClear (ent->fields.server->avelocity);
2454 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2458 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2460 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2461 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2462 VectorClear (ent->fields.server->velocity);
2463 VectorClear (ent->fields.server->avelocity);
2466 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2471 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2472 if (trace.plane.normal[2] > 0.7)
2474 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2475 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2476 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2477 ent->priv.server->suspendedinairflag = true;
2478 VectorClear (ent->fields.server->velocity);
2479 VectorClear (ent->fields.server->avelocity);
2482 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2484 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2488 // check for in water
2489 SV_CheckWaterTransition (ent);
2493 ===============================================================================
2497 ===============================================================================
2504 Monsters freefall when they don't have a ground entity, otherwise
2505 all movement is done with discrete steps.
2507 This is also used for objects that have become still on the ground, but
2508 will fall if the floor is pulled out from under them.
2511 void SV_Physics_Step (prvm_edict_t *ent)
2513 int flags = (int)ent->fields.server->flags;
2516 // Backup Velocity in the event that movetypesteplandevent is called,
2517 // to provide a parameter with the entity's velocity at impact.
2518 prvm_eval_t *movetypesteplandevent;
2519 vec3_t backupVelocity;
2520 VectorCopy(ent->fields.server->velocity, backupVelocity);
2521 // don't fall at all if fly/swim
2522 if (!(flags & (FL_FLY | FL_SWIM)))
2524 if (flags & FL_ONGROUND)
2526 // freefall if onground and moving upward
2527 // freefall if not standing on a world surface (it may be a lift or trap door)
2528 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2530 ent->fields.server->flags -= FL_ONGROUND;
2531 SV_CheckVelocity(ent);
2532 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2533 SV_LinkEdict(ent, true);
2534 ent->priv.server->waterposition_forceupdate = true;
2539 // freefall if not onground
2540 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2542 SV_CheckVelocity(ent);
2543 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2544 SV_LinkEdict(ent, true);
2547 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2549 // DRESK - Check for Entity Land Event Function
2550 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2552 if(movetypesteplandevent->function)
2553 { // Valid Function; Execute
2554 // Prepare Parameters
2555 // Assign Velocity at Impact
2556 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2557 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2558 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2560 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2561 // Execute VM Function
2562 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2565 // Check for Engine Landing Sound
2566 if(sv_sound_land.string)
2567 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2569 ent->priv.server->waterposition_forceupdate = true;
2574 if (!SV_RunThink(ent))
2577 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2579 ent->priv.server->waterposition_forceupdate = false;
2580 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2581 SV_CheckWaterTransition(ent);
2585 //============================================================================
2587 static void SV_Physics_Entity (prvm_edict_t *ent)
2589 // don't run think/move on newly spawned projectiles as it messes up
2590 // movement interpolation and rocket trails, and is inconsistent with
2591 // respect to entities spawned in the same frame
2592 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2593 // but if it spawns a lower numbered ent, it doesn't - this never moves
2594 // ents in the first frame regardless)
2595 qboolean runmove = ent->priv.server->move;
2596 ent->priv.server->move = true;
2597 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2599 switch ((int) ent->fields.server->movetype)
2602 case MOVETYPE_FAKEPUSH:
2603 SV_Physics_Pusher (ent);
2606 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2607 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2610 case MOVETYPE_FOLLOW:
2611 SV_Physics_Follow (ent);
2613 case MOVETYPE_NOCLIP:
2614 if (SV_RunThink(ent))
2617 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2618 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2620 SV_LinkEdict(ent, false);
2623 SV_Physics_Step (ent);
2626 if (SV_RunThink (ent))
2630 case MOVETYPE_BOUNCE:
2631 case MOVETYPE_BOUNCEMISSILE:
2632 case MOVETYPE_FLYMISSILE:
2635 if (SV_RunThink (ent))
2636 SV_Physics_Toss (ent);
2639 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2644 void SV_Physics_ClientMove(void)
2647 ent = host_client->edict;
2649 // call player physics, this needs the proper frametime
2650 prog->globals.server->frametime = sv.frametime;
2653 // call standard client pre-think, with frametime = 0
2654 prog->globals.server->time = sv.time;
2655 prog->globals.server->frametime = 0;
2656 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2657 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2658 prog->globals.server->frametime = sv.frametime;
2660 // make sure the velocity is sane (not a NaN)
2661 SV_CheckVelocity(ent);
2662 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2663 // player_run/player_stand1 does not horribly malfunction if the
2664 // velocity becomes a number that is both == 0 and != 0
2665 // (sounds to me like NaN but to be absolutely safe...)
2666 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2667 VectorClear(ent->fields.server->velocity);
2669 // perform MOVETYPE_WALK behavior
2672 // call standard player post-think, with frametime = 0
2673 prog->globals.server->time = sv.time;
2674 prog->globals.server->frametime = 0;
2675 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2676 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2677 prog->globals.server->frametime = sv.frametime;
2679 if(ent->fields.server->fixangle)
2681 // angle fixing was requested by physics code...
2682 // so store the current angles for later use
2683 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2684 host_client->fixangle_angles_set = TRUE;
2686 // and clear fixangle for the next frame
2687 ent->fields.server->fixangle = 0;
2691 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2693 // don't do physics on disconnected clients, FrikBot relies on this
2694 if (!host_client->spawned)
2696 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2700 // don't run physics here if running asynchronously
2701 if (host_client->clmovement_inputtimeout <= 0)
2704 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2707 // make sure the velocity is sane (not a NaN)
2708 SV_CheckVelocity(ent);
2709 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2710 // player_run/player_stand1 does not horribly malfunction if the
2711 // velocity becomes a number that is both == 0 and != 0
2712 // (sounds to me like NaN but to be absolutely safe...)
2713 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2714 VectorClear(ent->fields.server->velocity);
2716 // call standard client pre-think
2717 prog->globals.server->time = sv.time;
2718 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2719 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2720 SV_CheckVelocity (ent);
2722 switch ((int) ent->fields.server->movetype)
2725 case MOVETYPE_FAKEPUSH:
2726 SV_Physics_Pusher (ent);
2729 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2730 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2733 case MOVETYPE_FOLLOW:
2734 SV_Physics_Follow (ent);
2736 case MOVETYPE_NOCLIP:
2739 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2740 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2743 SV_Physics_Step (ent);
2747 // don't run physics here if running asynchronously
2748 if (host_client->clmovement_inputtimeout <= 0)
2752 case MOVETYPE_BOUNCE:
2753 case MOVETYPE_BOUNCEMISSILE:
2754 case MOVETYPE_FLYMISSILE:
2757 SV_Physics_Toss (ent);
2764 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2768 // decrement the countdown variable used to decide when to go back to
2769 // synchronous physics
2770 if (host_client->clmovement_inputtimeout > sv.frametime)
2771 host_client->clmovement_inputtimeout -= sv.frametime;
2773 host_client->clmovement_inputtimeout = 0;
2775 SV_CheckVelocity (ent);
2777 SV_LinkEdict (ent, true);
2779 SV_CheckVelocity (ent);
2781 // call standard player post-think
2782 prog->globals.server->time = sv.time;
2783 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2784 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2786 if(ent->fields.server->fixangle)
2788 // angle fixing was requested by physics code...
2789 // so store the current angles for later use
2790 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2791 host_client->fixangle_angles_set = TRUE;
2793 // and clear fixangle for the next frame
2794 ent->fields.server->fixangle = 0;
2804 void SV_Physics (void)
2809 // let the progs know that a new frame has started
2810 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2811 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2812 prog->globals.server->time = sv.time;
2813 prog->globals.server->frametime = sv.frametime;
2814 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2817 // treat each object in turn
2820 // if force_retouch, relink all the entities
2821 if (prog->globals.server->force_retouch > 0)
2822 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2823 if (!ent->priv.server->free)
2824 SV_LinkEdict (ent, true); // force retouch even for stationary
2826 // run physics on the client entities
2827 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2828 if (!ent->priv.server->free)
2829 SV_Physics_ClientEntity(ent);
2831 // run physics on all the non-client entities
2832 if (!sv_freezenonclients.integer)
2834 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2835 if (!ent->priv.server->free)
2836 SV_Physics_Entity(ent);
2837 // make a second pass to see if any ents spawned this frame and make
2838 // sure they run their move/think
2839 if (sv_gameplayfix_delayprojectiles.integer < 0)
2840 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2841 if (!ent->priv.server->move && !ent->priv.server->free)
2842 SV_Physics_Entity(ent);
2845 if (prog->globals.server->force_retouch > 0)
2846 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2848 // LordHavoc: endframe support
2849 if (prog->funcoffsets.EndFrame)
2851 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2852 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2853 prog->globals.server->time = sv.time;
2854 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2857 // decrement prog->num_edicts if the highest number entities died
2858 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2860 if (!sv_freezenonclients.integer)
2861 sv.time += sv.frametime;