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 if (check->fields.server->movetype == MOVETYPE_NONE
1638 || check->fields.server->movetype == MOVETYPE_PUSH
1639 || check->fields.server->movetype == MOVETYPE_FOLLOW
1640 || check->fields.server->movetype == MOVETYPE_NOCLIP
1641 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1644 if (check->fields.server->owner == pusherprog)
1647 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1650 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1652 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1653 check->priv.server->waterposition_forceupdate = true;
1655 checkcontents = SV_GenericHitSuperContentsMask(check);
1657 // if the entity is standing on the pusher, it will definitely be moved
1658 // if the entity is not standing on the pusher, but is in the pusher's
1659 // final position, move it
1660 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1662 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);
1663 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1664 if (!trace.startsolid)
1666 //Con_Printf("- not in solid\n");
1674 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1675 org2[0] = DotProduct (org, forward);
1676 org2[1] = DotProduct (org, left);
1677 org2[2] = DotProduct (org, up);
1678 VectorSubtract (org2, org, move);
1679 VectorAdd (move, move1, move);
1682 VectorCopy (move1, move);
1684 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1686 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1687 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1688 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1690 // try moving the contacted entity
1691 pusher->fields.server->solid = SOLID_NOT;
1692 if(!SV_PushEntity (&trace, check, move, true, true))
1694 // entity "check" got teleported
1695 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1696 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1697 continue; // pushed enough
1699 // FIXME: turn players specially
1700 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1701 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1702 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1704 // this trace.fraction < 1 check causes items to fall off of pushers
1705 // if they pass under or through a wall
1706 // the groundentity check causes items to fall off of ledges
1707 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1708 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1710 // if it is still inside the pusher, block
1711 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);
1712 if (trace.startsolid)
1714 // try moving the contacted entity a tiny bit further to account for precision errors
1716 pusher->fields.server->solid = SOLID_NOT;
1717 VectorScale(move, 1.1, move2);
1718 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1719 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1720 if(!SV_PushEntity (&trace2, check, move2, true, true))
1722 // entity "check" got teleported
1725 pusher->fields.server->solid = savesolid;
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 less to account for precision errors
1730 pusher->fields.server->solid = SOLID_NOT;
1731 VectorScale(move, 0.9, move2);
1732 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1733 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1734 if(!SV_PushEntity (&trace2, check, move2, true, true))
1736 // entity "check" got teleported
1739 pusher->fields.server->solid = savesolid;
1740 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);
1741 if (trace.startsolid)
1743 // still inside pusher, so it's really blocked
1746 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1748 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1751 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1752 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1756 VectorCopy (pushorig, pusher->fields.server->origin);
1757 VectorCopy (pushang, pusher->fields.server->angles);
1758 pusher->fields.server->ltime = pushltime;
1759 SV_LinkEdict(pusher);
1761 // move back any entities we already moved
1762 for (i = 0;i < num_moved;i++)
1764 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1765 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1766 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1770 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1771 if (pusher->fields.server->blocked)
1773 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1774 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1775 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1782 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1783 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1784 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1793 void SV_Physics_Pusher (prvm_edict_t *ent)
1795 float thinktime, oldltime, movetime;
1797 oldltime = ent->fields.server->ltime;
1799 thinktime = ent->fields.server->nextthink;
1800 if (thinktime < ent->fields.server->ltime + sv.frametime)
1802 movetime = thinktime - ent->fields.server->ltime;
1807 movetime = sv.frametime;
1810 // advances ent->fields.server->ltime if not blocked
1811 SV_PushMove (ent, movetime);
1813 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1815 ent->fields.server->nextthink = 0;
1816 prog->globals.server->time = sv.time;
1817 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1818 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1819 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1825 ===============================================================================
1829 ===============================================================================
1832 static float unstickoffsets[] =
1834 // poutting -/+z changes first as they are least weird
1849 typedef enum unstickresult_e
1857 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1861 // if not stuck in a bmodel, just return
1862 if (!SV_TestEntityPosition(ent, vec3_origin))
1863 return UNSTICK_GOOD;
1865 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1867 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1869 VectorCopy(unstickoffsets + i, offset);
1871 //SV_LinkEdict_TouchAreaGrid(ent);
1872 return UNSTICK_UNSTUCK;
1876 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1877 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1879 for(i = 2; i <= maxunstick; ++i)
1881 VectorClear(offset);
1883 if (!SV_TestEntityPosition(ent, offset))
1886 //SV_LinkEdict_TouchAreaGrid(ent);
1887 return UNSTICK_UNSTUCK;
1890 if (!SV_TestEntityPosition(ent, offset))
1893 //SV_LinkEdict_TouchAreaGrid(ent);
1894 return UNSTICK_UNSTUCK;
1898 return UNSTICK_STUCK;
1901 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1904 switch(SV_UnstickEntityReturnOffset(ent, offset))
1908 case UNSTICK_UNSTUCK:
1909 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]);
1912 if (developer.integer >= 100)
1913 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1916 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1925 This is a big hack to try and fix the rare case of getting stuck in the world
1929 void SV_CheckStuck (prvm_edict_t *ent)
1933 switch(SV_UnstickEntityReturnOffset(ent, offset))
1936 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1938 case UNSTICK_UNSTUCK:
1939 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]);
1942 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1943 if (!SV_TestEntityPosition(ent, offset))
1945 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1947 //SV_LinkEdict_TouchAreaGrid(ent);
1950 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1953 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1963 qboolean SV_CheckWater (prvm_edict_t *ent)
1966 int nNativeContents;
1969 point[0] = ent->fields.server->origin[0];
1970 point[1] = ent->fields.server->origin[1];
1971 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1973 // DRESK - Support for Entity Contents Transition Event
1974 // NOTE: Some logic needed to be slightly re-ordered
1975 // to not affect performance and allow for the feature.
1977 // Acquire Super Contents Prior to Resets
1978 cont = SV_PointSuperContents(point);
1979 // Acquire Native Contents Here
1980 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1982 // DRESK - Support for Entity Contents Transition Event
1983 if(ent->fields.server->watertype)
1984 // Entity did NOT Spawn; Check
1985 SV_CheckContentsTransition(ent, nNativeContents);
1988 ent->fields.server->waterlevel = 0;
1989 ent->fields.server->watertype = CONTENTS_EMPTY;
1990 cont = SV_PointSuperContents(point);
1991 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1993 ent->fields.server->watertype = nNativeContents;
1994 ent->fields.server->waterlevel = 1;
1995 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1996 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1998 ent->fields.server->waterlevel = 2;
1999 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
2000 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
2001 ent->fields.server->waterlevel = 3;
2005 return ent->fields.server->waterlevel > 1;
2014 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2017 vec3_t forward, into, side;
2019 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2020 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2022 // cut the tangential velocity
2023 i = DotProduct (stepnormal, ent->fields.server->velocity);
2024 VectorScale (stepnormal, i, into);
2025 VectorSubtract (ent->fields.server->velocity, into, side);
2026 ent->fields.server->velocity[0] = side[0] * (1 + d);
2027 ent->fields.server->velocity[1] = side[1] * (1 + d);
2033 =====================
2036 Player has come to a dead stop, possibly due to the problem with limited
2037 float precision at some angle joins in the BSP hull.
2039 Try fixing by pushing one pixel in each direction.
2041 This is a hack, but in the interest of good gameplay...
2042 ======================
2044 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2049 VectorCopy (ent->fields.server->origin, oldorg);
2052 for (i=0 ; i<8 ; i++)
2054 // try pushing a little in an axial direction
2057 case 0: dir[0] = 2; dir[1] = 0; break;
2058 case 1: dir[0] = 0; dir[1] = 2; break;
2059 case 2: dir[0] = -2; dir[1] = 0; break;
2060 case 3: dir[0] = 0; dir[1] = -2; break;
2061 case 4: dir[0] = 2; dir[1] = 2; break;
2062 case 5: dir[0] = -2; dir[1] = 2; break;
2063 case 6: dir[0] = 2; dir[1] = -2; break;
2064 case 7: dir[0] = -2; dir[1] = -2; break;
2067 SV_PushEntity (&trace, ent, dir, false, true);
2069 // retry the original move
2070 ent->fields.server->velocity[0] = oldvel[0];
2071 ent->fields.server->velocity[1] = oldvel[1];
2072 ent->fields.server->velocity[2] = 0;
2073 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2075 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2076 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2078 Con_DPrint("TryUnstick - success.\n");
2082 // go back to the original pos and try again
2083 VectorCopy (oldorg, ent->fields.server->origin);
2087 VectorClear (ent->fields.server->velocity);
2088 Con_DPrint("TryUnstick - failure.\n");
2094 =====================
2097 Only used by players
2098 ======================
2100 void SV_WalkMove (prvm_edict_t *ent)
2102 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2103 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2104 trace_t downtrace, trace;
2105 qboolean applygravity;
2107 // if frametime is 0 (due to client sending the same timestamp twice),
2109 if (sv.frametime <= 0)
2112 SV_CheckStuck (ent);
2114 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2116 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2118 SV_CheckVelocity(ent);
2120 // do a regular slide move unless it looks like you ran into a step
2121 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2123 VectorCopy (ent->fields.server->origin, start_origin);
2124 VectorCopy (ent->fields.server->velocity, start_velocity);
2126 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2128 // if the move did not hit the ground at any point, we're not on ground
2130 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2132 SV_CheckVelocity(ent);
2134 SV_LinkEdict_TouchAreaGrid(ent);
2136 if(clip & 8) // teleport
2139 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2142 if (sv_nostep.integer)
2145 VectorCopy(ent->fields.server->origin, originalmove_origin);
2146 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2147 originalmove_clip = clip;
2148 originalmove_flags = (int)ent->fields.server->flags;
2149 originalmove_groundentity = ent->fields.server->groundentity;
2151 // if move didn't block on a step, return
2154 // if move was not trying to move into the step, return
2155 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2158 if (ent->fields.server->movetype != MOVETYPE_FLY)
2160 // return if gibbed by a trigger
2161 if (ent->fields.server->movetype != MOVETYPE_WALK)
2164 // only step up while jumping if that is enabled
2165 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2166 if (!oldonground && ent->fields.server->waterlevel == 0)
2170 // try moving up and forward to go up a step
2171 // back to start pos
2172 VectorCopy (start_origin, ent->fields.server->origin);
2173 VectorCopy (start_velocity, ent->fields.server->velocity);
2176 VectorClear (upmove);
2177 upmove[2] = sv_stepheight.value;
2178 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2180 // we got teleported when upstepping... must abort the move
2185 ent->fields.server->velocity[2] = 0;
2186 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2187 ent->fields.server->velocity[2] += start_velocity[2];
2190 // we got teleported when upstepping... must abort the move
2191 // note that z velocity handling may not be what QC expects here, but we cannot help it
2195 SV_CheckVelocity(ent);
2197 SV_LinkEdict_TouchAreaGrid(ent);
2199 // check for stuckness, possibly due to the limited precision of floats
2200 // in the clipping hulls
2202 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2203 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2205 //Con_Printf("wall\n");
2206 // stepping up didn't make any progress, revert to original move
2207 VectorCopy(originalmove_origin, ent->fields.server->origin);
2208 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2209 //clip = originalmove_clip;
2210 ent->fields.server->flags = originalmove_flags;
2211 ent->fields.server->groundentity = originalmove_groundentity;
2212 // now try to unstick if needed
2213 //clip = SV_TryUnstick (ent, oldvel);
2217 //Con_Printf("step - ");
2219 // extra friction based on view angle
2220 if (clip & 2 && sv_wallfriction.integer)
2221 SV_WallFriction (ent, stepnormal);
2223 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2224 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))
2228 VectorClear (downmove);
2229 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2230 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2232 // we got teleported when downstepping... must abort the move
2236 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2238 // this has been disabled so that you can't jump when you are stepping
2239 // up while already jumping (also known as the Quake2 double jump bug)
2241 // LordHavoc: disabled this check so you can walk on monsters/players
2242 //if (ent->fields.server->solid == SOLID_BSP)
2244 //Con_Printf("onground\n");
2245 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2246 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2252 //Con_Printf("slope\n");
2253 // if the push down didn't end up on good ground, use the move without
2254 // the step up. This happens near wall / slope combinations, and can
2255 // cause the player to hop up higher on a slope too steep to climb
2256 VectorCopy(originalmove_origin, ent->fields.server->origin);
2257 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2258 //clip = originalmove_clip;
2259 ent->fields.server->flags = originalmove_flags;
2260 ent->fields.server->groundentity = originalmove_groundentity;
2263 SV_CheckVelocity(ent);
2265 SV_LinkEdict_TouchAreaGrid(ent);
2268 //============================================================================
2274 Entities that are "stuck" to another entity
2277 void SV_Physics_Follow (prvm_edict_t *ent)
2279 vec3_t vf, vr, vu, angles, v;
2283 if (!SV_RunThink (ent))
2286 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2287 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2288 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])
2290 // quick case for no rotation
2291 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2295 angles[0] = -ent->fields.server->punchangle[0];
2296 angles[1] = ent->fields.server->punchangle[1];
2297 angles[2] = ent->fields.server->punchangle[2];
2298 AngleVectors (angles, vf, vr, vu);
2299 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];
2300 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];
2301 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];
2302 angles[0] = -e->fields.server->angles[0];
2303 angles[1] = e->fields.server->angles[1];
2304 angles[2] = e->fields.server->angles[2];
2305 AngleVectors (angles, vf, vr, vu);
2306 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2307 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2308 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2310 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2312 //SV_LinkEdict_TouchAreaGrid(ent);
2316 ==============================================================================
2320 ==============================================================================
2325 SV_CheckWaterTransition
2329 void SV_CheckWaterTransition (prvm_edict_t *ent)
2332 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2333 if (!ent->fields.server->watertype)
2335 // just spawned here
2336 ent->fields.server->watertype = cont;
2337 ent->fields.server->waterlevel = 1;
2341 // DRESK - Support for Entity Contents Transition Event
2342 // NOTE: Call here BEFORE updating the watertype below,
2343 // and suppress watersplash sound if a valid function
2344 // call was made to allow for custom "splash" sounds.
2345 if( !SV_CheckContentsTransition(ent, cont) )
2346 { // Contents Transition Function Invalid; Potentially Play Water Sound
2347 // check if the entity crossed into or out of water
2348 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2349 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2352 if (cont <= CONTENTS_WATER)
2354 ent->fields.server->watertype = cont;
2355 ent->fields.server->waterlevel = 1;
2359 ent->fields.server->watertype = CONTENTS_EMPTY;
2360 ent->fields.server->waterlevel = 0;
2368 Toss, bounce, and fly movement. When onground, do nothing.
2371 void SV_Physics_Toss (prvm_edict_t *ent)
2378 // if onground, return without moving
2379 if ((int)ent->fields.server->flags & FL_ONGROUND)
2381 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2383 // don't stick to ground if onground and moving upward
2384 ent->fields.server->flags -= FL_ONGROUND;
2386 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2388 // we can trust FL_ONGROUND if groundentity is world because it never moves
2391 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2393 // if ent was supported by a brush model on previous frame,
2394 // and groundentity is now freed, set groundentity to 0 (world)
2395 // which leaves it suspended in the air
2396 ent->fields.server->groundentity = 0;
2397 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2401 ent->priv.server->suspendedinairflag = false;
2403 SV_CheckVelocity (ent);
2406 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2407 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2410 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2412 movetime = sv.frametime;
2413 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2416 VectorScale (ent->fields.server->velocity, movetime, move);
2417 if(!SV_PushEntity (&trace, ent, move, true, true))
2418 return; // teleported
2419 if (ent->priv.server->free)
2421 if (trace.bmodelstartsolid)
2423 // try to unstick the entity
2424 SV_UnstickEntity(ent);
2425 if(!SV_PushEntity (&trace, ent, move, false, true))
2426 return; // teleported
2427 if (ent->priv.server->free)
2430 if (trace.fraction == 1)
2432 movetime *= 1 - min(1, trace.fraction);
2433 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2436 float bouncefactor = 1.0f;
2437 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2438 if (val!=0 && val->_float)
2439 bouncefactor = val->_float;
2441 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2442 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2444 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2446 float d, ent_gravity;
2448 float bouncefactor = 0.5f;
2449 float bouncestop = 60.0f / 800.0f;
2451 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2452 if (val!=0 && val->_float)
2453 bouncefactor = val->_float;
2455 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2456 if (val!=0 && val->_float)
2457 bouncestop = val->_float;
2459 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2460 // LordHavoc: fixed grenades not bouncing when fired down a slope
2461 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2462 if (val!=0 && val->_float)
2463 ent_gravity = val->_float;
2466 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2468 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2469 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2471 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2472 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2473 VectorClear (ent->fields.server->velocity);
2474 VectorClear (ent->fields.server->avelocity);
2477 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2481 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2483 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2484 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2485 VectorClear (ent->fields.server->velocity);
2486 VectorClear (ent->fields.server->avelocity);
2489 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2494 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2495 if (trace.plane.normal[2] > 0.7)
2497 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2498 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2499 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2500 ent->priv.server->suspendedinairflag = true;
2501 VectorClear (ent->fields.server->velocity);
2502 VectorClear (ent->fields.server->avelocity);
2505 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2507 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2511 // check for in water
2512 SV_CheckWaterTransition (ent);
2516 ===============================================================================
2520 ===============================================================================
2527 Monsters freefall when they don't have a ground entity, otherwise
2528 all movement is done with discrete steps.
2530 This is also used for objects that have become still on the ground, but
2531 will fall if the floor is pulled out from under them.
2534 void SV_Physics_Step (prvm_edict_t *ent)
2536 int flags = (int)ent->fields.server->flags;
2539 // Backup Velocity in the event that movetypesteplandevent is called,
2540 // to provide a parameter with the entity's velocity at impact.
2541 prvm_eval_t *movetypesteplandevent;
2542 vec3_t backupVelocity;
2543 VectorCopy(ent->fields.server->velocity, backupVelocity);
2544 // don't fall at all if fly/swim
2545 if (!(flags & (FL_FLY | FL_SWIM)))
2547 if (flags & FL_ONGROUND)
2549 // freefall if onground and moving upward
2550 // freefall if not standing on a world surface (it may be a lift or trap door)
2551 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2553 ent->fields.server->flags -= FL_ONGROUND;
2554 SV_CheckVelocity(ent);
2555 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2557 SV_LinkEdict_TouchAreaGrid(ent);
2558 ent->priv.server->waterposition_forceupdate = true;
2563 // freefall if not onground
2564 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2566 SV_CheckVelocity(ent);
2567 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2569 SV_LinkEdict_TouchAreaGrid(ent);
2572 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2574 // DRESK - Check for Entity Land Event Function
2575 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2577 if(movetypesteplandevent->function)
2578 { // Valid Function; Execute
2579 // Prepare Parameters
2580 // Assign Velocity at Impact
2581 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2582 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2583 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2585 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2586 // Execute VM Function
2587 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2590 // Check for Engine Landing Sound
2591 if(sv_sound_land.string)
2592 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2594 ent->priv.server->waterposition_forceupdate = true;
2599 if (!SV_RunThink(ent))
2602 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2604 ent->priv.server->waterposition_forceupdate = false;
2605 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2606 SV_CheckWaterTransition(ent);
2610 //============================================================================
2612 static void SV_Physics_Entity (prvm_edict_t *ent)
2614 // don't run think/move on newly spawned projectiles as it messes up
2615 // movement interpolation and rocket trails, and is inconsistent with
2616 // respect to entities spawned in the same frame
2617 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2618 // but if it spawns a lower numbered ent, it doesn't - this never moves
2619 // ents in the first frame regardless)
2620 qboolean runmove = ent->priv.server->move;
2621 ent->priv.server->move = true;
2622 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2624 switch ((int) ent->fields.server->movetype)
2627 case MOVETYPE_FAKEPUSH:
2628 SV_Physics_Pusher (ent);
2631 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2632 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2635 case MOVETYPE_FOLLOW:
2636 SV_Physics_Follow (ent);
2638 case MOVETYPE_NOCLIP:
2639 if (SV_RunThink(ent))
2642 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2643 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2648 SV_Physics_Step (ent);
2651 if (SV_RunThink (ent))
2655 case MOVETYPE_BOUNCE:
2656 case MOVETYPE_BOUNCEMISSILE:
2657 case MOVETYPE_FLYMISSILE:
2660 if (SV_RunThink (ent))
2661 SV_Physics_Toss (ent);
2664 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2669 void SV_Physics_ClientMove(void)
2672 ent = host_client->edict;
2674 // call player physics, this needs the proper frametime
2675 prog->globals.server->frametime = sv.frametime;
2678 // call standard client pre-think, with frametime = 0
2679 prog->globals.server->time = sv.time;
2680 prog->globals.server->frametime = 0;
2681 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2682 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2683 prog->globals.server->frametime = sv.frametime;
2685 // make sure the velocity is sane (not a NaN)
2686 SV_CheckVelocity(ent);
2688 // perform MOVETYPE_WALK behavior
2691 // call standard player post-think, with frametime = 0
2692 prog->globals.server->time = sv.time;
2693 prog->globals.server->frametime = 0;
2694 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2695 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2696 prog->globals.server->frametime = sv.frametime;
2698 if(ent->fields.server->fixangle)
2700 // angle fixing was requested by physics code...
2701 // so store the current angles for later use
2702 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2703 host_client->fixangle_angles_set = TRUE;
2705 // and clear fixangle for the next frame
2706 ent->fields.server->fixangle = 0;
2710 static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
2712 // don't do physics on disconnected clients, FrikBot relies on this
2713 if (!host_client->spawned)
2716 // make sure the velocity is sane (not a NaN)
2717 SV_CheckVelocity(ent);
2719 // don't run physics here if running asynchronously
2720 if (host_client->clmovement_inputtimeout <= 0)
2723 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2726 // make sure the velocity is still sane (not a NaN)
2727 SV_CheckVelocity(ent);
2729 // call standard client pre-think
2730 prog->globals.server->time = sv.time;
2731 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2732 PRVM_ExecuteProgram(prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2734 // make sure the velocity is still sane (not a NaN)
2735 SV_CheckVelocity(ent);
2738 static void SV_Physics_ClientEntity_PostThink(prvm_edict_t *ent)
2740 // don't do physics on disconnected clients, FrikBot relies on this
2741 if (!host_client->spawned)
2744 // make sure the velocity is sane (not a NaN)
2745 SV_CheckVelocity(ent);
2747 // call standard player post-think
2748 prog->globals.server->time = sv.time;
2749 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2750 PRVM_ExecuteProgram(prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2752 // make sure the velocity is still sane (not a NaN)
2753 SV_CheckVelocity(ent);
2755 if(ent->fields.server->fixangle)
2757 // angle fixing was requested by physics code...
2758 // so store the current angles for later use
2759 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2760 host_client->fixangle_angles_set = TRUE;
2762 // and clear fixangle for the next frame
2763 ent->fields.server->fixangle = 0;
2766 // decrement the countdown variable used to decide when to go back to
2767 // synchronous physics
2768 if (host_client->clmovement_inputtimeout > sv.frametime)
2769 host_client->clmovement_inputtimeout -= sv.frametime;
2771 host_client->clmovement_inputtimeout = 0;
2774 static void SV_Physics_ClientEntity(prvm_edict_t *ent)
2776 // don't do physics on disconnected clients, FrikBot relies on this
2777 if (!host_client->spawned)
2779 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2783 // make sure the velocity is sane (not a NaN)
2784 SV_CheckVelocity(ent);
2786 switch ((int) ent->fields.server->movetype)
2789 case MOVETYPE_FAKEPUSH:
2790 SV_Physics_Pusher (ent);
2793 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2794 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2797 case MOVETYPE_FOLLOW:
2798 SV_Physics_Follow (ent);
2800 case MOVETYPE_NOCLIP:
2803 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2804 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2807 SV_Physics_Step (ent);
2811 // don't run physics here if running asynchronously
2812 if (host_client->clmovement_inputtimeout <= 0)
2816 case MOVETYPE_BOUNCE:
2817 case MOVETYPE_BOUNCEMISSILE:
2818 case MOVETYPE_FLYMISSILE:
2821 SV_Physics_Toss (ent);
2828 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2832 SV_CheckVelocity (ent);
2835 SV_LinkEdict_TouchAreaGrid(ent);
2837 SV_CheckVelocity (ent);
2846 void SV_Physics (void)
2851 // let the progs know that a new frame has started
2852 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2853 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2854 prog->globals.server->time = sv.time;
2855 prog->globals.server->frametime = sv.frametime;
2856 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2859 // treat each object in turn
2862 // if force_retouch, relink all the entities
2863 if (prog->globals.server->force_retouch > 0)
2864 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2865 if (!ent->priv.server->free)
2866 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2868 if (sv_gameplayfix_consistentplayerprethink.integer)
2870 // run physics on the client entities in 3 stages
2871 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2872 if (!ent->priv.server->free)
2873 SV_Physics_ClientEntity_PreThink(ent);
2875 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2876 if (!ent->priv.server->free)
2877 SV_Physics_ClientEntity(ent);
2879 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2880 if (!ent->priv.server->free)
2881 SV_Physics_ClientEntity_PostThink(ent);
2885 // run physics on the client entities
2886 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2888 if (!ent->priv.server->free)
2890 SV_Physics_ClientEntity_PreThink(ent);
2891 SV_Physics_ClientEntity(ent);
2892 SV_Physics_ClientEntity_PostThink(ent);
2897 // run physics on all the non-client entities
2898 if (!sv_freezenonclients.integer)
2900 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2901 if (!ent->priv.server->free)
2902 SV_Physics_Entity(ent);
2903 // make a second pass to see if any ents spawned this frame and make
2904 // sure they run their move/think
2905 if (sv_gameplayfix_delayprojectiles.integer < 0)
2906 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2907 if (!ent->priv.server->move && !ent->priv.server->free)
2908 SV_Physics_Entity(ent);
2911 if (prog->globals.server->force_retouch > 0)
2912 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2914 // LordHavoc: endframe support
2915 if (prog->funcoffsets.EndFrame)
2917 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2918 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2919 prog->globals.server->time = sv.time;
2920 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2923 // decrement prog->num_edicts if the highest number entities died
2924 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2926 if (!sv_freezenonclients.integer)
2927 sv.time += sv.frametime;