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 #if COLLISIONPARANOID >= 1
86 trace_t SV_Move_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
88 trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
91 vec3_t hullmins, hullmaxs;
92 int i, bodysupercontents;
95 prvm_edict_t *traceowner, *touch;
97 // bounding box of entire move area
98 vec3_t clipboxmins, clipboxmaxs;
99 // size of the moving object
100 vec3_t clipmins, clipmaxs;
101 // size when clipping against monsters
102 vec3_t clipmins2, clipmaxs2;
103 // start and end origin of move
104 vec3_t clipstart, clipend;
107 // matrices to transform into/out of other entity's space
108 matrix4x4_t matrix, imatrix;
109 // model of other entity
111 // list of entities to test for collisions
113 prvm_edict_t *touchedicts[MAX_EDICTS];
115 VectorCopy(start, clipstart);
116 VectorCopy(end, clipend);
117 VectorCopy(mins, clipmins);
118 VectorCopy(maxs, clipmaxs);
119 VectorCopy(mins, clipmins2);
120 VectorCopy(maxs, clipmaxs2);
121 #if COLLISIONPARANOID >= 3
122 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
126 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
127 cliptrace.bmodelstartsolid = cliptrace.startsolid;
128 if (cliptrace.startsolid || cliptrace.fraction < 1)
129 cliptrace.ent = prog->edicts;
130 if (type == MOVE_WORLDONLY)
133 if (type == MOVE_MISSILE)
135 // LordHavoc: modified this, was = -15, now -= 15
136 for (i = 0;i < 3;i++)
143 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
144 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
145 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
148 VectorCopy(clipmins, hullmins);
149 VectorCopy(clipmaxs, hullmaxs);
152 // create the bounding box of the entire move
153 for (i = 0;i < 3;i++)
155 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
156 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
159 // debug override to test against everything
160 if (sv_debugmove.integer)
162 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
163 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
166 // if the passedict is world, make it NULL (to avoid two checks each time)
167 if (passedict == prog->edicts)
169 // precalculate prog value for passedict for comparisons
170 passedictprog = PRVM_EDICT_TO_PROG(passedict);
171 // figure out whether this is a point trace for comparisons
172 pointtrace = VectorCompare(clipmins, clipmaxs);
173 // precalculate passedict's owner edict pointer for comparisons
174 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
177 // because this uses World_EntitiestoBox, we know all entity boxes overlap
178 // the clip region, so we can skip culling checks in the loop below
179 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
180 if (numtouchedicts > MAX_EDICTS)
182 // this never happens
183 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
184 numtouchedicts = MAX_EDICTS;
186 for (i = 0;i < numtouchedicts;i++)
188 touch = touchedicts[i];
190 if (touch->fields.server->solid < SOLID_BBOX)
192 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
197 // don't clip against self
198 if (passedict == touch)
200 // don't clip owned entities against owner
201 if (traceowner == touch)
203 // don't clip owner against owned entities
204 if (passedictprog == touch->fields.server->owner)
206 // don't clip points against points (they can't collide)
207 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
211 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
213 // might interact, so do an exact clip
215 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
217 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
218 // if the modelindex is 0, it shouldn't be SOLID_BSP!
219 if (modelindex > 0 && modelindex < MAX_MODELS)
220 model = sv.models[(int)touch->fields.server->modelindex];
223 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);
225 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
226 Matrix4x4_Invert_Simple(&imatrix, &matrix);
227 if ((int)touch->fields.server->flags & FL_MONSTER)
228 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
230 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
232 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
238 #if COLLISIONPARANOID >= 1
239 trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
244 trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
247 VectorCopy(trace.endpos, temp);
248 endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
249 #if COLLISIONPARANOID < 3
250 if (trace.startsolid || endstuck)
252 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" : "");
259 ===============================================================================
261 Linking entities into the world culling system
263 ===============================================================================
266 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
268 int i, numtouchedicts, old_self, old_other;
269 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
271 // build a list of edicts to touch, because the link loop can be corrupted
272 // by IncreaseEdicts called during touch functions
273 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
274 if (numtouchedicts > MAX_EDICTS)
276 // this never happens
277 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
278 numtouchedicts = MAX_EDICTS;
281 old_self = prog->globals.server->self;
282 old_other = prog->globals.server->other;
283 for (i = 0;i < numtouchedicts;i++)
285 touch = touchedicts[i];
286 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
289 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
290 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
291 prog->globals.server->time = sv.time;
292 prog->globals.server->trace_allsolid = false;
293 prog->globals.server->trace_startsolid = false;
294 prog->globals.server->trace_fraction = 1;
295 prog->globals.server->trace_inwater = false;
296 prog->globals.server->trace_inopen = true;
297 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
298 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
299 prog->globals.server->trace_plane_dist = 0;
300 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
301 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
303 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
305 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
307 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
309 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
312 prog->globals.server->self = old_self;
313 prog->globals.server->other = old_other;
322 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
327 if (ent == prog->edicts)
328 return; // don't add the world
330 if (ent->priv.server->free)
335 if (ent->fields.server->solid == SOLID_BSP)
337 int modelindex = (int)ent->fields.server->modelindex;
338 if (modelindex < 0 || modelindex > MAX_MODELS)
340 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
343 model = sv.models[modelindex];
346 if (!model->TraceBox && developer.integer >= 1)
347 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
349 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
351 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
352 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
354 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
356 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
357 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
361 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
362 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
367 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
368 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
369 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
374 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
375 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
379 // to make items easier to pick up and allow them to be grabbed off
380 // of shelves, the abs sizes are expanded
382 if ((int)ent->fields.server->flags & FL_ITEM)
393 // because movement is clipped an epsilon away from an actual edge,
394 // we must fully check even when bounding boxes don't quite touch
403 VectorCopy(mins, ent->fields.server->absmin);
404 VectorCopy(maxs, ent->fields.server->absmax);
406 World_LinkEdict(&sv.world, ent, mins, maxs);
408 // if touch_triggers, call touch on all entities overlapping this box
409 if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
410 SV_LinkEdict_TouchAreaGrid(ent);
414 ===============================================================================
418 ===============================================================================
423 SV_TestEntityPosition
425 returns true if the entity is in solid currently
428 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
432 VectorAdd(ent->fields.server->origin, offset, org);
433 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
434 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
438 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
440 // q1bsp/hlbsp use hulls and if the entity does not exactly match
441 // a hull size it is incorrectly tested, so this code tries to
442 // 'fix' it slightly...
443 // FIXME: this breaks entities larger than the hull size
446 VectorAdd(org, ent->fields.server->mins, m1);
447 VectorAdd(org, ent->fields.server->maxs, m2);
448 VectorSubtract(m2, m1, s);
449 #define EPSILON (1.0f / 32.0f)
450 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
451 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
452 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
453 for (i = 0;i < 8;i++)
455 v[0] = (i & 1) ? m2[0] : m1[0];
456 v[1] = (i & 2) ? m2[1] : m1[1];
457 v[2] = (i & 4) ? m2[2] : m1[2];
458 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
463 // if the trace found a better position for the entity, move it there
464 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
465 VectorCopy(trace.endpos, ent->fields.server->origin);
474 void SV_CheckAllEnts (void)
479 // see if any solid entities are inside the final position
480 check = PRVM_NEXT_EDICT(prog->edicts);
481 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
483 if (check->priv.server->free)
485 if (check->fields.server->movetype == MOVETYPE_PUSH
486 || check->fields.server->movetype == MOVETYPE_NONE
487 || check->fields.server->movetype == MOVETYPE_FOLLOW
488 || check->fields.server->movetype == MOVETYPE_NOCLIP)
491 if (SV_TestEntityPosition (check, vec3_origin))
492 Con_Print("entity in invalid position\n");
496 // DRESK - Support for Entity Contents Transition Event
499 SV_CheckContentsTransition
501 returns true if entity had a valid contentstransition function call
504 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
506 int bValidFunctionCall;
507 prvm_eval_t *contentstransition;
509 // Default Valid Function Call to False
510 bValidFunctionCall = false;
512 if(ent->fields.server->watertype != nContents)
513 { // Changed Contents
514 // Acquire Contents Transition Function from QC
515 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
517 if(contentstransition->function)
518 { // Valid Function; Execute
519 // Assign Valid Function
520 bValidFunctionCall = true;
521 // Prepare Parameters (Original Contents, New Contents)
523 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
525 PRVM_G_FLOAT(OFS_PARM1) = nContents;
527 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
528 // Execute VM Function
529 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
533 // Return if Function Call was Valid
534 return bValidFunctionCall;
543 void SV_CheckVelocity (prvm_edict_t *ent)
551 for (i=0 ; i<3 ; i++)
553 if (IS_NAN(ent->fields.server->velocity[i]))
555 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
556 ent->fields.server->velocity[i] = 0;
558 if (IS_NAN(ent->fields.server->origin[i]))
560 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
561 ent->fields.server->origin[i] = 0;
565 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
566 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
567 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
569 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
570 ent->fields.server->velocity[0] *= wishspeed;
571 ent->fields.server->velocity[1] *= wishspeed;
572 ent->fields.server->velocity[2] *= wishspeed;
580 Runs thinking code if time. There is some play in the exact time the think
581 function will be called, because it is called before any movement is done
582 in a frame. Not used for pushmove objects, because they must be exact.
583 Returns false if the entity removed itself.
586 qboolean SV_RunThink (prvm_edict_t *ent)
590 // don't let things stay in the past.
591 // it is possible to start that way by a trigger with a local time.
592 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
595 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
597 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
598 ent->fields.server->nextthink = 0;
599 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
600 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
601 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
602 // mods often set nextthink to time to cause a think every frame,
603 // we don't want to loop in that case, so exit if the new nextthink is
604 // <= the time the qc was told, also exit if it is past the end of the
606 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
609 return !ent->priv.server->free;
616 Two entities have touched, so run their touch functions
619 extern void VM_SetTraceGlobals(const trace_t *trace);
620 extern sizebuf_t vm_tempstringsbuf;
621 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
623 int restorevm_tempstringsbuf_cursize;
624 int old_self, old_other;
625 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
628 old_self = prog->globals.server->self;
629 old_other = prog->globals.server->other;
630 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
632 VM_SetTraceGlobals(trace);
634 prog->globals.server->time = sv.time;
635 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
637 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
638 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
639 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
642 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
644 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
645 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
646 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
647 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
648 prog->globals.server->trace_plane_dist = -trace->plane.dist;
649 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
650 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
652 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
654 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
656 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
658 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
661 prog->globals.server->self = old_self;
662 prog->globals.server->other = old_other;
663 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
671 Slide off of the impacting object
672 returns the blocked flags (1 = floor, 2 = step / wall)
675 #define STOP_EPSILON 0.1
676 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
681 backoff = -DotProduct (in, normal) * overbounce;
682 VectorMA(in, backoff, normal, out);
684 for (i = 0;i < 3;i++)
685 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
694 The basic solid body movement clip that slides along multiple planes
695 Returns the clipflags if the velocity was modified (hit something solid)
699 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
702 // LordHavoc: increased from 5 to 32
703 #define MAX_CLIP_PLANES 32
704 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
706 int blocked, bumpcount;
707 int i, j, impact, numplanes;
709 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
714 VectorCopy(ent->fields.server->velocity, original_velocity);
715 VectorCopy(ent->fields.server->velocity, primal_velocity);
718 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
720 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
723 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
724 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
726 //if (trace.fraction < 0.002)
731 VectorCopy(ent->fields.server->origin, start);
732 start[2] += 3;//0.03125;
733 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
734 end[2] += 3;//0.03125;
735 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
736 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)))
738 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
744 for (i = 0;i < numplanes;i++)
746 VectorCopy(ent->fields.server->origin, start);
747 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
748 VectorMA(start, 3, planes[i], start);
749 VectorMA(end, 3, planes[i], end);
750 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
751 if (trace.fraction < testtrace.fraction)
754 VectorCopy(start, ent->fields.server->origin);
759 // VectorAdd(ent->fields.server->origin, planes[j], start);
765 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);
766 if (trace.fraction < 1)
767 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
772 if (trace.bmodelstartsolid)
774 // LordHavoc: note: this code is what makes entities stick in place
775 // if embedded in world only (you can walk through other objects if
777 // entity is trapped in another solid
778 VectorClear(ent->fields.server->velocity);
783 // break if it moved the entire distance
784 if (trace.fraction == 1)
786 VectorCopy(trace.endpos, ent->fields.server->origin);
792 Con_Printf ("SV_FlyMove: !trace.ent");
793 trace.ent = prog->edicts;
796 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
798 if (trace.plane.normal[2])
800 if (trace.plane.normal[2] > 0.7)
804 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
805 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
812 // save the trace for player extrafriction
814 VectorCopy(trace.plane.normal, stepnormal);
817 if (trace.fraction >= 0.001)
819 // actually covered some distance
820 VectorCopy(trace.endpos, ent->fields.server->origin);
821 VectorCopy(ent->fields.server->velocity, original_velocity);
825 // run the impact function
828 SV_Impact(ent, &trace);
830 // break if removed by the impact function
831 if (ent->priv.server->free)
835 time_left *= 1 - trace.fraction;
837 // clipped to another plane
838 if (numplanes >= MAX_CLIP_PLANES)
840 // this shouldn't really happen
841 VectorClear(ent->fields.server->velocity);
847 for (i = 0;i < numplanes;i++)
848 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
852 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
857 VectorCopy(trace.plane.normal, planes[numplanes]);
860 if (sv_newflymove.integer)
861 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
864 // modify original_velocity so it parallels all of the clip planes
865 for (i = 0;i < numplanes;i++)
867 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
868 for (j = 0;j < numplanes;j++)
873 if (DotProduct(new_velocity, planes[j]) < 0)
883 // go along this plane
884 VectorCopy(new_velocity, ent->fields.server->velocity);
888 // go along the crease
891 VectorClear(ent->fields.server->velocity);
895 CrossProduct(planes[0], planes[1], dir);
896 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
897 VectorNormalize(dir);
898 d = DotProduct(dir, ent->fields.server->velocity);
899 VectorScale(dir, d, ent->fields.server->velocity);
903 // if current velocity is against the original velocity,
904 // stop dead to avoid tiny occilations in sloping corners
905 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
907 VectorClear(ent->fields.server->velocity);
912 //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]);
915 if ((blocked & 1) == 0 && bumpcount > 1)
917 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
918 // flag ONGROUND if there's ground under it
919 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
923 // LordHavoc: this came from QW and allows you to get out of water more easily
924 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
925 VectorCopy(primal_velocity, ent->fields.server->velocity);
935 void SV_AddGravity (prvm_edict_t *ent)
940 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
941 if (val!=0 && val->_float)
942 ent_gravity = val->_float;
945 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
950 ===============================================================================
954 ===============================================================================
961 Does not change the entities velocity at all
964 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
970 VectorAdd (ent->fields.server->origin, push, end);
972 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
974 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
975 type = MOVE_NOMONSTERS; // only clip against bmodels
979 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
980 if (trace.bmodelstartsolid && failonbmodelstartsolid)
983 VectorCopy (trace.endpos, ent->fields.server->origin);
984 SV_LinkEdict (ent, true);
986 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)))
987 SV_Impact (ent, &trace);
998 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1001 float savesolid, movetime2, pushltime;
1002 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1004 int numcheckentities;
1005 static prvm_edict_t *checkentities[MAX_EDICTS];
1006 model_t *pushermodel;
1008 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1009 unsigned short moved_edicts[MAX_EDICTS];
1011 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])
1013 pusher->fields.server->ltime += movetime;
1017 switch ((int) pusher->fields.server->solid)
1019 // LordHavoc: valid pusher types
1022 case SOLID_SLIDEBOX:
1023 case SOLID_CORPSE: // LordHavoc: this would be weird...
1025 // LordHavoc: no collisions
1028 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1029 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1030 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1031 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1032 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1033 pusher->fields.server->ltime += movetime;
1034 SV_LinkEdict (pusher, false);
1037 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1040 index = (int) pusher->fields.server->modelindex;
1041 if (index < 1 || index >= MAX_MODELS)
1043 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1046 pushermodel = sv.models[index];
1048 movetime2 = movetime;
1049 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1050 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1051 if (moveangle[0] || moveangle[2])
1053 for (i = 0;i < 3;i++)
1057 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1058 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1062 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1063 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1067 else if (moveangle[1])
1069 for (i = 0;i < 3;i++)
1073 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1074 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1078 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1079 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1085 for (i = 0;i < 3;i++)
1089 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1090 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1094 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1095 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1100 VectorNegate (moveangle, a);
1101 AngleVectorsFLU (a, forward, left, up);
1103 VectorCopy (pusher->fields.server->origin, pushorig);
1104 VectorCopy (pusher->fields.server->angles, pushang);
1105 pushltime = pusher->fields.server->ltime;
1107 // move the pusher to its final position
1109 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1110 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1111 pusher->fields.server->ltime += movetime;
1112 SV_LinkEdict (pusher, false);
1115 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1116 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1117 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);
1118 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1120 savesolid = pusher->fields.server->solid;
1122 // see if any solid entities are inside the final position
1125 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1126 for (e = 0;e < numcheckentities;e++)
1128 prvm_edict_t *check = checkentities[e];
1129 int checkcontents = SV_GenericHitSuperContentsMask(check);
1130 if (check->fields.server->movetype == MOVETYPE_NONE
1131 || check->fields.server->movetype == MOVETYPE_PUSH
1132 || check->fields.server->movetype == MOVETYPE_FOLLOW
1133 || check->fields.server->movetype == MOVETYPE_NOCLIP
1134 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1137 // if the entity is standing on the pusher, it will definitely be moved
1138 // if the entity is not standing on the pusher, but is in the pusher's
1139 // final position, move it
1140 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1142 Collision_ClipToGenericEntity(&trace, pushermodel, 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);
1143 if (!trace.startsolid)
1148 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1151 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1152 org2[0] = DotProduct (org, forward);
1153 org2[1] = DotProduct (org, left);
1154 org2[2] = DotProduct (org, up);
1155 VectorSubtract (org2, org, move);
1156 VectorAdd (move, move1, move);
1159 VectorCopy (move1, move);
1161 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1162 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1163 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1165 // try moving the contacted entity
1166 pusher->fields.server->solid = SOLID_NOT;
1167 trace = SV_PushEntity (check, move, true);
1168 // FIXME: turn players specially
1169 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1170 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1171 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1173 // this trace.fraction < 1 check causes items to fall off of pushers
1174 // if they pass under or through a wall
1175 // the groundentity check causes items to fall off of ledges
1176 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1177 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1179 // if it is still inside the pusher, block
1180 Collision_ClipToGenericEntity(&trace, pushermodel, 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);
1181 if (trace.startsolid)
1183 // try moving the contacted entity a tiny bit further to account for precision errors
1185 pusher->fields.server->solid = SOLID_NOT;
1186 VectorScale(move, 1.1, move2);
1187 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1188 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1189 SV_PushEntity (check, move2, true);
1190 pusher->fields.server->solid = savesolid;
1191 Collision_ClipToGenericEntity(&trace, pushermodel, 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);
1192 if (trace.startsolid)
1194 // try moving the contacted entity a tiny bit less to account for precision errors
1195 pusher->fields.server->solid = SOLID_NOT;
1196 VectorScale(move, 0.9, move2);
1197 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1198 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1199 SV_PushEntity (check, move2, true);
1200 pusher->fields.server->solid = savesolid;
1201 Collision_ClipToGenericEntity(&trace, pushermodel, 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);
1202 if (trace.startsolid)
1204 // still inside pusher, so it's really blocked
1207 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1209 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1212 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1213 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1217 VectorCopy (pushorig, pusher->fields.server->origin);
1218 VectorCopy (pushang, pusher->fields.server->angles);
1219 pusher->fields.server->ltime = pushltime;
1220 SV_LinkEdict (pusher, false);
1222 // move back any entities we already moved
1223 for (i = 0;i < num_moved;i++)
1225 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1226 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1227 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1228 SV_LinkEdict (ed, false);
1231 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1232 if (pusher->fields.server->blocked)
1234 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1235 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1236 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1243 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1244 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1245 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1254 void SV_Physics_Pusher (prvm_edict_t *ent)
1256 float thinktime, oldltime, movetime;
1258 oldltime = ent->fields.server->ltime;
1260 thinktime = ent->fields.server->nextthink;
1261 if (thinktime < ent->fields.server->ltime + sv.frametime)
1263 movetime = thinktime - ent->fields.server->ltime;
1268 movetime = sv.frametime;
1271 // advances ent->fields.server->ltime if not blocked
1272 SV_PushMove (ent, movetime);
1274 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1276 ent->fields.server->nextthink = 0;
1277 prog->globals.server->time = sv.time;
1278 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1279 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1280 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1286 ===============================================================================
1290 ===============================================================================
1293 static float unstickoffsets[] =
1327 This is a big hack to try and fix the rare case of getting stuck in the world
1331 void SV_CheckStuck (prvm_edict_t *ent)
1336 if (!SV_TestEntityPosition(ent, vec3_origin))
1338 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1342 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1344 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1346 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), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
1347 SV_LinkEdict (ent, true);
1352 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1353 if (!SV_TestEntityPosition(ent, offset))
1355 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1356 SV_LinkEdict (ent, true);
1360 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1363 static void SV_UnstickEntity (prvm_edict_t *ent)
1367 // if not stuck in a bmodel, just return
1368 if (!SV_TestEntityPosition(ent, vec3_origin))
1371 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1373 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1375 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), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
1376 SV_LinkEdict (ent, true);
1381 if (developer.integer >= 100)
1382 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1391 qboolean SV_CheckWater (prvm_edict_t *ent)
1394 int nNativeContents;
1397 point[0] = ent->fields.server->origin[0];
1398 point[1] = ent->fields.server->origin[1];
1399 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1401 // DRESK - Support for Entity Contents Transition Event
1402 // NOTE: Some logic needed to be slightly re-ordered
1403 // to not affect performance and allow for the feature.
1405 // Acquire Super Contents Prior to Resets
1406 cont = SV_PointSuperContents(point);
1407 // Acquire Native Contents Here
1408 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1410 // DRESK - Support for Entity Contents Transition Event
1411 if(ent->fields.server->watertype)
1412 // Entity did NOT Spawn; Check
1413 SV_CheckContentsTransition(ent, nNativeContents);
1416 ent->fields.server->waterlevel = 0;
1417 ent->fields.server->watertype = CONTENTS_EMPTY;
1418 cont = SV_PointSuperContents(point);
1419 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1421 ent->fields.server->watertype = nNativeContents;
1422 ent->fields.server->waterlevel = 1;
1423 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1424 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1426 ent->fields.server->waterlevel = 2;
1427 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1428 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1429 ent->fields.server->waterlevel = 3;
1433 return ent->fields.server->waterlevel > 1;
1442 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1445 vec3_t forward, into, side;
1447 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1448 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1450 // cut the tangential velocity
1451 i = DotProduct (stepnormal, ent->fields.server->velocity);
1452 VectorScale (stepnormal, i, into);
1453 VectorSubtract (ent->fields.server->velocity, into, side);
1454 ent->fields.server->velocity[0] = side[0] * (1 + d);
1455 ent->fields.server->velocity[1] = side[1] * (1 + d);
1461 =====================
1464 Player has come to a dead stop, possibly due to the problem with limited
1465 float precision at some angle joins in the BSP hull.
1467 Try fixing by pushing one pixel in each direction.
1469 This is a hack, but in the interest of good gameplay...
1470 ======================
1472 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1477 VectorCopy (ent->fields.server->origin, oldorg);
1480 for (i=0 ; i<8 ; i++)
1482 // try pushing a little in an axial direction
1485 case 0: dir[0] = 2; dir[1] = 0; break;
1486 case 1: dir[0] = 0; dir[1] = 2; break;
1487 case 2: dir[0] = -2; dir[1] = 0; break;
1488 case 3: dir[0] = 0; dir[1] = -2; break;
1489 case 4: dir[0] = 2; dir[1] = 2; break;
1490 case 5: dir[0] = -2; dir[1] = 2; break;
1491 case 6: dir[0] = 2; dir[1] = -2; break;
1492 case 7: dir[0] = -2; dir[1] = -2; break;
1495 SV_PushEntity (ent, dir, false);
1497 // retry the original move
1498 ent->fields.server->velocity[0] = oldvel[0];
1499 ent->fields.server->velocity[1] = oldvel[1];
1500 ent->fields.server->velocity[2] = 0;
1501 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1503 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1504 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1506 Con_DPrint("TryUnstick - success.\n");
1510 // go back to the original pos and try again
1511 VectorCopy (oldorg, ent->fields.server->origin);
1515 VectorClear (ent->fields.server->velocity);
1516 Con_DPrint("TryUnstick - failure.\n");
1522 =====================
1525 Only used by players
1526 ======================
1528 void SV_WalkMove (prvm_edict_t *ent)
1530 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1531 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1534 // if frametime is 0 (due to client sending the same timestamp twice),
1536 if (sv.frametime <= 0)
1539 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1541 SV_CheckVelocity(ent);
1543 // do a regular slide move unless it looks like you ran into a step
1544 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1546 VectorCopy (ent->fields.server->origin, start_origin);
1547 VectorCopy (ent->fields.server->velocity, start_velocity);
1549 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1551 // if the move did not hit the ground at any point, we're not on ground
1553 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1555 SV_CheckVelocity(ent);
1557 VectorCopy(ent->fields.server->origin, originalmove_origin);
1558 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1559 originalmove_clip = clip;
1560 originalmove_flags = (int)ent->fields.server->flags;
1561 originalmove_groundentity = ent->fields.server->groundentity;
1563 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1566 if (sv_nostep.integer)
1569 // if move didn't block on a step, return
1572 // if move was not trying to move into the step, return
1573 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1576 if (ent->fields.server->movetype != MOVETYPE_FLY)
1578 // return if gibbed by a trigger
1579 if (ent->fields.server->movetype != MOVETYPE_WALK)
1582 // only step up while jumping if that is enabled
1583 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1584 if (!oldonground && ent->fields.server->waterlevel == 0)
1588 // try moving up and forward to go up a step
1589 // back to start pos
1590 VectorCopy (start_origin, ent->fields.server->origin);
1591 VectorCopy (start_velocity, ent->fields.server->velocity);
1594 VectorClear (upmove);
1595 upmove[2] = sv_stepheight.value;
1596 // FIXME: don't link?
1597 SV_PushEntity(ent, upmove, false);
1600 ent->fields.server->velocity[2] = 0;
1601 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1602 ent->fields.server->velocity[2] += start_velocity[2];
1604 SV_CheckVelocity(ent);
1606 // check for stuckness, possibly due to the limited precision of floats
1607 // in the clipping hulls
1609 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1610 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1612 //Con_Printf("wall\n");
1613 // stepping up didn't make any progress, revert to original move
1614 VectorCopy(originalmove_origin, ent->fields.server->origin);
1615 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1616 //clip = originalmove_clip;
1617 ent->fields.server->flags = originalmove_flags;
1618 ent->fields.server->groundentity = originalmove_groundentity;
1619 // now try to unstick if needed
1620 //clip = SV_TryUnstick (ent, oldvel);
1624 //Con_Printf("step - ");
1626 // extra friction based on view angle
1627 if (clip & 2 && sv_wallfriction.integer)
1628 SV_WallFriction (ent, stepnormal);
1630 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1631 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))
1635 VectorClear (downmove);
1636 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1637 // FIXME: don't link?
1638 downtrace = SV_PushEntity (ent, downmove, false);
1640 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1642 // this has been disabled so that you can't jump when you are stepping
1643 // up while already jumping (also known as the Quake2 double jump bug)
1645 // LordHavoc: disabled this check so you can walk on monsters/players
1646 //if (ent->fields.server->solid == SOLID_BSP)
1648 //Con_Printf("onground\n");
1649 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1650 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1656 //Con_Printf("slope\n");
1657 // if the push down didn't end up on good ground, use the move without
1658 // the step up. This happens near wall / slope combinations, and can
1659 // cause the player to hop up higher on a slope too steep to climb
1660 VectorCopy(originalmove_origin, ent->fields.server->origin);
1661 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1662 //clip = originalmove_clip;
1663 ent->fields.server->flags = originalmove_flags;
1664 ent->fields.server->groundentity = originalmove_groundentity;
1667 SV_CheckVelocity(ent);
1670 //============================================================================
1676 Entities that are "stuck" to another entity
1679 void SV_Physics_Follow (prvm_edict_t *ent)
1681 vec3_t vf, vr, vu, angles, v;
1685 if (!SV_RunThink (ent))
1688 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1689 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1690 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])
1692 // quick case for no rotation
1693 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1697 angles[0] = -ent->fields.server->punchangle[0];
1698 angles[1] = ent->fields.server->punchangle[1];
1699 angles[2] = ent->fields.server->punchangle[2];
1700 AngleVectors (angles, vf, vr, vu);
1701 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];
1702 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];
1703 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];
1704 angles[0] = -e->fields.server->angles[0];
1705 angles[1] = e->fields.server->angles[1];
1706 angles[2] = e->fields.server->angles[2];
1707 AngleVectors (angles, vf, vr, vu);
1708 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1709 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1710 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1712 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1713 SV_LinkEdict (ent, true);
1717 ==============================================================================
1721 ==============================================================================
1726 SV_CheckWaterTransition
1730 void SV_CheckWaterTransition (prvm_edict_t *ent)
1733 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1734 if (!ent->fields.server->watertype)
1736 // just spawned here
1737 ent->fields.server->watertype = cont;
1738 ent->fields.server->waterlevel = 1;
1742 // DRESK - Support for Entity Contents Transition Event
1743 // NOTE: Call here BEFORE updating the watertype below,
1744 // and suppress watersplash sound if a valid function
1745 // call was made to allow for custom "splash" sounds.
1746 if( !SV_CheckContentsTransition(ent, cont) )
1747 { // Contents Transition Function Invalid; Potentially Play Water Sound
1748 // check if the entity crossed into or out of water
1749 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1750 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1753 if (cont <= CONTENTS_WATER)
1755 ent->fields.server->watertype = cont;
1756 ent->fields.server->waterlevel = 1;
1760 ent->fields.server->watertype = CONTENTS_EMPTY;
1761 ent->fields.server->waterlevel = 0;
1769 Toss, bounce, and fly movement. When onground, do nothing.
1772 void SV_Physics_Toss (prvm_edict_t *ent)
1777 // if onground, return without moving
1778 if ((int)ent->fields.server->flags & FL_ONGROUND)
1780 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1782 // don't stick to ground if onground and moving upward
1783 ent->fields.server->flags -= FL_ONGROUND;
1785 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1787 // we can trust FL_ONGROUND if groundentity is world because it never moves
1790 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1792 // if ent was supported by a brush model on previous frame,
1793 // and groundentity is now freed, set groundentity to 0 (world)
1794 // which leaves it suspended in the air
1795 ent->fields.server->groundentity = 0;
1799 ent->priv.server->suspendedinairflag = false;
1801 SV_CheckVelocity (ent);
1804 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1805 SV_AddGravity (ent);
1808 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1811 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1812 trace = SV_PushEntity (ent, move, true);
1813 if (ent->priv.server->free)
1815 if (trace.bmodelstartsolid)
1817 // try to unstick the entity
1818 SV_UnstickEntity(ent);
1819 trace = SV_PushEntity (ent, move, false);
1820 if (ent->priv.server->free)
1824 if (trace.fraction < 1)
1826 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1828 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1829 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1831 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1834 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1835 // LordHavoc: fixed grenades not bouncing when fired down a slope
1836 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1838 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1839 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1841 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1842 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1843 VectorClear (ent->fields.server->velocity);
1844 VectorClear (ent->fields.server->avelocity);
1847 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1851 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1853 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1854 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1855 VectorClear (ent->fields.server->velocity);
1856 VectorClear (ent->fields.server->avelocity);
1859 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1864 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1865 if (trace.plane.normal[2] > 0.7)
1867 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1868 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1869 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1870 ent->priv.server->suspendedinairflag = true;
1871 VectorClear (ent->fields.server->velocity);
1872 VectorClear (ent->fields.server->avelocity);
1875 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1879 // check for in water
1880 SV_CheckWaterTransition (ent);
1884 ===============================================================================
1888 ===============================================================================
1895 Monsters freefall when they don't have a ground entity, otherwise
1896 all movement is done with discrete steps.
1898 This is also used for objects that have become still on the ground, but
1899 will fall if the floor is pulled out from under them.
1902 void SV_Physics_Step (prvm_edict_t *ent)
1904 int flags = (int)ent->fields.server->flags;
1905 // don't fall at all if fly/swim
1906 if (!(flags & (FL_FLY | FL_SWIM)))
1908 if (flags & FL_ONGROUND)
1910 // freefall if onground and moving upward
1911 // freefall if not standing on a world surface (it may be a lift or trap door)
1912 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1914 ent->fields.server->flags -= FL_ONGROUND;
1916 SV_CheckVelocity(ent);
1917 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1918 SV_LinkEdict(ent, true);
1923 // freefall if not onground
1924 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1927 SV_CheckVelocity(ent);
1928 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1929 SV_LinkEdict(ent, true);
1932 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1933 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1940 SV_CheckWaterTransition(ent);
1943 //============================================================================
1945 static void SV_Physics_Entity (prvm_edict_t *ent)
1947 // don't run a move on newly spawned projectiles as it messes up movement
1948 // interpolation and rocket trails
1949 qboolean runmove = ent->priv.server->move;
1950 ent->priv.server->move = true;
1951 switch ((int) ent->fields.server->movetype)
1954 case MOVETYPE_FAKEPUSH:
1955 SV_Physics_Pusher (ent);
1958 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1959 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1962 case MOVETYPE_FOLLOW:
1963 SV_Physics_Follow (ent);
1965 case MOVETYPE_NOCLIP:
1966 if (SV_RunThink(ent))
1969 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1970 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1972 SV_LinkEdict(ent, false);
1975 SV_Physics_Step (ent);
1978 if (SV_RunThink (ent))
1980 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1981 SV_AddGravity (ent);
1982 SV_CheckStuck (ent);
1984 SV_LinkEdict (ent, true);
1988 case MOVETYPE_BOUNCE:
1989 case MOVETYPE_BOUNCEMISSILE:
1990 case MOVETYPE_FLYMISSILE:
1993 if (SV_RunThink (ent) && (runmove || !sv_gameplayfix_delayprojectiles.integer))
1994 SV_Physics_Toss (ent);
1997 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2002 void SV_Physics_ClientMove(void)
2005 ent = host_client->edict;
2007 // call player physics, this needs the proper frametime
2008 prog->globals.server->frametime = sv.frametime;
2011 // call standard client pre-think, with frametime = 0
2012 prog->globals.server->time = sv.time;
2013 prog->globals.server->frametime = 0;
2014 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2015 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2016 prog->globals.server->frametime = sv.frametime;
2018 // make sure the velocity is sane (not a NaN)
2019 SV_CheckVelocity(ent);
2020 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2021 // player_run/player_stand1 does not horribly malfunction if the
2022 // velocity becomes a number that is both == 0 and != 0
2023 // (sounds to me like NaN but to be absolutely safe...)
2024 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2025 VectorClear(ent->fields.server->velocity);
2027 // perform MOVETYPE_WALK behavior
2028 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2029 SV_AddGravity (ent);
2030 SV_CheckStuck (ent);
2033 SV_CheckVelocity (ent);
2035 SV_LinkEdict (ent, true);
2037 SV_CheckVelocity (ent);
2039 // call standard player post-think, with frametime = 0
2040 prog->globals.server->time = sv.time;
2041 prog->globals.server->frametime = 0;
2042 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2043 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2044 prog->globals.server->frametime = sv.frametime;
2046 if(ent->fields.server->fixangle)
2048 // angle fixing was requested by physics code...
2049 // so store the current angles for later use
2050 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2051 host_client->fixangle_angles_set = TRUE;
2053 // and clear fixangle for the next frame
2054 ent->fields.server->fixangle = 0;
2058 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2060 // don't do physics on disconnected clients, FrikBot relies on this
2061 if (!host_client->spawned)
2063 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2067 // don't run physics here if running asynchronously
2068 if (host_client->clmovement_skipphysicsframes <= 0)
2071 // make sure the velocity is sane (not a NaN)
2072 SV_CheckVelocity(ent);
2073 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2074 // player_run/player_stand1 does not horribly malfunction if the
2075 // velocity becomes a number that is both == 0 and != 0
2076 // (sounds to me like NaN but to be absolutely safe...)
2077 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2078 VectorClear(ent->fields.server->velocity);
2080 // call standard client pre-think
2081 prog->globals.server->time = sv.time;
2082 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2083 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2084 SV_CheckVelocity (ent);
2086 switch ((int) ent->fields.server->movetype)
2089 case MOVETYPE_FAKEPUSH:
2090 SV_Physics_Pusher (ent);
2093 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2094 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2097 case MOVETYPE_FOLLOW:
2098 SV_Physics_Follow (ent);
2100 case MOVETYPE_NOCLIP:
2103 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2104 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2107 SV_Physics_Step (ent);
2111 // don't run physics here if running asynchronously
2112 if (host_client->clmovement_skipphysicsframes <= 0)
2114 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2115 SV_AddGravity (ent);
2116 SV_CheckStuck (ent);
2118 host_client->cmd.time = max(host_client->cmd.time, sv.time); // ignore client movement data for anything before NOW
2122 case MOVETYPE_BOUNCE:
2123 case MOVETYPE_BOUNCEMISSILE:
2124 case MOVETYPE_FLYMISSILE:
2127 SV_Physics_Toss (ent);
2131 SV_CheckWater (ent);
2135 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2139 // decrement the countdown variable used to decide when to go back to
2140 // synchronous physics
2141 if (host_client->clmovement_skipphysicsframes > 0)
2142 host_client->clmovement_skipphysicsframes--;
2144 SV_CheckVelocity (ent);
2146 SV_LinkEdict (ent, true);
2148 SV_CheckVelocity (ent);
2150 // call standard player post-think
2151 prog->globals.server->time = sv.time;
2152 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2153 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2155 if(ent->fields.server->fixangle)
2157 // angle fixing was requested by physics code...
2158 // so store the current angles for later use
2159 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2160 host_client->fixangle_angles_set = TRUE;
2162 // and clear fixangle for the next frame
2163 ent->fields.server->fixangle = 0;
2173 void SV_Physics (void)
2178 // let the progs know that a new frame has started
2179 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2180 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2181 prog->globals.server->time = sv.time;
2182 prog->globals.server->frametime = sv.frametime;
2183 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2186 // treat each object in turn
2189 // if force_retouch, relink all the entities
2190 if (prog->globals.server->force_retouch > 0)
2191 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2192 if (!ent->priv.server->free)
2193 SV_LinkEdict (ent, true); // force retouch even for stationary
2195 // run physics on the client entities
2196 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2197 if (!ent->priv.server->free)
2198 SV_Physics_ClientEntity(ent);
2200 // run physics on all the non-client entities
2201 if (!sv_freezenonclients.integer)
2202 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2203 if (!ent->priv.server->free)
2204 SV_Physics_Entity(ent);
2206 if (prog->globals.server->force_retouch > 0)
2207 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2209 // LordHavoc: endframe support
2210 if (prog->funcoffsets.EndFrame)
2212 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2213 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2214 prog->globals.server->time = sv.time;
2215 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2218 // decrement prog->num_edicts if the highest number entities died
2219 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2221 if (!sv_freezenonclients.integer)
2222 sv.time += sv.frametime;