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: max velocity fix, inspired by Maddes's source fixes, but this is faster
1029 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
1030 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
1032 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
1033 ent->fields.server->velocity[0] *= wishspeed;
1034 ent->fields.server->velocity[1] *= wishspeed;
1035 ent->fields.server->velocity[2] *= wishspeed;
1043 Runs thinking code if time. There is some play in the exact time the think
1044 function will be called, because it is called before any movement is done
1045 in a frame. Not used for pushmove objects, because they must be exact.
1046 Returns false if the entity removed itself.
1049 qboolean SV_RunThink (prvm_edict_t *ent)
1053 // don't let things stay in the past.
1054 // it is possible to start that way by a trigger with a local time.
1055 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
1058 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
1060 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
1061 ent->fields.server->nextthink = 0;
1062 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1063 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1064 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1065 // mods often set nextthink to time to cause a think every frame,
1066 // we don't want to loop in that case, so exit if the new nextthink is
1067 // <= the time the qc was told, also exit if it is past the end of the
1069 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
1072 return !ent->priv.server->free;
1079 Two entities have touched, so run their touch functions
1080 returns true if the impact kept the origin of the touching entity intact
1083 extern void VM_SetTraceGlobals(const trace_t *trace);
1084 extern sizebuf_t vm_tempstringsbuf;
1085 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
1087 int restorevm_tempstringsbuf_cursize;
1088 int old_self, old_other;
1090 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
1093 old_self = prog->globals.server->self;
1094 old_other = prog->globals.server->other;
1095 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1097 VectorCopy(e1->fields.server->origin, org);
1099 VM_SetTraceGlobals(trace);
1101 prog->globals.server->time = sv.time;
1102 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
1104 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
1105 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
1106 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
1109 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
1111 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
1112 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
1113 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
1114 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
1115 prog->globals.server->trace_plane_dist = -trace->plane.dist;
1116 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
1117 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
1119 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
1121 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
1123 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
1125 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
1128 prog->globals.server->self = old_self;
1129 prog->globals.server->other = old_other;
1130 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1132 return VectorCompare(e1->fields.server->origin, org);
1140 Slide off of the impacting object
1141 returns the blocked flags (1 = floor, 2 = step / wall)
1144 #define STOP_EPSILON 0.1
1145 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
1150 backoff = -DotProduct (in, normal) * overbounce;
1151 VectorMA(in, backoff, normal, out);
1153 for (i = 0;i < 3;i++)
1154 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
1163 The basic solid body movement clip that slides along multiple planes
1164 Returns the clipflags if the velocity was modified (hit something solid)
1168 8 = teleported by touch method
1169 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
1172 static float SV_Gravity (prvm_edict_t *ent);
1173 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
1174 #define MAX_CLIP_PLANES 5
1175 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
1177 int blocked, bumpcount;
1178 int i, j, numplanes;
1179 float d, time_left, gravity;
1180 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
1190 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
1192 gravity = SV_Gravity(ent) * 0.5f;
1193 ent->fields.server->velocity[2] -= gravity;
1197 applygravity = false;
1198 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1202 VectorCopy(ent->fields.server->velocity, original_velocity);
1203 VectorCopy(ent->fields.server->velocity, primal_velocity);
1206 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
1208 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
1211 VectorScale(ent->fields.server->velocity, time_left, push);
1213 VectorAdd(ent->fields.server->origin, push, end);
1215 if(!SV_PushEntity(&trace, ent, push, false, false))
1217 // we got teleported by a touch function
1218 // let's abort the move
1224 //if (trace.fraction < 0.002)
1229 VectorCopy(ent->fields.server->origin, start);
1230 start[2] += 3;//0.03125;
1231 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1232 end[2] += 3;//0.03125;
1233 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1234 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)))
1236 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
1242 for (i = 0;i < numplanes;i++)
1244 VectorCopy(ent->fields.server->origin, start);
1245 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
1246 VectorMA(start, 3, planes[i], start);
1247 VectorMA(end, 3, planes[i], end);
1248 testtrace = SV_TraceBox(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1249 if (trace.fraction < testtrace.fraction)
1252 VectorCopy(start, ent->fields.server->origin);
1257 // VectorAdd(ent->fields.server->origin, planes[j], start);
1263 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);
1264 if (trace.fraction < 1)
1265 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
1270 if (trace.bmodelstartsolid)
1272 // LordHavoc: note: this code is what makes entities stick in place
1273 // if embedded in world only (you can walk through other objects if
1275 // entity is trapped in another solid
1276 VectorClear(ent->fields.server->velocity);
1281 if (trace.fraction == 1)
1283 if (trace.plane.normal[2])
1285 if (trace.plane.normal[2] > 0.7)
1292 Con_Printf ("SV_FlyMove: !trace.ent");
1293 trace.ent = prog->edicts;
1296 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1297 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1304 // save the trace for player extrafriction
1306 VectorCopy(trace.plane.normal, stepnormal);
1308 if (trace.fraction >= 0.001)
1310 // actually covered some distance
1311 VectorCopy(ent->fields.server->velocity, original_velocity);
1315 time_left *= 1 - trace.fraction;
1317 // clipped to another plane
1318 if (numplanes >= MAX_CLIP_PLANES)
1320 // this shouldn't really happen
1321 VectorClear(ent->fields.server->velocity);
1327 for (i = 0;i < numplanes;i++)
1328 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1332 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1337 VectorCopy(trace.plane.normal, planes[numplanes]);
1340 if (sv_newflymove.integer)
1341 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1344 // modify original_velocity so it parallels all of the clip planes
1345 for (i = 0;i < numplanes;i++)
1347 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1348 for (j = 0;j < numplanes;j++)
1353 if (DotProduct(new_velocity, planes[j]) < 0)
1363 // go along this plane
1364 VectorCopy(new_velocity, ent->fields.server->velocity);
1368 // go along the crease
1371 VectorClear(ent->fields.server->velocity);
1375 CrossProduct(planes[0], planes[1], dir);
1376 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1377 VectorNormalize(dir);
1378 d = DotProduct(dir, ent->fields.server->velocity);
1379 VectorScale(dir, d, ent->fields.server->velocity);
1383 // if current velocity is against the original velocity,
1384 // stop dead to avoid tiny occilations in sloping corners
1385 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1387 VectorClear(ent->fields.server->velocity);
1392 //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]);
1395 if ((blocked & 1) == 0 && bumpcount > 1)
1397 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1398 // flag ONGROUND if there's ground under it
1399 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1403 // LordHavoc: this came from QW and allows you to get out of water more easily
1404 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1405 VectorCopy(primal_velocity, ent->fields.server->velocity);
1406 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1407 ent->fields.server->velocity[2] -= gravity;
1417 static float SV_Gravity (prvm_edict_t *ent)
1422 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1423 if (val!=0 && val->_float)
1424 ent_gravity = val->_float;
1427 return ent_gravity * sv_gravity.value * sv.frametime;
1432 ===============================================================================
1436 ===============================================================================
1443 Does not change the entities velocity at all
1444 The trace struct is filled with the trace that has been done.
1445 Returns true if the push did not result in the entity being teleported by QC code.
1448 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1453 VectorAdd (ent->fields.server->origin, push, end);
1455 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1456 type = MOVE_MISSILE;
1457 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1458 type = MOVE_NOMONSTERS; // only clip against bmodels
1462 *trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1463 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1466 VectorCopy (trace->endpos, ent->fields.server->origin);
1470 if(!trace->startsolid)
1471 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)
1473 Con_Printf("something eeeeevil happened\n");
1478 SV_LinkEdict_TouchAreaGrid(ent);
1480 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))))
1481 return SV_Impact (ent, trace);
1493 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1496 int pusherowner, pusherprog;
1499 float savesolid, movetime2, pushltime;
1500 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1502 int numcheckentities;
1503 static prvm_edict_t *checkentities[MAX_EDICTS];
1504 dp_model_t *pushermodel;
1505 trace_t trace, trace2;
1506 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1507 unsigned short moved_edicts[MAX_EDICTS];
1509 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])
1511 pusher->fields.server->ltime += movetime;
1515 switch ((int) pusher->fields.server->solid)
1517 // LordHavoc: valid pusher types
1520 case SOLID_SLIDEBOX:
1521 case SOLID_CORPSE: // LordHavoc: this would be weird...
1523 // LordHavoc: no collisions
1526 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1527 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1528 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1529 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1530 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1531 pusher->fields.server->ltime += movetime;
1532 SV_LinkEdict(pusher);
1535 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1538 index = (int) pusher->fields.server->modelindex;
1539 if (index < 1 || index >= MAX_MODELS)
1541 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1544 pushermodel = sv.models[index];
1545 pusherowner = pusher->fields.server->owner;
1546 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1548 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1550 movetime2 = movetime;
1551 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1552 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1553 if (moveangle[0] || moveangle[2])
1555 for (i = 0;i < 3;i++)
1559 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1560 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1564 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1565 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1569 else if (moveangle[1])
1571 for (i = 0;i < 3;i++)
1575 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1576 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1580 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1581 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1587 for (i = 0;i < 3;i++)
1591 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1592 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1596 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1597 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1602 VectorNegate (moveangle, a);
1603 AngleVectorsFLU (a, forward, left, up);
1605 VectorCopy (pusher->fields.server->origin, pushorig);
1606 VectorCopy (pusher->fields.server->angles, pushang);
1607 pushltime = pusher->fields.server->ltime;
1609 // move the pusher to its final position
1611 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1612 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1613 pusher->fields.server->ltime += movetime;
1614 SV_LinkEdict(pusher);
1617 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1618 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1619 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);
1620 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1622 savesolid = pusher->fields.server->solid;
1624 // see if any solid entities are inside the final position
1627 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1628 for (e = 0;e < numcheckentities;e++)
1630 prvm_edict_t *check = checkentities[e];
1631 if (check->fields.server->movetype == MOVETYPE_NONE
1632 || check->fields.server->movetype == MOVETYPE_PUSH
1633 || check->fields.server->movetype == MOVETYPE_FOLLOW
1634 || check->fields.server->movetype == MOVETYPE_NOCLIP
1635 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1638 if (check->fields.server->owner == pusherprog)
1641 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1644 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1646 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1647 check->priv.server->waterposition_forceupdate = true;
1649 checkcontents = SV_GenericHitSuperContentsMask(check);
1651 // if the entity is standing on the pusher, it will definitely be moved
1652 // if the entity is not standing on the pusher, but is in the pusher's
1653 // final position, move it
1654 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1656 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);
1657 //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1658 if (!trace.startsolid)
1660 //Con_Printf("- not in solid\n");
1668 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1669 org2[0] = DotProduct (org, forward);
1670 org2[1] = DotProduct (org, left);
1671 org2[2] = DotProduct (org, up);
1672 VectorSubtract (org2, org, move);
1673 VectorAdd (move, move1, move);
1676 VectorCopy (move1, move);
1678 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1680 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1681 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1682 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1684 // try moving the contacted entity
1685 pusher->fields.server->solid = SOLID_NOT;
1686 if(!SV_PushEntity (&trace, check, move, true, true))
1688 // entity "check" got teleported
1689 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1690 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1691 continue; // pushed enough
1693 // FIXME: turn players specially
1694 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1695 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1696 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1698 // this trace.fraction < 1 check causes items to fall off of pushers
1699 // if they pass under or through a wall
1700 // the groundentity check causes items to fall off of ledges
1701 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1702 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1704 // if it is still inside the pusher, block
1705 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);
1706 if (trace.startsolid)
1708 // try moving the contacted entity a tiny bit further to account for precision errors
1710 pusher->fields.server->solid = SOLID_NOT;
1711 VectorScale(move, 1.1, move2);
1712 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1713 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1714 if(!SV_PushEntity (&trace2, check, move2, true, true))
1716 // entity "check" got teleported
1719 pusher->fields.server->solid = savesolid;
1720 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);
1721 if (trace.startsolid)
1723 // try moving the contacted entity a tiny bit less to account for precision errors
1724 pusher->fields.server->solid = SOLID_NOT;
1725 VectorScale(move, 0.9, move2);
1726 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1727 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1728 if(!SV_PushEntity (&trace2, check, move2, true, true))
1730 // entity "check" got teleported
1733 pusher->fields.server->solid = savesolid;
1734 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);
1735 if (trace.startsolid)
1737 // still inside pusher, so it's really blocked
1740 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1742 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1745 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1746 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1750 VectorCopy (pushorig, pusher->fields.server->origin);
1751 VectorCopy (pushang, pusher->fields.server->angles);
1752 pusher->fields.server->ltime = pushltime;
1753 SV_LinkEdict(pusher);
1755 // move back any entities we already moved
1756 for (i = 0;i < num_moved;i++)
1758 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1759 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1760 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1764 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1765 if (pusher->fields.server->blocked)
1767 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1768 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1769 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1776 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1777 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1778 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1787 void SV_Physics_Pusher (prvm_edict_t *ent)
1789 float thinktime, oldltime, movetime;
1791 oldltime = ent->fields.server->ltime;
1793 thinktime = ent->fields.server->nextthink;
1794 if (thinktime < ent->fields.server->ltime + sv.frametime)
1796 movetime = thinktime - ent->fields.server->ltime;
1801 movetime = sv.frametime;
1804 // advances ent->fields.server->ltime if not blocked
1805 SV_PushMove (ent, movetime);
1807 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1809 ent->fields.server->nextthink = 0;
1810 prog->globals.server->time = sv.time;
1811 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1812 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1813 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1819 ===============================================================================
1823 ===============================================================================
1826 static float unstickoffsets[] =
1828 // poutting -/+z changes first as they are least weird
1843 typedef enum unstickresult_e
1851 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1855 // if not stuck in a bmodel, just return
1856 if (!SV_TestEntityPosition(ent, vec3_origin))
1857 return UNSTICK_GOOD;
1859 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1861 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1863 VectorCopy(unstickoffsets + i, offset);
1865 //SV_LinkEdict_TouchAreaGrid(ent);
1866 return UNSTICK_UNSTUCK;
1870 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1871 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1873 for(i = 2; i <= maxunstick; ++i)
1875 VectorClear(offset);
1877 if (!SV_TestEntityPosition(ent, offset))
1880 //SV_LinkEdict_TouchAreaGrid(ent);
1881 return UNSTICK_UNSTUCK;
1884 if (!SV_TestEntityPosition(ent, offset))
1887 //SV_LinkEdict_TouchAreaGrid(ent);
1888 return UNSTICK_UNSTUCK;
1892 return UNSTICK_STUCK;
1895 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1898 switch(SV_UnstickEntityReturnOffset(ent, offset))
1902 case UNSTICK_UNSTUCK:
1903 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]);
1906 if (developer.integer >= 100)
1907 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1910 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1919 This is a big hack to try and fix the rare case of getting stuck in the world
1923 void SV_CheckStuck (prvm_edict_t *ent)
1927 switch(SV_UnstickEntityReturnOffset(ent, offset))
1930 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1932 case UNSTICK_UNSTUCK:
1933 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]);
1936 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1937 if (!SV_TestEntityPosition(ent, offset))
1939 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1941 //SV_LinkEdict_TouchAreaGrid(ent);
1944 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1947 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1957 qboolean SV_CheckWater (prvm_edict_t *ent)
1960 int nNativeContents;
1963 point[0] = ent->fields.server->origin[0];
1964 point[1] = ent->fields.server->origin[1];
1965 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1967 // DRESK - Support for Entity Contents Transition Event
1968 // NOTE: Some logic needed to be slightly re-ordered
1969 // to not affect performance and allow for the feature.
1971 // Acquire Super Contents Prior to Resets
1972 cont = SV_PointSuperContents(point);
1973 // Acquire Native Contents Here
1974 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1976 // DRESK - Support for Entity Contents Transition Event
1977 if(ent->fields.server->watertype)
1978 // Entity did NOT Spawn; Check
1979 SV_CheckContentsTransition(ent, nNativeContents);
1982 ent->fields.server->waterlevel = 0;
1983 ent->fields.server->watertype = CONTENTS_EMPTY;
1984 cont = SV_PointSuperContents(point);
1985 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1987 ent->fields.server->watertype = nNativeContents;
1988 ent->fields.server->waterlevel = 1;
1989 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1990 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1992 ent->fields.server->waterlevel = 2;
1993 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1994 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1995 ent->fields.server->waterlevel = 3;
1999 return ent->fields.server->waterlevel > 1;
2008 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
2011 vec3_t forward, into, side;
2013 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
2014 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
2016 // cut the tangential velocity
2017 i = DotProduct (stepnormal, ent->fields.server->velocity);
2018 VectorScale (stepnormal, i, into);
2019 VectorSubtract (ent->fields.server->velocity, into, side);
2020 ent->fields.server->velocity[0] = side[0] * (1 + d);
2021 ent->fields.server->velocity[1] = side[1] * (1 + d);
2027 =====================
2030 Player has come to a dead stop, possibly due to the problem with limited
2031 float precision at some angle joins in the BSP hull.
2033 Try fixing by pushing one pixel in each direction.
2035 This is a hack, but in the interest of good gameplay...
2036 ======================
2038 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
2043 VectorCopy (ent->fields.server->origin, oldorg);
2046 for (i=0 ; i<8 ; i++)
2048 // try pushing a little in an axial direction
2051 case 0: dir[0] = 2; dir[1] = 0; break;
2052 case 1: dir[0] = 0; dir[1] = 2; break;
2053 case 2: dir[0] = -2; dir[1] = 0; break;
2054 case 3: dir[0] = 0; dir[1] = -2; break;
2055 case 4: dir[0] = 2; dir[1] = 2; break;
2056 case 5: dir[0] = -2; dir[1] = 2; break;
2057 case 6: dir[0] = 2; dir[1] = -2; break;
2058 case 7: dir[0] = -2; dir[1] = -2; break;
2061 SV_PushEntity (&trace, ent, dir, false, true);
2063 // retry the original move
2064 ent->fields.server->velocity[0] = oldvel[0];
2065 ent->fields.server->velocity[1] = oldvel[1];
2066 ent->fields.server->velocity[2] = 0;
2067 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
2069 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
2070 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
2072 Con_DPrint("TryUnstick - success.\n");
2076 // go back to the original pos and try again
2077 VectorCopy (oldorg, ent->fields.server->origin);
2081 VectorClear (ent->fields.server->velocity);
2082 Con_DPrint("TryUnstick - failure.\n");
2088 =====================
2091 Only used by players
2092 ======================
2094 void SV_WalkMove (prvm_edict_t *ent)
2096 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
2097 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
2098 trace_t downtrace, trace;
2099 qboolean applygravity;
2101 // if frametime is 0 (due to client sending the same timestamp twice),
2103 if (sv.frametime <= 0)
2106 SV_CheckStuck (ent);
2108 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
2110 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
2112 SV_CheckVelocity(ent);
2114 // do a regular slide move unless it looks like you ran into a step
2115 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
2117 VectorCopy (ent->fields.server->origin, start_origin);
2118 VectorCopy (ent->fields.server->velocity, start_velocity);
2120 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
2122 // if the move did not hit the ground at any point, we're not on ground
2124 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2126 SV_CheckVelocity(ent);
2128 SV_LinkEdict_TouchAreaGrid(ent);
2130 if(clip & 8) // teleport
2133 if ((int)ent->fields.server->flags & FL_WATERJUMP)
2136 if (sv_nostep.integer)
2139 VectorCopy(ent->fields.server->origin, originalmove_origin);
2140 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
2141 originalmove_clip = clip;
2142 originalmove_flags = (int)ent->fields.server->flags;
2143 originalmove_groundentity = ent->fields.server->groundentity;
2145 // if move didn't block on a step, return
2148 // if move was not trying to move into the step, return
2149 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
2152 if (ent->fields.server->movetype != MOVETYPE_FLY)
2154 // return if gibbed by a trigger
2155 if (ent->fields.server->movetype != MOVETYPE_WALK)
2158 // only step up while jumping if that is enabled
2159 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
2160 if (!oldonground && ent->fields.server->waterlevel == 0)
2164 // try moving up and forward to go up a step
2165 // back to start pos
2166 VectorCopy (start_origin, ent->fields.server->origin);
2167 VectorCopy (start_velocity, ent->fields.server->velocity);
2170 VectorClear (upmove);
2171 upmove[2] = sv_stepheight.value;
2172 if(!SV_PushEntity(&trace, ent, upmove, false, true))
2174 // we got teleported when upstepping... must abort the move
2179 ent->fields.server->velocity[2] = 0;
2180 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
2181 ent->fields.server->velocity[2] += start_velocity[2];
2184 // we got teleported when upstepping... must abort the move
2185 // note that z velocity handling may not be what QC expects here, but we cannot help it
2189 SV_CheckVelocity(ent);
2191 SV_LinkEdict_TouchAreaGrid(ent);
2193 // check for stuckness, possibly due to the limited precision of floats
2194 // in the clipping hulls
2196 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
2197 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
2199 //Con_Printf("wall\n");
2200 // stepping up didn't make any progress, revert to original move
2201 VectorCopy(originalmove_origin, ent->fields.server->origin);
2202 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2203 //clip = originalmove_clip;
2204 ent->fields.server->flags = originalmove_flags;
2205 ent->fields.server->groundentity = originalmove_groundentity;
2206 // now try to unstick if needed
2207 //clip = SV_TryUnstick (ent, oldvel);
2211 //Con_Printf("step - ");
2213 // extra friction based on view angle
2214 if (clip & 2 && sv_wallfriction.integer)
2215 SV_WallFriction (ent, stepnormal);
2217 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
2218 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))
2222 VectorClear (downmove);
2223 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
2224 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
2226 // we got teleported when downstepping... must abort the move
2230 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
2232 // this has been disabled so that you can't jump when you are stepping
2233 // up while already jumping (also known as the Quake2 double jump bug)
2235 // LordHavoc: disabled this check so you can walk on monsters/players
2236 //if (ent->fields.server->solid == SOLID_BSP)
2238 //Con_Printf("onground\n");
2239 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2240 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
2246 //Con_Printf("slope\n");
2247 // if the push down didn't end up on good ground, use the move without
2248 // the step up. This happens near wall / slope combinations, and can
2249 // cause the player to hop up higher on a slope too steep to climb
2250 VectorCopy(originalmove_origin, ent->fields.server->origin);
2251 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
2252 //clip = originalmove_clip;
2253 ent->fields.server->flags = originalmove_flags;
2254 ent->fields.server->groundentity = originalmove_groundentity;
2257 SV_CheckVelocity(ent);
2259 SV_LinkEdict_TouchAreaGrid(ent);
2262 //============================================================================
2268 Entities that are "stuck" to another entity
2271 void SV_Physics_Follow (prvm_edict_t *ent)
2273 vec3_t vf, vr, vu, angles, v;
2277 if (!SV_RunThink (ent))
2280 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
2281 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
2282 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])
2284 // quick case for no rotation
2285 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
2289 angles[0] = -ent->fields.server->punchangle[0];
2290 angles[1] = ent->fields.server->punchangle[1];
2291 angles[2] = ent->fields.server->punchangle[2];
2292 AngleVectors (angles, vf, vr, vu);
2293 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];
2294 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];
2295 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];
2296 angles[0] = -e->fields.server->angles[0];
2297 angles[1] = e->fields.server->angles[1];
2298 angles[2] = e->fields.server->angles[2];
2299 AngleVectors (angles, vf, vr, vu);
2300 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
2301 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
2302 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
2304 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
2306 //SV_LinkEdict_TouchAreaGrid(ent);
2310 ==============================================================================
2314 ==============================================================================
2319 SV_CheckWaterTransition
2323 void SV_CheckWaterTransition (prvm_edict_t *ent)
2326 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
2327 if (!ent->fields.server->watertype)
2329 // just spawned here
2330 ent->fields.server->watertype = cont;
2331 ent->fields.server->waterlevel = 1;
2335 // DRESK - Support for Entity Contents Transition Event
2336 // NOTE: Call here BEFORE updating the watertype below,
2337 // and suppress watersplash sound if a valid function
2338 // call was made to allow for custom "splash" sounds.
2339 if( !SV_CheckContentsTransition(ent, cont) )
2340 { // Contents Transition Function Invalid; Potentially Play Water Sound
2341 // check if the entity crossed into or out of water
2342 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
2343 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
2346 if (cont <= CONTENTS_WATER)
2348 ent->fields.server->watertype = cont;
2349 ent->fields.server->waterlevel = 1;
2353 ent->fields.server->watertype = CONTENTS_EMPTY;
2354 ent->fields.server->waterlevel = 0;
2362 Toss, bounce, and fly movement. When onground, do nothing.
2365 void SV_Physics_Toss (prvm_edict_t *ent)
2372 // if onground, return without moving
2373 if ((int)ent->fields.server->flags & FL_ONGROUND)
2375 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2377 // don't stick to ground if onground and moving upward
2378 ent->fields.server->flags -= FL_ONGROUND;
2380 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2382 // we can trust FL_ONGROUND if groundentity is world because it never moves
2385 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2387 // if ent was supported by a brush model on previous frame,
2388 // and groundentity is now freed, set groundentity to 0 (world)
2389 // which leaves it suspended in the air
2390 ent->fields.server->groundentity = 0;
2391 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2395 ent->priv.server->suspendedinairflag = false;
2397 SV_CheckVelocity (ent);
2400 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2401 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2404 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2406 movetime = sv.frametime;
2407 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2410 VectorScale (ent->fields.server->velocity, movetime, move);
2411 if(!SV_PushEntity (&trace, ent, move, true, true))
2412 return; // teleported
2413 if (ent->priv.server->free)
2415 if (trace.bmodelstartsolid)
2417 // try to unstick the entity
2418 SV_UnstickEntity(ent);
2419 if(!SV_PushEntity (&trace, ent, move, false, true))
2420 return; // teleported
2421 if (ent->priv.server->free)
2424 if (trace.fraction == 1)
2426 movetime *= 1 - min(1, trace.fraction);
2427 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2429 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
2430 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2432 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2434 float d, ent_gravity;
2436 float bouncefactor = 0.5f;
2437 float bouncestop = 60.0f / 800.0f;
2439 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2440 if (val!=0 && val->_float)
2441 bouncefactor = val->_float;
2443 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2444 if (val!=0 && val->_float)
2445 bouncestop = val->_float;
2447 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2448 // LordHavoc: fixed grenades not bouncing when fired down a slope
2449 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2450 if (val!=0 && val->_float)
2451 ent_gravity = val->_float;
2454 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2456 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2457 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2459 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2460 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2461 VectorClear (ent->fields.server->velocity);
2462 VectorClear (ent->fields.server->avelocity);
2465 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2469 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 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;
2482 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2483 if (trace.plane.normal[2] > 0.7)
2485 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2486 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2487 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2488 ent->priv.server->suspendedinairflag = true;
2489 VectorClear (ent->fields.server->velocity);
2490 VectorClear (ent->fields.server->avelocity);
2493 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2495 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2499 // check for in water
2500 SV_CheckWaterTransition (ent);
2504 ===============================================================================
2508 ===============================================================================
2515 Monsters freefall when they don't have a ground entity, otherwise
2516 all movement is done with discrete steps.
2518 This is also used for objects that have become still on the ground, but
2519 will fall if the floor is pulled out from under them.
2522 void SV_Physics_Step (prvm_edict_t *ent)
2524 int flags = (int)ent->fields.server->flags;
2527 // Backup Velocity in the event that movetypesteplandevent is called,
2528 // to provide a parameter with the entity's velocity at impact.
2529 prvm_eval_t *movetypesteplandevent;
2530 vec3_t backupVelocity;
2531 VectorCopy(ent->fields.server->velocity, backupVelocity);
2532 // don't fall at all if fly/swim
2533 if (!(flags & (FL_FLY | FL_SWIM)))
2535 if (flags & FL_ONGROUND)
2537 // freefall if onground and moving upward
2538 // freefall if not standing on a world surface (it may be a lift or trap door)
2539 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2541 ent->fields.server->flags -= FL_ONGROUND;
2542 SV_CheckVelocity(ent);
2543 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2545 SV_LinkEdict_TouchAreaGrid(ent);
2546 ent->priv.server->waterposition_forceupdate = true;
2551 // freefall if not onground
2552 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2554 SV_CheckVelocity(ent);
2555 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2557 SV_LinkEdict_TouchAreaGrid(ent);
2560 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2562 // DRESK - Check for Entity Land Event Function
2563 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2565 if(movetypesteplandevent->function)
2566 { // Valid Function; Execute
2567 // Prepare Parameters
2568 // Assign Velocity at Impact
2569 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2570 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2571 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2573 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2574 // Execute VM Function
2575 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2578 // Check for Engine Landing Sound
2579 if(sv_sound_land.string)
2580 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2582 ent->priv.server->waterposition_forceupdate = true;
2587 if (!SV_RunThink(ent))
2590 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2592 ent->priv.server->waterposition_forceupdate = false;
2593 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2594 SV_CheckWaterTransition(ent);
2598 //============================================================================
2600 static void SV_Physics_Entity (prvm_edict_t *ent)
2602 // don't run think/move on newly spawned projectiles as it messes up
2603 // movement interpolation and rocket trails, and is inconsistent with
2604 // respect to entities spawned in the same frame
2605 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2606 // but if it spawns a lower numbered ent, it doesn't - this never moves
2607 // ents in the first frame regardless)
2608 qboolean runmove = ent->priv.server->move;
2609 ent->priv.server->move = true;
2610 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2612 switch ((int) ent->fields.server->movetype)
2615 case MOVETYPE_FAKEPUSH:
2616 SV_Physics_Pusher (ent);
2619 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2620 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2623 case MOVETYPE_FOLLOW:
2624 SV_Physics_Follow (ent);
2626 case MOVETYPE_NOCLIP:
2627 if (SV_RunThink(ent))
2630 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2631 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2636 SV_Physics_Step (ent);
2639 if (SV_RunThink (ent))
2643 case MOVETYPE_BOUNCE:
2644 case MOVETYPE_BOUNCEMISSILE:
2645 case MOVETYPE_FLYMISSILE:
2648 if (SV_RunThink (ent))
2649 SV_Physics_Toss (ent);
2652 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2657 void SV_Physics_ClientMove(void)
2660 ent = host_client->edict;
2662 // call player physics, this needs the proper frametime
2663 prog->globals.server->frametime = sv.frametime;
2666 // call standard client pre-think, with frametime = 0
2667 prog->globals.server->time = sv.time;
2668 prog->globals.server->frametime = 0;
2669 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2670 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2671 prog->globals.server->frametime = sv.frametime;
2673 // make sure the velocity is sane (not a NaN)
2674 SV_CheckVelocity(ent);
2675 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2676 // player_run/player_stand1 does not horribly malfunction if the
2677 // velocity becomes a number that is both == 0 and != 0
2678 // (sounds to me like NaN but to be absolutely safe...)
2679 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2680 VectorClear(ent->fields.server->velocity);
2682 // perform MOVETYPE_WALK behavior
2685 // call standard player post-think, with frametime = 0
2686 prog->globals.server->time = sv.time;
2687 prog->globals.server->frametime = 0;
2688 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2689 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2690 prog->globals.server->frametime = sv.frametime;
2692 if(ent->fields.server->fixangle)
2694 // angle fixing was requested by physics code...
2695 // so store the current angles for later use
2696 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2697 host_client->fixangle_angles_set = TRUE;
2699 // and clear fixangle for the next frame
2700 ent->fields.server->fixangle = 0;
2704 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2706 // don't do physics on disconnected clients, FrikBot relies on this
2707 if (!host_client->spawned)
2709 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2713 // don't run physics here if running asynchronously
2714 if (host_client->clmovement_inputtimeout <= 0)
2717 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2720 // make sure the velocity is sane (not a NaN)
2721 SV_CheckVelocity(ent);
2722 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2723 // player_run/player_stand1 does not horribly malfunction if the
2724 // velocity becomes a number that is both == 0 and != 0
2725 // (sounds to me like NaN but to be absolutely safe...)
2726 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2727 VectorClear(ent->fields.server->velocity);
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");
2733 SV_CheckVelocity (ent);
2735 switch ((int) ent->fields.server->movetype)
2738 case MOVETYPE_FAKEPUSH:
2739 SV_Physics_Pusher (ent);
2742 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2743 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2746 case MOVETYPE_FOLLOW:
2747 SV_Physics_Follow (ent);
2749 case MOVETYPE_NOCLIP:
2752 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2753 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2756 SV_Physics_Step (ent);
2760 // don't run physics here if running asynchronously
2761 if (host_client->clmovement_inputtimeout <= 0)
2765 case MOVETYPE_BOUNCE:
2766 case MOVETYPE_BOUNCEMISSILE:
2767 case MOVETYPE_FLYMISSILE:
2770 SV_Physics_Toss (ent);
2777 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2781 // decrement the countdown variable used to decide when to go back to
2782 // synchronous physics
2783 if (host_client->clmovement_inputtimeout > sv.frametime)
2784 host_client->clmovement_inputtimeout -= sv.frametime;
2786 host_client->clmovement_inputtimeout = 0;
2788 SV_CheckVelocity (ent);
2791 SV_LinkEdict_TouchAreaGrid(ent);
2793 SV_CheckVelocity (ent);
2795 // call standard player post-think
2796 prog->globals.server->time = sv.time;
2797 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2798 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2800 if(ent->fields.server->fixangle)
2802 // angle fixing was requested by physics code...
2803 // so store the current angles for later use
2804 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2805 host_client->fixangle_angles_set = TRUE;
2807 // and clear fixangle for the next frame
2808 ent->fields.server->fixangle = 0;
2818 void SV_Physics (void)
2823 // let the progs know that a new frame has started
2824 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2825 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2826 prog->globals.server->time = sv.time;
2827 prog->globals.server->frametime = sv.frametime;
2828 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2831 // treat each object in turn
2834 // if force_retouch, relink all the entities
2835 if (prog->globals.server->force_retouch > 0)
2836 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2837 if (!ent->priv.server->free)
2838 SV_LinkEdict_TouchAreaGrid(ent); // force retouch even for stationary
2840 // run physics on the client entities
2841 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2842 if (!ent->priv.server->free)
2843 SV_Physics_ClientEntity(ent);
2845 // run physics on all the non-client entities
2846 if (!sv_freezenonclients.integer)
2848 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2849 if (!ent->priv.server->free)
2850 SV_Physics_Entity(ent);
2851 // make a second pass to see if any ents spawned this frame and make
2852 // sure they run their move/think
2853 if (sv_gameplayfix_delayprojectiles.integer < 0)
2854 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2855 if (!ent->priv.server->move && !ent->priv.server->free)
2856 SV_Physics_Entity(ent);
2859 if (prog->globals.server->force_retouch > 0)
2860 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2862 // LordHavoc: endframe support
2863 if (prog->funcoffsets.EndFrame)
2865 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2866 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2867 prog->globals.server->time = sv.time;
2868 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2871 // decrement prog->num_edicts if the highest number entities died
2872 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2874 if (!sv_freezenonclients.integer)
2875 sv.time += sv.frametime;