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 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
110 VectorCopy(start, clipstart);
111 VectorClear(clipmins2);
112 VectorClear(clipmaxs2);
113 #if COLLISIONPARANOID >= 3
114 Con_Printf("move(%f %f %f)", clipstart[0], clipstart[1], clipstart[2]);
118 Collision_ClipPointToWorld(&cliptrace, sv.worldmodel, clipstart, hitsupercontentsmask);
119 cliptrace.bmodelstartsolid = cliptrace.startsolid;
120 if (cliptrace.startsolid || cliptrace.fraction < 1)
121 cliptrace.ent = prog->edicts;
122 if (type == MOVE_WORLDONLY)
125 if (type == MOVE_MISSILE)
127 // LordHavoc: modified this, was = -15, now -= 15
128 for (i = 0;i < 3;i++)
135 // create the bounding box of the entire move
136 for (i = 0;i < 3;i++)
138 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
139 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
142 // debug override to test against everything
143 if (sv_debugmove.integer)
145 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
146 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
149 // if the passedict is world, make it NULL (to avoid two checks each time)
150 if (passedict == prog->edicts)
152 // precalculate prog value for passedict for comparisons
153 passedictprog = PRVM_EDICT_TO_PROG(passedict);
154 // precalculate passedict's owner edict pointer for comparisons
155 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
158 // because this uses World_EntitiestoBox, we know all entity boxes overlap
159 // the clip region, so we can skip culling checks in the loop below
160 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
161 if (numtouchedicts > MAX_EDICTS)
163 // this never happens
164 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
165 numtouchedicts = MAX_EDICTS;
167 for (i = 0;i < numtouchedicts;i++)
169 touch = touchedicts[i];
171 if (touch->fields.server->solid < SOLID_BBOX)
173 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
178 // don't clip against self
179 if (passedict == touch)
181 // don't clip owned entities against owner
182 if (traceowner == touch)
184 // don't clip owner against owned entities
185 if (passedictprog == touch->fields.server->owner)
187 // don't clip points against points (they can't collide)
188 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
192 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
194 // might interact, so do an exact clip
196 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
198 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
199 // if the modelindex is 0, it shouldn't be SOLID_BSP!
200 if (modelindex > 0 && modelindex < MAX_MODELS)
201 model = sv.models[(int)touch->fields.server->modelindex];
204 ((modelindex = (int)touch->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[(int)touch->fields.server->modelindex]))
206 model->type == mod_alias
209 (((unsigned char)PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
211 ((gamemode == GAME_TENEBRAE) && ((unsigned int)touch->fields.server->effects & (16 | 32)))
217 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);
219 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
220 Matrix4x4_Invert_Simple(&imatrix, &matrix);
221 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
222 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);
224 Collision_ClipPointToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
226 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
238 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
239 trace_t SV_TraceLine(const vec3_t start, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
241 trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
244 int i, bodysupercontents;
247 prvm_edict_t *traceowner, *touch;
249 // bounding box of entire move area
250 vec3_t clipboxmins, clipboxmaxs;
251 // size when clipping against monsters
252 vec3_t clipmins2, clipmaxs2;
253 // start and end origin of move
254 vec3_t clipstart, clipend;
257 // matrices to transform into/out of other entity's space
258 matrix4x4_t matrix, imatrix;
259 // model of other entity
261 // list of entities to test for collisions
263 prvm_edict_t *touchedicts[MAX_EDICTS];
264 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
268 if(!VectorCompare(start, pEnd))
270 // TRICK: make the trace 1 qu longer!
271 VectorSubtract(pEnd, start, end);
272 len = VectorNormalizeLength(end);
273 VectorAdd(pEnd, end, end);
276 VectorCopy(pEnd, end);
279 //return SV_TraceBox(start, vec3_origin, vec3_origin, end, type, passedict, hitsupercontentsmask);
281 if (VectorCompare(start, end))
282 return SV_TracePoint(start, type, passedict, hitsupercontentsmask);
284 VectorCopy(start, clipstart);
285 VectorCopy(end, clipend);
286 VectorClear(clipmins2);
287 VectorClear(clipmaxs2);
288 #if COLLISIONPARANOID >= 3
289 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
293 Collision_ClipLineToWorld(&cliptrace, sv.worldmodel, clipstart, clipend, hitsupercontentsmask);
294 cliptrace.bmodelstartsolid = cliptrace.startsolid;
295 if (cliptrace.startsolid || cliptrace.fraction < 1)
296 cliptrace.ent = prog->edicts;
297 if (type == MOVE_WORLDONLY)
300 if (type == MOVE_MISSILE)
302 // LordHavoc: modified this, was = -15, now -= 15
303 for (i = 0;i < 3;i++)
310 // create the bounding box of the entire move
311 for (i = 0;i < 3;i++)
313 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + clipmins2[i] - 1;
314 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + clipmaxs2[i] + 1;
317 // debug override to test against everything
318 if (sv_debugmove.integer)
320 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
321 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
324 // if the passedict is world, make it NULL (to avoid two checks each time)
325 if (passedict == prog->edicts)
327 // precalculate prog value for passedict for comparisons
328 passedictprog = PRVM_EDICT_TO_PROG(passedict);
329 // precalculate passedict's owner edict pointer for comparisons
330 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
333 // because this uses World_EntitiestoBox, we know all entity boxes overlap
334 // the clip region, so we can skip culling checks in the loop below
335 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
336 if (numtouchedicts > MAX_EDICTS)
338 // this never happens
339 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
340 numtouchedicts = MAX_EDICTS;
342 for (i = 0;i < numtouchedicts;i++)
344 touch = touchedicts[i];
346 if (touch->fields.server->solid < SOLID_BBOX)
348 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
353 // don't clip against self
354 if (passedict == touch)
356 // don't clip owned entities against owner
357 if (traceowner == touch)
359 // don't clip owner against owned entities
360 if (passedictprog == touch->fields.server->owner)
362 // don't clip points against points (they can't collide)
363 if (VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
367 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
369 // might interact, so do an exact clip
371 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
373 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
374 // if the modelindex is 0, it shouldn't be SOLID_BSP!
375 if (modelindex > 0 && modelindex < MAX_MODELS)
376 model = sv.models[(int)touch->fields.server->modelindex];
379 ((modelindex = (int)touch->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[(int)touch->fields.server->modelindex]))
381 model->type == mod_alias
384 (((unsigned char)PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
386 ((gamemode == GAME_TENEBRAE) && ((unsigned int)touch->fields.server->effects & (16 | 32)))
392 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);
394 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
395 Matrix4x4_Invert_Simple(&imatrix, &matrix);
396 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
397 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);
399 Collision_ClipLineToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
401 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
405 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
406 if(!VectorCompare(start, pEnd))
407 Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd);
417 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
418 #if COLLISIONPARANOID >= 1
419 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 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)
424 #if COLLISIONPARANOID >= 1
425 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 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)
431 vec3_t hullmins, hullmaxs;
432 int i, bodysupercontents;
436 prvm_edict_t *traceowner, *touch;
438 // bounding box of entire move area
439 vec3_t clipboxmins, clipboxmaxs;
440 // size of the moving object
441 vec3_t clipmins, clipmaxs;
442 // size when clipping against monsters
443 vec3_t clipmins2, clipmaxs2;
444 // start and end origin of move
445 vec3_t clipstart, clipend;
448 // matrices to transform into/out of other entity's space
449 matrix4x4_t matrix, imatrix;
450 // model of other entity
452 // list of entities to test for collisions
454 prvm_edict_t *touchedicts[MAX_EDICTS];
455 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
459 if(!VectorCompare(start, pEnd))
461 // TRICK: make the trace 1 qu longer!
462 VectorSubtract(pEnd, start, end);
463 len = VectorNormalizeLength(end);
464 VectorAdd(pEnd, end, end);
467 VectorCopy(pEnd, end);
470 if (VectorCompare(mins, maxs))
472 vec3_t shiftstart, shiftend;
473 VectorAdd(start, mins, shiftstart);
474 VectorAdd(end, mins, shiftend);
475 if (VectorCompare(start, end))
476 trace = SV_TracePoint(shiftstart, type, passedict, hitsupercontentsmask);
478 trace = SV_TraceLine(shiftstart, shiftend, type, passedict, hitsupercontentsmask);
479 VectorSubtract(trace.endpos, mins, trace.endpos);
483 VectorCopy(start, clipstart);
484 VectorCopy(end, clipend);
485 VectorCopy(mins, clipmins);
486 VectorCopy(maxs, clipmaxs);
487 VectorCopy(mins, clipmins2);
488 VectorCopy(maxs, clipmaxs2);
489 #if COLLISIONPARANOID >= 3
490 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
494 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
495 cliptrace.bmodelstartsolid = cliptrace.startsolid;
496 if (cliptrace.startsolid || cliptrace.fraction < 1)
497 cliptrace.ent = prog->edicts;
498 if (type == MOVE_WORLDONLY)
501 if (type == MOVE_MISSILE)
503 // LordHavoc: modified this, was = -15, now -= 15
504 for (i = 0;i < 3;i++)
511 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
512 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
513 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
516 VectorCopy(clipmins, hullmins);
517 VectorCopy(clipmaxs, hullmaxs);
520 // create the bounding box of the entire move
521 for (i = 0;i < 3;i++)
523 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
524 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
527 // debug override to test against everything
528 if (sv_debugmove.integer)
530 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
531 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
534 // if the passedict is world, make it NULL (to avoid two checks each time)
535 if (passedict == prog->edicts)
537 // precalculate prog value for passedict for comparisons
538 passedictprog = PRVM_EDICT_TO_PROG(passedict);
539 // figure out whether this is a point trace for comparisons
540 pointtrace = VectorCompare(clipmins, clipmaxs);
541 // precalculate passedict's owner edict pointer for comparisons
542 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
545 // because this uses World_EntitiestoBox, we know all entity boxes overlap
546 // the clip region, so we can skip culling checks in the loop below
547 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
548 if (numtouchedicts > MAX_EDICTS)
550 // this never happens
551 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
552 numtouchedicts = MAX_EDICTS;
554 for (i = 0;i < numtouchedicts;i++)
556 touch = touchedicts[i];
558 if (touch->fields.server->solid < SOLID_BBOX)
560 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
565 // don't clip against self
566 if (passedict == touch)
568 // don't clip owned entities against owner
569 if (traceowner == touch)
571 // don't clip owner against owned entities
572 if (passedictprog == touch->fields.server->owner)
574 // don't clip points against points (they can't collide)
575 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
579 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
581 // might interact, so do an exact clip
583 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
585 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
586 // if the modelindex is 0, it shouldn't be SOLID_BSP!
587 if (modelindex > 0 && modelindex < MAX_MODELS)
588 model = sv.models[(int)touch->fields.server->modelindex];
591 ((modelindex = (int)touch->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[(int)touch->fields.server->modelindex]))
593 model->type == mod_alias
596 (((unsigned char)PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
598 ((gamemode == GAME_TENEBRAE) && ((unsigned int)touch->fields.server->effects & (16 | 32)))
604 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);
606 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
607 Matrix4x4_Invert_Simple(&imatrix, &matrix);
608 if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
609 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);
611 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);
613 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
617 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
618 if(!VectorCompare(start, pEnd))
619 Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd);
624 #if COLLISIONPARANOID >= 1
625 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)
630 trace = SV_TraceBox_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
633 VectorCopy(trace.endpos, temp);
634 endstuck = SV_TraceBox_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
635 #if COLLISIONPARANOID < 3
636 if (trace.startsolid || endstuck)
638 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" : "");
644 int SV_PointSuperContents(const vec3_t point)
646 int supercontents = 0;
650 // matrices to transform into/out of other entity's space
651 matrix4x4_t matrix, imatrix;
652 // model of other entity
654 unsigned int modelindex;
656 // list of entities to test for collisions
658 prvm_edict_t *touchedicts[MAX_EDICTS];
660 // get world supercontents at this point
661 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
662 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
664 // if sv_gameplayfix_swiminbmodels is off we're done
665 if (!sv_gameplayfix_swiminbmodels.integer)
666 return supercontents;
668 // get list of entities at this point
669 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
670 if (numtouchedicts > MAX_EDICTS)
672 // this never happens
673 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
674 numtouchedicts = MAX_EDICTS;
676 for (i = 0;i < numtouchedicts;i++)
678 touch = touchedicts[i];
680 // we only care about SOLID_BSP for pointcontents
681 if (touch->fields.server->solid != SOLID_BSP)
684 // might interact, so do an exact clip
685 modelindex = (unsigned int)touch->fields.server->modelindex;
686 if (modelindex >= MAX_MODELS)
688 model = sv.models[(int)touch->fields.server->modelindex];
689 if (!model || !model->PointSuperContents)
691 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);
692 Matrix4x4_Invert_Simple(&imatrix, &matrix);
693 Matrix4x4_Transform(&imatrix, point, transformed);
694 frame = (int)touch->fields.server->frame;
695 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
698 return supercontents;
702 ===============================================================================
704 Linking entities into the world culling system
706 ===============================================================================
709 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
711 int i, numtouchedicts, old_self, old_other;
712 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
714 if (ent == prog->edicts)
715 return; // don't add the world
717 if (ent->priv.server->free)
720 if (ent->fields.server->solid == SOLID_NOT)
723 // build a list of edicts to touch, because the link loop can be corrupted
724 // by IncreaseEdicts called during touch functions
725 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
726 if (numtouchedicts > MAX_EDICTS)
728 // this never happens
729 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
730 numtouchedicts = MAX_EDICTS;
733 old_self = prog->globals.server->self;
734 old_other = prog->globals.server->other;
735 for (i = 0;i < numtouchedicts;i++)
737 touch = touchedicts[i];
738 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
741 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
742 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
743 prog->globals.server->time = sv.time;
744 prog->globals.server->trace_allsolid = false;
745 prog->globals.server->trace_startsolid = false;
746 prog->globals.server->trace_fraction = 1;
747 prog->globals.server->trace_inwater = false;
748 prog->globals.server->trace_inopen = true;
749 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
750 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
751 prog->globals.server->trace_plane_dist = 0;
752 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
753 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
755 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
757 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
759 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
761 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
764 prog->globals.server->self = old_self;
765 prog->globals.server->other = old_other;
774 void SV_LinkEdict (prvm_edict_t *ent)
779 if (ent == prog->edicts)
780 return; // don't add the world
782 if (ent->priv.server->free)
787 if (ent->fields.server->solid == SOLID_BSP)
789 int modelindex = (int)ent->fields.server->modelindex;
790 if (modelindex < 0 || modelindex >= MAX_MODELS)
792 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
795 model = sv.models[modelindex];
798 if (!model->TraceBox && developer.integer >= 1)
799 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
801 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
803 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
804 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
806 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
808 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
809 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
813 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
814 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
819 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
820 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
821 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
826 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
827 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
831 // to make items easier to pick up and allow them to be grabbed off
832 // of shelves, the abs sizes are expanded
834 if ((int)ent->fields.server->flags & FL_ITEM)
845 // because movement is clipped an epsilon away from an actual edge,
846 // we must fully check even when bounding boxes don't quite touch
855 VectorCopy(mins, ent->fields.server->absmin);
856 VectorCopy(maxs, ent->fields.server->absmax);
858 World_LinkEdict(&sv.world, ent, mins, maxs);
862 ===============================================================================
866 ===============================================================================
871 SV_TestEntityPosition
873 returns true if the entity is in solid currently
876 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
881 contents = SV_GenericHitSuperContentsMask(ent);
882 VectorAdd(ent->fields.server->origin, offset, org);
883 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
884 if (trace.startsupercontents & contents)
888 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
890 // q1bsp/hlbsp use hulls and if the entity does not exactly match
891 // a hull size it is incorrectly tested, so this code tries to
892 // 'fix' it slightly...
893 // FIXME: this breaks entities larger than the hull size
896 VectorAdd(org, ent->fields.server->mins, m1);
897 VectorAdd(org, ent->fields.server->maxs, m2);
898 VectorSubtract(m2, m1, s);
899 #define EPSILON (1.0f / 32.0f)
900 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
901 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
902 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
903 for (i = 0;i < 8;i++)
905 v[0] = (i & 1) ? m2[0] : m1[0];
906 v[1] = (i & 2) ? m2[1] : m1[1];
907 v[2] = (i & 4) ? m2[2] : m1[2];
908 if (SV_PointSuperContents(v) & contents)
913 // if the trace found a better position for the entity, move it there
914 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
917 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
918 VectorCopy(trace.endpos, ent->fields.server->origin);
920 // verify if the endpos is REALLY outside solid
921 VectorCopy(trace.endpos, org);
922 trace = SV_TraceBox(org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
924 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
926 VectorCopy(org, ent->fields.server->origin);
937 void SV_CheckAllEnts (void)
942 // see if any solid entities are inside the final position
943 check = PRVM_NEXT_EDICT(prog->edicts);
944 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
946 if (check->priv.server->free)
948 if (check->fields.server->movetype == MOVETYPE_PUSH
949 || check->fields.server->movetype == MOVETYPE_NONE
950 || check->fields.server->movetype == MOVETYPE_FOLLOW
951 || check->fields.server->movetype == MOVETYPE_NOCLIP)
954 if (SV_TestEntityPosition (check, vec3_origin))
955 Con_Print("entity in invalid position\n");
959 // DRESK - Support for Entity Contents Transition Event
962 SV_CheckContentsTransition
964 returns true if entity had a valid contentstransition function call
967 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
969 int bValidFunctionCall;
970 prvm_eval_t *contentstransition;
972 // Default Valid Function Call to False
973 bValidFunctionCall = false;
975 if(ent->fields.server->watertype != nContents)
976 { // Changed Contents
977 // Acquire Contents Transition Function from QC
978 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
980 if(contentstransition->function)
981 { // Valid Function; Execute
982 // Assign Valid Function
983 bValidFunctionCall = true;
984 // Prepare Parameters (Original Contents, New Contents)
986 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
988 PRVM_G_FLOAT(OFS_PARM1) = nContents;
990 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
991 // Execute VM Function
992 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
996 // Return if Function Call was Valid
997 return bValidFunctionCall;
1006 void SV_CheckVelocity (prvm_edict_t *ent)
1014 for (i=0 ; i<3 ; i++)
1016 if (IS_NAN(ent->fields.server->velocity[i]))
1018 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1019 ent->fields.server->velocity[i] = 0;
1021 if (IS_NAN(ent->fields.server->origin[i]))
1023 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
1024 ent->fields.server->origin[i] = 0;
1028 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1029 // player_run/player_stand1 does not horribly malfunction if the
1030 // velocity becomes a denormalized float
1031 if (VectorLength2(ent->fields.server->velocity) < 0.0001)
1032 VectorClear(ent->fields.server->velocity);
1034 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
1035 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1036 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1038 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1039 ent->fields.server->velocity[0] *= wishspeed;
1040 ent->fields.server->velocity[1] *= wishspeed;
1041 ent->fields.server->velocity[2] *= wishspeed;
1049 Runs thinking code if time. There is some play in the exact time the think
1050 function will be called, because it is called before any movement is done
1051 in a frame. Not used for pushmove objects, because they must be exact.
1052 Returns false if the entity removed itself.
1055 qboolean SV_RunThink (prvm_edict_t *ent)
1059 // don't let things stay in the past.
1060 // it is possible to start that way by a trigger with a local time.
1061 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1064 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1066 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1067 ent->fields.server->nextthink = 0;
1068 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1069 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1070 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1071 // mods often set nextthink to time to cause a think every frame,
1072 // we don't want to loop in that case, so exit if the new nextthink is
1073 // <= the time the qc was told, also exit if it is past the end of the
1075 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1078 return !ent->priv.server->free;
1085 Two entities have touched, so run their touch functions
1086 returns true if the impact kept the origin of the touching entity intact
1089 extern void VM_SetTraceGlobals(const trace_t *trace);
1090 extern sizebuf_t vm_tempstringsbuf;
1091 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1093 int restorevm_tempstringsbuf_cursize;
1094 int old_self, old_other;
1096 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1099 old_self = prog->globals.server->self;
1100 old_other = prog->globals.server->other;
1101 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1103 VectorCopy(e1->fields.server->origin, org);
1105 VM_SetTraceGlobals(trace);
1107 prog->globals.server->time = sv.time;
1108 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1110 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1111 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1112 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1115 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1117 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1118 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1119 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1120 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1121 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1122 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1123 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1125 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1127 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1129 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1131 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1134 prog->globals.server->self = old_self;
1135 prog->globals.server->other = old_other;
1136 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1138 return VectorCompare(e1->fields.server->origin, org);
1146 Slide off of the impacting object
1147 returns the blocked flags (1 = floor, 2 = step / wall)
1150 #define STOP_EPSILON 0.1
1151 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1156 backoff = -DotProduct (in, normal) * overbounce;
1157 VectorMA(in, backoff, normal, out);
1159 for (i = 0;i < 3;i++)
1160 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1169 The basic solid body movement clip that slides along multiple planes
1170 Returns the clipflags if the velocity was modified (hit something solid)
1174 8 = teleported by touch method
1175 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1178 static float SV_Gravity (prvm_edict_t *ent);
1179 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1180 #define MAX_CLIP_PLANES 5
1181 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
1183 int blocked, bumpcount;
1184 int i, j, numplanes;
1185 float d, time_left, gravity;
1186 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1196 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1198 gravity = SV_Gravity(ent) * 0.5f;
1199 ent->fields.server->velocity[2] -= gravity;
1203 applygravity = false;
1204 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1208 VectorCopy(ent->fields.server->velocity, original_velocity);
1209 VectorCopy(ent->fields.server->velocity, primal_velocity);
1212 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1214 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1217 VectorScale(ent->fields.server->velocity, time_left, push);
1219 VectorAdd(ent->fields.server->origin, push, end);
1221 if(!SV_PushEntity(&trace, ent, push, false, false))
1223 // we got teleported by a touch function
1224 // let's abort the move
1230 //if (trace.fraction < 0.002)
1235 VectorCopy(ent->fields.server->origin, start);
1236 start[2] += 3;//0.03125;
1237 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1238 end[2] += 3;//0.03125;
1239 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1240 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)))
1242 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
1248 for (i = 0;i < numplanes;i++)
1250 VectorCopy(ent->fields.server->origin, start);
1251 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1252 VectorMA(start, 3, planes[i], start);
1253 VectorMA(end, 3, planes[i], end);
1254 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1255 if (trace.fraction < testtrace.fraction)
1258 VectorCopy(start, ent->fields.server->origin);
1263 // VectorAdd(ent->fields.server->origin, planes[j], start);
1269 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);
1270 if (trace.fraction < 1)
1271 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
1276 if (trace.bmodelstartsolid)
1278 // LordHavoc: note: this code is what makes entities stick in place
1279 // if embedded in world only (you can walk through other objects if
1281 // entity is trapped in another solid
1282 VectorClear(ent->fields.server->velocity);
1287 if (trace.fraction == 1)
1289 if (trace.plane.normal[2])
1291 if (trace.plane.normal[2] > 0.7)
1298 Con_Printf ("SV_FlyMove: !trace.ent");
1299 trace.ent = prog->edicts;
1302 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1303 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1310 // save the trace for player extrafriction
1312 VectorCopy(trace.plane.normal, stepnormal);
1314 if (trace.fraction >= 0.001)
1316 // actually covered some distance
1317 VectorCopy(ent->fields.server->velocity, original_velocity);
1321 time_left *= 1 - trace.fraction;
1323 // clipped to another plane
1324 if (numplanes >= MAX_CLIP_PLANES)
1326 // this shouldn't really happen
1327 VectorClear(ent->fields.server->velocity);
1333 for (i = 0;i < numplanes;i++)
1334 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1338 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1343 VectorCopy(trace.plane.normal, planes[numplanes]);
1346 if (sv_newflymove.integer)
1347 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1350 // modify original_velocity so it parallels all of the clip planes
1351 for (i = 0;i < numplanes;i++)
1353 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1354 for (j = 0;j < numplanes;j++)
1359 if (DotProduct(new_velocity, planes[j]) < 0)
1369 // go along this plane
1370 VectorCopy(new_velocity, ent->fields.server->velocity);
1374 // go along the crease
1377 VectorClear(ent->fields.server->velocity);
1381 CrossProduct(planes[0], planes[1], dir);
1382 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1383 VectorNormalize(dir);
1384 d = DotProduct(dir, ent->fields.server->velocity);
1385 VectorScale(dir, d, ent->fields.server->velocity);
1389 // if current velocity is against the original velocity,
1390 // stop dead to avoid tiny occilations in sloping corners
1391 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1393 VectorClear(ent->fields.server->velocity);
1398 //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]);
1401 if ((blocked & 1) == 0 && bumpcount > 1)
1403 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1404 // flag ONGROUND if there's ground under it
1405 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1409 // LordHavoc: this came from QW and allows you to get out of water more easily
1410 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1411 VectorCopy(primal_velocity, ent->fields.server->velocity);
1412 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1413 ent->fields.server->velocity[2] -= gravity;
1423 static float SV_Gravity (prvm_edict_t *ent)
1428 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1429 if (val!=0 && val->_float)
1430 ent_gravity = val->_float;
1433 return ent_gravity * sv_gravity.value * sv.frametime;
1438 ===============================================================================
1442 ===============================================================================
1449 Does not change the entities velocity at all
1450 The trace struct is filled with the trace that has been done.
1451 Returns true if the push did not result in the entity being teleported by QC code.
1454 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1459 VectorAdd (ent->fields.server->origin, push, end);
1461 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1462 type = MOVE_MISSILE;
1463 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1464 type = MOVE_NOMONSTERS; // only clip against bmodels
1468 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1469 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1472 VectorCopy (trace->endpos, ent->fields.server->origin);
1476 if(!trace->startsolid)
1477 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)
1479 Con_Printf("something eeeeevil happened\n");
1484 SV_LinkEdict_TouchAreaGrid(ent);
1486 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))))
1487 return SV_Impact (ent, trace);
1499 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1502 int pusherowner, pusherprog;
1505 float savesolid, movetime2, pushltime;
1506 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1508 int numcheckentities;
1509 static prvm_edict_t *checkentities[MAX_EDICTS];
1510 dp_model_t *pushermodel;
1511 trace_t trace, trace2;
1512 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1513 unsigned short moved_edicts[MAX_EDICTS];
1515 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])
1517 pusher->fields.server->ltime += movetime;
1521 switch ((int) pusher->fields.server->solid)
1523 // LordHavoc: valid pusher types
1526 case SOLID_SLIDEBOX:
1527 case SOLID_CORPSE: // LordHavoc: this would be weird...
1529 // LordHavoc: no collisions
1532 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1533 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1534 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1535 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1536 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1537 pusher->fields.server->ltime += movetime;
1538 SV_LinkEdict(pusher);
1541 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1544 index = (int) pusher->fields.server->modelindex;
1545 if (index < 1 || index >= MAX_MODELS)
1547 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1550 pushermodel = sv.models[index];
1551 pusherowner = pusher->fields.server->owner;
1552 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1554 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1556 movetime2 = movetime;
1557 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1558 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1559 if (moveangle[0] || moveangle[2])
1561 for (i = 0;i < 3;i++)
1565 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1566 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1570 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1571 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1575 else if (moveangle[1])
1577 for (i = 0;i < 3;i++)
1581 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1582 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1586 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1587 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1593 for (i = 0;i < 3;i++)
1597 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1598 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1602 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1603 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1608 VectorNegate (moveangle, a);
1609 AngleVectorsFLU (a, forward, left, up);
1611 VectorCopy (pusher->fields.server->origin, pushorig);
1612 VectorCopy (pusher->fields.server->angles, pushang);
1613 pushltime = pusher->fields.server->ltime;
1615 // move the pusher to its final position
1617 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1618 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1619 pusher->fields.server->ltime += movetime;
1620 SV_LinkEdict(pusher);
1623 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1624 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1625 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);
1626 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1628 savesolid = pusher->fields.server->solid;
1630 // see if any solid entities are inside the final position
1633 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1634 for (e = 0;e < numcheckentities;e++)
1636 prvm_edict_t *check = checkentities[e];
1637 int movetype = (int)check->fields.server->movetype;
1642 case MOVETYPE_FOLLOW:
1643 case MOVETYPE_NOCLIP:
1644 case MOVETYPE_FAKEPUSH:
1650 if (check->fields.server->owner == pusherprog)
1653 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1656 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1658 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1659 check->priv.server->waterposition_forceupdate = true;
1661 checkcontents = SV_GenericHitSuperContentsMask(check);
1663 // if the entity is standing on the pusher, it will definitely be moved
1664 // if the entity is not standing on the pusher, but is in the pusher's
1665 // final position, move it
1666 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1668 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);
1669 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1670 if (!trace.startsolid)
1672 //Con_Printf("- not in solid\n");
1680 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1681 org2[0] = DotProduct (org, forward);
1682 org2[1] = DotProduct (org, left);
1683 org2[2] = DotProduct (org, up);
1684 VectorSubtract (org2, org, move);
1685 VectorAdd (move, move1, move);
1688 VectorCopy (move1, move);
1690 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1692 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1693 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1694 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1696 // physics objects need better collisions than this code can do
1697 if (movetype == MOVETYPE_PHYSICS)
1699 VectorAdd(check->fields.server->origin, move, check->fields.server->origin);
1700 SV_LinkEdict(check);
1701 SV_LinkEdict_TouchAreaGrid(check);
1705 // try moving the contacted entity
1706 pusher->fields.server->solid = SOLID_NOT;
1707 if(!SV_PushEntity (&trace, check, move, true, true))
1709 // entity "check" got teleported
1710 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1711 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1712 continue; // pushed enough
1714 // FIXME: turn players specially
1715 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1716 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1717 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1719 // this trace.fraction < 1 check causes items to fall off of pushers
1720 // if they pass under or through a wall
1721 // the groundentity check causes items to fall off of ledges
1722 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1723 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1725 // if it is still inside the pusher, block
1726 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);
1727 if (trace.startsolid)
1729 // try moving the contacted entity a tiny bit further to account for precision errors
1731 pusher->fields.server->solid = SOLID_NOT;
1732 VectorScale(move, 1.1, move2);
1733 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1734 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1735 if(!SV_PushEntity (&trace2, check, move2, true, true))
1737 // entity "check" got teleported
1740 pusher->fields.server->solid = savesolid;
1741 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);
1742 if (trace.startsolid)
1744 // try moving the contacted entity a tiny bit less to account for precision errors
1745 pusher->fields.server->solid = SOLID_NOT;
1746 VectorScale(move, 0.9, move2);
1747 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1748 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1749 if(!SV_PushEntity (&trace2, check, move2, true, true))
1751 // entity "check" got teleported
1754 pusher->fields.server->solid = savesolid;
1755 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);
1756 if (trace.startsolid)
1758 // still inside pusher, so it's really blocked
1761 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1763 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1766 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1767 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1771 VectorCopy (pushorig, pusher->fields.server->origin);
1772 VectorCopy (pushang, pusher->fields.server->angles);
1773 pusher->fields.server->ltime = pushltime;
1774 SV_LinkEdict(pusher);
1776 // move back any entities we already moved
1777 for (i = 0;i < num_moved;i++)
1779 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1780 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1781 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1785 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1786 if (pusher->fields.server->blocked)
1788 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1789 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1790 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1797 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1798 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1799 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1808 void SV_Physics_Pusher (prvm_edict_t *ent)
1810 float thinktime, oldltime, movetime;
1812 oldltime = ent->fields.server->ltime;
1814 thinktime = ent->fields.server->nextthink;
1815 if (thinktime < ent->fields.server->ltime + sv.frametime)
1817 movetime = thinktime - ent->fields.server->ltime;
1822 movetime = sv.frametime;
1825 // advances ent->fields.server->ltime if not blocked
1826 SV_PushMove (ent, movetime);
1828 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1830 ent->fields.server->nextthink = 0;
1831 prog->globals.server->time = sv.time;
1832 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1833 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1834 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1840 ===============================================================================
1844 ===============================================================================
1847 static float unstickoffsets[] =
1849 // poutting -/+z changes first as they are least weird
1864 typedef enum unstickresult_e
1872 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1876 // if not stuck in a bmodel, just return
1877 if (!SV_TestEntityPosition(ent, vec3_origin))
1878 return UNSTICK_GOOD;
1880 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1882 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1884 VectorCopy(unstickoffsets + i, offset);
1886 //SV_LinkEdict_TouchAreaGrid(ent);
1887 return UNSTICK_UNSTUCK;
1891 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1892 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1894 for(i = 2; i <= maxunstick; ++i)
1896 VectorClear(offset);
1898 if (!SV_TestEntityPosition(ent, offset))
1901 //SV_LinkEdict_TouchAreaGrid(ent);
1902 return UNSTICK_UNSTUCK;
1905 if (!SV_TestEntityPosition(ent, offset))
1908 //SV_LinkEdict_TouchAreaGrid(ent);
1909 return UNSTICK_UNSTUCK;
1913 return UNSTICK_STUCK;
1916 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1919 switch(SV_UnstickEntityReturnOffset(ent, offset))
1923 case UNSTICK_UNSTUCK:
1924 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]);
1927 if (developer.integer >= 100)
1928 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1931 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1940 This is a big hack to try and fix the rare case of getting stuck in the world
1944 void SV_CheckStuck (prvm_edict_t *ent)
1948 switch(SV_UnstickEntityReturnOffset(ent, offset))
1951 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1953 case UNSTICK_UNSTUCK:
1954 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]);
1957 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1958 if (!SV_TestEntityPosition(ent, offset))
1960 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1962 //SV_LinkEdict_TouchAreaGrid(ent);
1965 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1968 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1978 qboolean SV_CheckWater (prvm_edict_t *ent)
1981 int nNativeContents;
1984 point[0] = ent->fields.server->origin[0];
1985 point[1] = ent->fields.server->origin[1];
1986 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1988 // DRESK - Support for Entity Contents Transition Event
1989 // NOTE: Some logic needed to be slightly re-ordered
1990 // to not affect performance and allow for the feature.
1992 // Acquire Super Contents Prior to Resets
1993 cont = SV_PointSuperContents(point);
1994 // Acquire Native Contents Here
1995 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1997 // DRESK - Support for Entity Contents Transition Event
1998 if(ent->fields.server->watertype)
1999 // Entity did NOT Spawn; Check
2000 SV_CheckContentsTransition(ent, nNativeContents);
2003 ent->fields.server->waterlevel = 0;
2004 ent->fields.server->watertype = CONTENTS_EMPTY;
2005 cont = SV_PointSuperContents(point);
2006 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
2008 ent->fields.server->watertype = nNativeContents;
2009 ent->fields.server->waterlevel = 1;
2010 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
2011 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2013 ent->fields.server->waterlevel = 2;
2014 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2015 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2016 ent->fields.server->waterlevel = 3;
2020 return ent->fields.server->waterlevel > 1;
2029 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2032 vec3_t forward, into, side;
2034 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2035 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2037 // cut the tangential velocity
2038 i = DotProduct (stepnormal, ent->fields.server->velocity);
2039 VectorScale (stepnormal, i, into);
2040 VectorSubtract (ent->fields.server->velocity, into, side);
2041 ent->fields.server->velocity[0] = side[0] * (1 + d);
2042 ent->fields.server->velocity[1] = side[1] * (1 + d);
2048 =====================
2051 Player has come to a dead stop, possibly due to the problem with limited
2052 float precision at some angle joins in the BSP hull.
2054 Try fixing by pushing one pixel in each direction.
2056 This is a hack, but in the interest of good gameplay...
2057 ======================
2059 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2064 VectorCopy (ent->fields.server->origin, oldorg);
2067 for (i=0 ; i<8 ; i++)
2069 // try pushing a little in an axial direction
2072 case 0: dir[0] = 2; dir[1] = 0; break;
2073 case 1: dir[0] = 0; dir[1] = 2; break;
2074 case 2: dir[0] = -2; dir[1] = 0; break;
2075 case 3: dir[0] = 0; dir[1] = -2; break;
2076 case 4: dir[0] = 2; dir[1] = 2; break;
2077 case 5: dir[0] = -2; dir[1] = 2; break;
2078 case 6: dir[0] = 2; dir[1] = -2; break;
2079 case 7: dir[0] = -2; dir[1] = -2; break;
2082 SV_PushEntity (&trace, ent, dir, false, true);
2084 // retry the original move
2085 ent->fields.server->velocity[0] = oldvel[0];
2086 ent->fields.server->velocity[1] = oldvel[1];
2087 ent->fields.server->velocity[2] = 0;
2088 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2090 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2091 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2093 Con_DPrint("TryUnstick - success.\n");
2097 // go back to the original pos and try again
2098 VectorCopy (oldorg, ent->fields.server->origin);
2102 VectorClear (ent->fields.server->velocity);
2103 Con_DPrint("TryUnstick - failure.\n");
2109 =====================
2112 Only used by players
2113 ======================
2115 void SV_WalkMove (prvm_edict_t *ent)
2117 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2118 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2119 trace_t downtrace, trace;
2120 qboolean applygravity;
2122 // if frametime is 0 (due to client sending the same timestamp twice),
2124 if (sv.frametime <= 0)
2127 SV_CheckStuck (ent);
2129 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2131 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2133 SV_CheckVelocity(ent);
2135 // do a regular slide move unless it looks like you ran into a step
2136 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2138 VectorCopy (ent->fields.server->origin, start_origin);
2139 VectorCopy (ent->fields.server->velocity, start_velocity);
2141 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2143 // if the move did not hit the ground at any point, we're not on ground
2145 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2147 SV_CheckVelocity(ent);
2149 SV_LinkEdict_TouchAreaGrid(ent);
2151 if(clip & 8) // teleport
2154 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2157 if (sv_nostep.integer)
2160 VectorCopy(ent->fields.server->origin, originalmove_origin);
2161 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2162 originalmove_clip = clip;
2163 originalmove_flags = (int)ent->fields.server->flags;
2164 originalmove_groundentity = ent->fields.server->groundentity;
2166 // if move didn't block on a step, return
2169 // if move was not trying to move into the step, return
2170 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2173 if (ent->fields.server->movetype != MOVETYPE_FLY)
2175 // return if gibbed by a trigger
2176 if (ent->fields.server->movetype != MOVETYPE_WALK)
2179 // only step up while jumping if that is enabled
2180 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2181 if (!oldonground && ent->fields.server->waterlevel == 0)
2185 // try moving up and forward to go up a step
2186 // back to start pos
2187 VectorCopy (start_origin, ent->fields.server->origin);
2188 VectorCopy (start_velocity, ent->fields.server->velocity);
2191 VectorClear (upmove);
2192 upmove[2] = sv_stepheight.value;
2193 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2195 // we got teleported when upstepping... must abort the move
2200 ent->fields.server->velocity[2] = 0;
2201 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2202 ent->fields.server->velocity[2] += start_velocity[2];
2205 // we got teleported when upstepping... must abort the move
2206 // note that z velocity handling may not be what QC expects here, but we cannot help it
2210 SV_CheckVelocity(ent);
2212 SV_LinkEdict_TouchAreaGrid(ent);
2214 // check for stuckness, possibly due to the limited precision of floats
2215 // in the clipping hulls
2217 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2218 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2220 //Con_Printf("wall\n");
2221 // stepping up didn't make any progress, revert to original move
2222 VectorCopy(originalmove_origin, ent->fields.server->origin);
2223 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2224 //clip = originalmove_clip;
2225 ent->fields.server->flags = originalmove_flags;
2226 ent->fields.server->groundentity = originalmove_groundentity;
2227 // now try to unstick if needed
2228 //clip = SV_TryUnstick (ent, oldvel);
2232 //Con_Printf("step - ");
2234 // extra friction based on view angle
2235 if (clip & 2 && sv_wallfriction.integer)
2236 SV_WallFriction (ent, stepnormal);
2238 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2239 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))
2243 VectorClear (downmove);
2244 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2245 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2247 // we got teleported when downstepping... must abort the move
2251 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2253 // this has been disabled so that you can't jump when you are stepping
2254 // up while already jumping (also known as the Quake2 double jump bug)
2256 // LordHavoc: disabled this check so you can walk on monsters/players
2257 //if (ent->fields.server->solid == SOLID_BSP)
2259 //Con_Printf("onground\n");
2260 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2261 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2267 //Con_Printf("slope\n");
2268 // if the push down didn't end up on good ground, use the move without
2269 // the step up. This happens near wall / slope combinations, and can
2270 // cause the player to hop up higher on a slope too steep to climb
2271 VectorCopy(originalmove_origin, ent->fields.server->origin);
2272 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2273 //clip = originalmove_clip;
2274 ent->fields.server->flags = originalmove_flags;
2275 ent->fields.server->groundentity = originalmove_groundentity;
2278 SV_CheckVelocity(ent);
2280 SV_LinkEdict_TouchAreaGrid(ent);
2283 //============================================================================
2289 Entities that are "stuck" to another entity
2292 void SV_Physics_Follow (prvm_edict_t *ent)
2294 vec3_t vf, vr, vu, angles, v;
2298 if (!SV_RunThink (ent))
2301 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2302 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2303 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])
2305 // quick case for no rotation
2306 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2310 angles[0] = -ent->fields.server->punchangle[0];
2311 angles[1] = ent->fields.server->punchangle[1];
2312 angles[2] = ent->fields.server->punchangle[2];
2313 AngleVectors (angles, vf, vr, vu);
2314 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];
2315 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];
2316 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];
2317 angles[0] = -e->fields.server->angles[0];
2318 angles[1] = e->fields.server->angles[1];
2319 angles[2] = e->fields.server->angles[2];
2320 AngleVectors (angles, vf, vr, vu);
2321 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2322 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2323 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2325 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2327 //SV_LinkEdict_TouchAreaGrid(ent);
2331 ==============================================================================
2335 ==============================================================================
2340 SV_CheckWaterTransition
2344 void SV_CheckWaterTransition (prvm_edict_t *ent)
2347 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2348 if (!ent->fields.server->watertype)
2350 // just spawned here
2351 ent->fields.server->watertype = cont;
2352 ent->fields.server->waterlevel = 1;
2356 // DRESK - Support for Entity Contents Transition Event
2357 // NOTE: Call here BEFORE updating the watertype below,
2358 // and suppress watersplash sound if a valid function
2359 // call was made to allow for custom "splash" sounds.
2360 if( !SV_CheckContentsTransition(ent, cont) )
2361 { // Contents Transition Function Invalid; Potentially Play Water Sound
2362 // check if the entity crossed into or out of water
2363 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2364 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2367 if (cont <= CONTENTS_WATER)
2369 ent->fields.server->watertype = cont;
2370 ent->fields.server->waterlevel = 1;
2374 ent->fields.server->watertype = CONTENTS_EMPTY;
2375 ent->fields.server->waterlevel = 0;
2383 Toss, bounce, and fly movement. When onground, do nothing.
2386 void SV_Physics_Toss (prvm_edict_t *ent)
2393 // if onground, return without moving
2394 if ((int)ent->fields.server->flags & FL_ONGROUND)
2396 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2398 // don't stick to ground if onground and moving upward
2399 ent->fields.server->flags -= FL_ONGROUND;
2401 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2403 // we can trust FL_ONGROUND if groundentity is world because it never moves
2406 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2408 // if ent was supported by a brush model on previous frame,
2409 // and groundentity is now freed, set groundentity to 0 (world)
2410 // which leaves it suspended in the air
2411 ent->fields.server->groundentity = 0;
2412 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2416 ent->priv.server->suspendedinairflag = false;
2418 SV_CheckVelocity (ent);
2421 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2422 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2425 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2427 movetime = sv.frametime;
2428 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2431 VectorScale (ent->fields.server->velocity, movetime, move);
2432 if(!SV_PushEntity (&trace, ent, move, true, true))
2433 return; // teleported
2434 if (ent->priv.server->free)
2436 if (trace.bmodelstartsolid)
2438 // try to unstick the entity
2439 SV_UnstickEntity(ent);
2440 if(!SV_PushEntity (&trace, ent, move, false, true))
2441 return; // teleported
2442 if (ent->priv.server->free)
2445 if (trace.fraction == 1)
2447 movetime *= 1 - min(1, trace.fraction);
2448 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2451 float bouncefactor = 1.0f;
2452 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2453 if (val!=0 && val->_float)
2454 bouncefactor = val->_float;
2456 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2457 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2459 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2461 float d, ent_gravity;
2463 float bouncefactor = 0.5f;
2464 float bouncestop = 60.0f / 800.0f;
2466 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2467 if (val!=0 && val->_float)
2468 bouncefactor = val->_float;
2470 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2471 if (val!=0 && val->_float)
2472 bouncestop = val->_float;
2474 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2475 // LordHavoc: fixed grenades not bouncing when fired down a slope
2476 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2477 if (val!=0 && val->_float)
2478 ent_gravity = val->_float;
2481 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2483 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2484 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2486 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2487 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2488 VectorClear (ent->fields.server->velocity);
2489 VectorClear (ent->fields.server->avelocity);
2492 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2496 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2498 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2499 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2500 VectorClear (ent->fields.server->velocity);
2501 VectorClear (ent->fields.server->avelocity);
2504 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2509 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2510 if (trace.plane.normal[2] > 0.7)
2512 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2513 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2514 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2515 ent->priv.server->suspendedinairflag = true;
2516 VectorClear (ent->fields.server->velocity);
2517 VectorClear (ent->fields.server->avelocity);
2520 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2522 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2526 // check for in water
2527 SV_CheckWaterTransition (ent);
2531 ===============================================================================
2535 ===============================================================================
2542 Monsters freefall when they don't have a ground entity, otherwise
2543 all movement is done with discrete steps.
2545 This is also used for objects that have become still on the ground, but
2546 will fall if the floor is pulled out from under them.
2549 void SV_Physics_Step (prvm_edict_t *ent)
2551 int flags = (int)ent->fields.server->flags;
2554 // Backup Velocity in the event that movetypesteplandevent is called,
2555 // to provide a parameter with the entity's velocity at impact.
2556 prvm_eval_t *movetypesteplandevent;
2557 vec3_t backupVelocity;
2558 VectorCopy(ent->fields.server->velocity, backupVelocity);
2559 // don't fall at all if fly/swim
2560 if (!(flags & (FL_FLY | FL_SWIM)))
2562 if (flags & FL_ONGROUND)
2564 // freefall if onground and moving upward
2565 // freefall if not standing on a world surface (it may be a lift or trap door)
2566 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2568 ent->fields.server->flags -= FL_ONGROUND;
2569 SV_CheckVelocity(ent);
2570 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2572 SV_LinkEdict_TouchAreaGrid(ent);
2573 ent->priv.server->waterposition_forceupdate = true;
2578 // freefall if not onground
2579 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2581 SV_CheckVelocity(ent);
2582 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2584 SV_LinkEdict_TouchAreaGrid(ent);
2587 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2589 // DRESK - Check for Entity Land Event Function
2590 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2592 if(movetypesteplandevent->function)
2593 { // Valid Function; Execute
2594 // Prepare Parameters
2595 // Assign Velocity at Impact
2596 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2597 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2598 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2600 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2601 // Execute VM Function
2602 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2605 // Check for Engine Landing Sound
2606 if(sv_sound_land.string)
2607 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2609 ent->priv.server->waterposition_forceupdate = true;
2614 if (!SV_RunThink(ent))
2617 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2619 ent->priv.server->waterposition_forceupdate = false;
2620 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2621 SV_CheckWaterTransition(ent);
2625 //============================================================================
2627 static void SV_Physics_Entity (prvm_edict_t *ent)
2629 // don't run think/move on newly spawned projectiles as it messes up
2630 // movement interpolation and rocket trails, and is inconsistent with
2631 // respect to entities spawned in the same frame
2632 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2633 // but if it spawns a lower numbered ent, it doesn't - this never moves
2634 // ents in the first frame regardless)
2635 qboolean runmove = ent->priv.server->move;
2636 ent->priv.server->move = true;
2637 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2639 switch ((int) ent->fields.server->movetype)
2642 case MOVETYPE_FAKEPUSH:
2643 SV_Physics_Pusher (ent);
2646 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2647 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2650 case MOVETYPE_FOLLOW:
2651 SV_Physics_Follow (ent);
2653 case MOVETYPE_NOCLIP:
2654 if (SV_RunThink(ent))
2657 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2658 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2663 SV_Physics_Step (ent);
2666 if (SV_RunThink (ent))
2670 case MOVETYPE_BOUNCE:
2671 case MOVETYPE_BOUNCEMISSILE:
2672 case MOVETYPE_FLYMISSILE:
2675 if (SV_RunThink (ent))
2676 SV_Physics_Toss (ent);
2678 case MOVETYPE_PHYSICS:
2679 if (SV_RunThink(ent))
2682 SV_LinkEdict_TouchAreaGrid(ent);
2686 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2691 void SV_Physics_ClientMove(void)
2694 ent = host_client->edict;
2696 // call player physics, this needs the proper frametime
2697 prog->globals.server->frametime = sv.frametime;
2700 // call standard client pre-think, with frametime = 0
2701 prog->globals.server->time = sv.time;
2702 prog->globals.server->frametime = 0;
2703 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2704 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2705 prog->globals.server->frametime = sv.frametime;
2707 // make sure the velocity is sane (not a NaN)
2708 SV_CheckVelocity(ent);
2710 // perform MOVETYPE_WALK behavior
2713 // call standard player post-think, with frametime = 0
2714 prog->globals.server->time = sv.time;
2715 prog->globals.server->frametime = 0;
2716 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2717 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2718 prog->globals.server->frametime = sv.frametime;
2720 if(ent->fields.server->fixangle)
2722 // angle fixing was requested by physics code...
2723 // so store the current angles for later use
2724 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2725 host_client->fixangle_angles_set = TRUE;
2727 // and clear fixangle for the next frame
2728 ent->fields.server->fixangle = 0;
2732 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2734 // don't do physics on disconnected clients, FrikBot relies on this
2735 if (!host_client->spawned)
2738 // make sure the velocity is sane (not a NaN)
2739 SV_CheckVelocity(ent);
2741 // don't run physics here if running asynchronously
2742 if (host_client->clmovement_inputtimeout <= 0)
2745 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2748 // make sure the velocity is still sane (not a NaN)
2749 SV_CheckVelocity(ent);
2751 // call standard client pre-think
2752 prog->globals.server->time = sv.time;
2753 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2754 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2756 // make sure the velocity is still sane (not a NaN)
2757 SV_CheckVelocity(ent);
2760 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2762 // don't do physics on disconnected clients, FrikBot relies on this
2763 if (!host_client->spawned)
2766 // make sure the velocity is sane (not a NaN)
2767 SV_CheckVelocity(ent);
2769 // call standard player post-think
2770 prog->globals.server->time = sv.time;
2771 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2772 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2774 // make sure the velocity is still sane (not a NaN)
2775 SV_CheckVelocity(ent);
2777 if(ent->fields.server->fixangle)
2779 // angle fixing was requested by physics code...
2780 // so store the current angles for later use
2781 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2782 host_client->fixangle_angles_set = TRUE;
2784 // and clear fixangle for the next frame
2785 ent->fields.server->fixangle = 0;
2788 // decrement the countdown variable used to decide when to go back to
2789 // synchronous physics
2790 if (host_client->clmovement_inputtimeout > sv.frametime)
2791 host_client->clmovement_inputtimeout -= sv.frametime;
2793 host_client->clmovement_inputtimeout = 0;
2796 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2798 // don't do physics on disconnected clients, FrikBot relies on this
2799 if (!host_client->spawned)
2801 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2805 // make sure the velocity is sane (not a NaN)
2806 SV_CheckVelocity(ent);
2808 switch ((int) ent->fields.server->movetype)
2811 case MOVETYPE_FAKEPUSH:
2812 SV_Physics_Pusher (ent);
2815 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2816 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2819 case MOVETYPE_FOLLOW:
2820 SV_Physics_Follow (ent);
2822 case MOVETYPE_NOCLIP:
2825 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2826 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2829 SV_Physics_Step (ent);
2833 // don't run physics here if running asynchronously
2834 if (host_client->clmovement_inputtimeout <= 0)
2838 case MOVETYPE_BOUNCE:
2839 case MOVETYPE_BOUNCEMISSILE:
2840 case MOVETYPE_FLYMISSILE:
2843 SV_Physics_Toss (ent);
2849 case MOVETYPE_PHYSICS:
2853 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2857 SV_CheckVelocity (ent);
2860 SV_LinkEdict_TouchAreaGrid(ent);
2862 SV_CheckVelocity (ent);
2871 void SV_Physics (void)
2876 // let the progs know that a new frame has started
2877 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2878 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2879 prog->globals.server->time = sv.time;
2880 prog->globals.server->frametime = sv.frametime;
2881 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2883 // run physics engine
2884 World_Physics_Frame(&sv.world, sv.frametime, sv_gravity.value);
2887 // treat each object in turn
2890 // if force_retouch, relink all the entities
2891 if (prog->globals.server->force_retouch > 0)
2892 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2893 if (!ent->priv.server->free)
2894 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2896 if (sv_gameplayfix_consistentplayerprethink.integer)
2898 // run physics on the client entities in 3 stages
2899 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2900 if (!ent->priv.server->free)
2901 SV_Physics_ClientEntity_PreThink(ent);
2903 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2904 if (!ent->priv.server->free)
2905 SV_Physics_ClientEntity(ent);
2907 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2908 if (!ent->priv.server->free)
2909 SV_Physics_ClientEntity_PostThink(ent);
2913 // run physics on the client entities
2914 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2916 if (!ent->priv.server->free)
2918 SV_Physics_ClientEntity_PreThink(ent);
2919 SV_Physics_ClientEntity(ent);
2920 SV_Physics_ClientEntity_PostThink(ent);
2925 // run physics on all the non-client entities
2926 if (!sv_freezenonclients.integer)
2928 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2929 if (!ent->priv.server->free)
2930 SV_Physics_Entity(ent);
2931 // make a second pass to see if any ents spawned this frame and make
2932 // sure they run their move/think
2933 if (sv_gameplayfix_delayprojectiles.integer < 0)
2934 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2935 if (!ent->priv.server->move && !ent->priv.server->free)
2936 SV_Physics_Entity(ent);
2939 if (prog->globals.server->force_retouch > 0)
2940 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2942 // LordHavoc: endframe support
2943 if (prog->funcoffsets.EndFrame)
2945 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2946 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2947 prog->globals.server->time = sv.time;
2948 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2951 // decrement prog->num_edicts if the highest number entities died
2952 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2954 if (!sv_freezenonclients.integer)
2955 sv.time += sv.frametime;