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)
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;
526 // Execute VM Function
527 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
531 // Return if Function Call was Valid
532 return bValidFunctionCall;
541 void SV_CheckVelocity (prvm_edict_t *ent)
549 for (i=0 ; i<3 ; i++)
551 if (IS_NAN(ent->fields.server->velocity[i]))
553 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
554 ent->fields.server->velocity[i] = 0;
556 if (IS_NAN(ent->fields.server->origin[i]))
558 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
559 ent->fields.server->origin[i] = 0;
563 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
564 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
565 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
567 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
568 ent->fields.server->velocity[0] *= wishspeed;
569 ent->fields.server->velocity[1] *= wishspeed;
570 ent->fields.server->velocity[2] *= wishspeed;
578 Runs thinking code if time. There is some play in the exact time the think
579 function will be called, because it is called before any movement is done
580 in a frame. Not used for pushmove objects, because they must be exact.
581 Returns false if the entity removed itself.
584 qboolean SV_RunThink (prvm_edict_t *ent)
588 // don't let things stay in the past.
589 // it is possible to start that way by a trigger with a local time.
590 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
593 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
595 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
596 ent->fields.server->nextthink = 0;
597 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
598 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
599 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
600 // mods often set nextthink to time to cause a think every frame,
601 // we don't want to loop in that case, so exit if the new nextthink is
602 // <= the time the qc was told, also exit if it is past the end of the
604 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
607 return !ent->priv.server->free;
614 Two entities have touched, so run their touch functions
617 extern void VM_SetTraceGlobals(const trace_t *trace);
618 extern sizebuf_t vm_tempstringsbuf;
619 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
621 int restorevm_tempstringsbuf_cursize;
622 int old_self, old_other;
623 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
626 old_self = prog->globals.server->self;
627 old_other = prog->globals.server->other;
628 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
630 VM_SetTraceGlobals(trace);
632 prog->globals.server->time = sv.time;
633 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
635 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
636 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
637 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
640 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
642 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
643 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
644 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
645 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
646 prog->globals.server->trace_plane_dist = -trace->plane.dist;
647 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
648 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
650 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
652 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
654 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
656 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
659 prog->globals.server->self = old_self;
660 prog->globals.server->other = old_other;
661 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
669 Slide off of the impacting object
670 returns the blocked flags (1 = floor, 2 = step / wall)
673 #define STOP_EPSILON 0.1
674 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
679 backoff = -DotProduct (in, normal) * overbounce;
680 VectorMA(in, backoff, normal, out);
682 for (i = 0;i < 3;i++)
683 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
692 The basic solid body movement clip that slides along multiple planes
693 Returns the clipflags if the velocity was modified (hit something solid)
697 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
700 // LordHavoc: increased from 5 to 32
701 #define MAX_CLIP_PLANES 32
702 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
704 int blocked, bumpcount;
705 int i, j, impact, numplanes;
707 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
712 VectorCopy(ent->fields.server->velocity, original_velocity);
713 VectorCopy(ent->fields.server->velocity, primal_velocity);
716 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
718 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
721 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
722 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
724 //if (trace.fraction < 0.002)
729 VectorCopy(ent->fields.server->origin, start);
730 start[2] += 3;//0.03125;
731 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
732 end[2] += 3;//0.03125;
733 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
734 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)))
736 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
742 for (i = 0;i < numplanes;i++)
744 VectorCopy(ent->fields.server->origin, start);
745 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
746 VectorMA(start, 3, planes[i], start);
747 VectorMA(end, 3, planes[i], end);
748 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
749 if (trace.fraction < testtrace.fraction)
752 VectorCopy(start, ent->fields.server->origin);
757 // VectorAdd(ent->fields.server->origin, planes[j], start);
763 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);
764 if (trace.fraction < 1)
765 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
770 if (trace.bmodelstartsolid)
772 // LordHavoc: note: this code is what makes entities stick in place
773 // if embedded in world only (you can walk through other objects if
775 // entity is trapped in another solid
776 VectorClear(ent->fields.server->velocity);
781 // break if it moved the entire distance
782 if (trace.fraction == 1)
784 VectorCopy(trace.endpos, ent->fields.server->origin);
790 Con_Printf ("SV_FlyMove: !trace.ent");
791 trace.ent = prog->edicts;
794 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
796 if (trace.plane.normal[2])
798 if (trace.plane.normal[2] > 0.7)
802 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
803 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
810 // save the trace for player extrafriction
812 VectorCopy(trace.plane.normal, stepnormal);
815 if (trace.fraction >= 0.001)
817 // actually covered some distance
818 VectorCopy(trace.endpos, ent->fields.server->origin);
819 VectorCopy(ent->fields.server->velocity, original_velocity);
823 // run the impact function
826 SV_Impact(ent, &trace);
828 // break if removed by the impact function
829 if (ent->priv.server->free)
833 time_left *= 1 - trace.fraction;
835 // clipped to another plane
836 if (numplanes >= MAX_CLIP_PLANES)
838 // this shouldn't really happen
839 VectorClear(ent->fields.server->velocity);
845 for (i = 0;i < numplanes;i++)
846 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
850 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
855 VectorCopy(trace.plane.normal, planes[numplanes]);
858 if (sv_newflymove.integer)
859 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
862 // modify original_velocity so it parallels all of the clip planes
863 for (i = 0;i < numplanes;i++)
865 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
866 for (j = 0;j < numplanes;j++)
871 if (DotProduct(new_velocity, planes[j]) < 0)
881 // go along this plane
882 VectorCopy(new_velocity, ent->fields.server->velocity);
886 // go along the crease
889 VectorClear(ent->fields.server->velocity);
893 CrossProduct(planes[0], planes[1], dir);
894 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
895 VectorNormalize(dir);
896 d = DotProduct(dir, ent->fields.server->velocity);
897 VectorScale(dir, d, ent->fields.server->velocity);
901 // if current velocity is against the original velocity,
902 // stop dead to avoid tiny occilations in sloping corners
903 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
905 VectorClear(ent->fields.server->velocity);
910 //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]);
913 if ((blocked & 1) == 0 && bumpcount > 1)
915 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
916 // flag ONGROUND if there's ground under it
917 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
921 // LordHavoc: this came from QW and allows you to get out of water more easily
922 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
923 VectorCopy(primal_velocity, ent->fields.server->velocity);
933 void SV_AddGravity (prvm_edict_t *ent)
938 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
939 if (val!=0 && val->_float)
940 ent_gravity = val->_float;
943 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
948 ===============================================================================
952 ===============================================================================
959 Does not change the entities velocity at all
962 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
968 VectorAdd (ent->fields.server->origin, push, end);
970 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
972 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
973 type = MOVE_NOMONSTERS; // only clip against bmodels
977 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
978 if (trace.bmodelstartsolid && failonbmodelstartsolid)
981 VectorCopy (trace.endpos, ent->fields.server->origin);
982 SV_LinkEdict (ent, true);
984 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)))
985 SV_Impact (ent, &trace);
996 void SV_PushMove (prvm_edict_t *pusher, float movetime)
999 float savesolid, movetime2, pushltime;
1000 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1002 int numcheckentities;
1003 static prvm_edict_t *checkentities[MAX_EDICTS];
1004 model_t *pushermodel;
1006 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1007 unsigned short moved_edicts[MAX_EDICTS];
1009 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])
1011 pusher->fields.server->ltime += movetime;
1015 switch ((int) pusher->fields.server->solid)
1017 // LordHavoc: valid pusher types
1020 case SOLID_SLIDEBOX:
1021 case SOLID_CORPSE: // LordHavoc: this would be weird...
1023 // LordHavoc: no collisions
1026 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1027 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1028 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1029 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1030 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1031 pusher->fields.server->ltime += movetime;
1032 SV_LinkEdict (pusher, false);
1035 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1038 index = (int) pusher->fields.server->modelindex;
1039 if (index < 1 || index >= MAX_MODELS)
1041 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1044 pushermodel = sv.models[index];
1046 movetime2 = movetime;
1047 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1048 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1049 if (moveangle[0] || moveangle[2])
1051 for (i = 0;i < 3;i++)
1055 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1056 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1060 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1061 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1065 else if (moveangle[1])
1067 for (i = 0;i < 3;i++)
1071 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1072 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1076 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1077 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1083 for (i = 0;i < 3;i++)
1087 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1088 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1092 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1093 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1098 VectorNegate (moveangle, a);
1099 AngleVectorsFLU (a, forward, left, up);
1101 VectorCopy (pusher->fields.server->origin, pushorig);
1102 VectorCopy (pusher->fields.server->angles, pushang);
1103 pushltime = pusher->fields.server->ltime;
1105 // move the pusher to its final position
1107 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1108 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1109 pusher->fields.server->ltime += movetime;
1110 SV_LinkEdict (pusher, false);
1113 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1114 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1115 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);
1116 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1118 savesolid = pusher->fields.server->solid;
1120 // see if any solid entities are inside the final position
1123 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1124 for (e = 0;e < numcheckentities;e++)
1126 prvm_edict_t *check = checkentities[e];
1127 int checkcontents = SV_GenericHitSuperContentsMask(check);
1128 if (check->fields.server->movetype == MOVETYPE_NONE
1129 || check->fields.server->movetype == MOVETYPE_PUSH
1130 || check->fields.server->movetype == MOVETYPE_FOLLOW
1131 || check->fields.server->movetype == MOVETYPE_NOCLIP
1132 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1135 // if the entity is standing on the pusher, it will definitely be moved
1136 // if the entity is not standing on the pusher, but is in the pusher's
1137 // final position, move it
1138 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1140 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);
1141 if (!trace.startsolid)
1146 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1149 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1150 org2[0] = DotProduct (org, forward);
1151 org2[1] = DotProduct (org, left);
1152 org2[2] = DotProduct (org, up);
1153 VectorSubtract (org2, org, move);
1154 VectorAdd (move, move1, move);
1157 VectorCopy (move1, move);
1159 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1160 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1161 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1163 // try moving the contacted entity
1164 pusher->fields.server->solid = SOLID_NOT;
1165 trace = SV_PushEntity (check, move, true);
1166 // FIXME: turn players specially
1167 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1168 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1169 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1171 // this trace.fraction < 1 check causes items to fall off of pushers
1172 // if they pass under or through a wall
1173 // the groundentity check causes items to fall off of ledges
1174 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1175 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1177 // if it is still inside the pusher, block
1178 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);
1179 if (trace.startsolid)
1181 // try moving the contacted entity a tiny bit further to account for precision errors
1183 pusher->fields.server->solid = SOLID_NOT;
1184 VectorScale(move, 1.1, move2);
1185 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1186 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1187 SV_PushEntity (check, move2, true);
1188 pusher->fields.server->solid = savesolid;
1189 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);
1190 if (trace.startsolid)
1192 // try moving the contacted entity a tiny bit less to account for precision errors
1193 pusher->fields.server->solid = SOLID_NOT;
1194 VectorScale(move, 0.9, move2);
1195 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1196 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1197 SV_PushEntity (check, move2, true);
1198 pusher->fields.server->solid = savesolid;
1199 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);
1200 if (trace.startsolid)
1202 // still inside pusher, so it's really blocked
1205 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1207 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1210 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1211 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1215 VectorCopy (pushorig, pusher->fields.server->origin);
1216 VectorCopy (pushang, pusher->fields.server->angles);
1217 pusher->fields.server->ltime = pushltime;
1218 SV_LinkEdict (pusher, false);
1220 // move back any entities we already moved
1221 for (i = 0;i < num_moved;i++)
1223 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1224 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1225 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1226 SV_LinkEdict (ed, false);
1229 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1230 if (pusher->fields.server->blocked)
1232 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1233 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1234 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1241 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1242 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1243 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1252 void SV_Physics_Pusher (prvm_edict_t *ent)
1254 float thinktime, oldltime, movetime;
1256 oldltime = ent->fields.server->ltime;
1258 thinktime = ent->fields.server->nextthink;
1259 if (thinktime < ent->fields.server->ltime + sv.frametime)
1261 movetime = thinktime - ent->fields.server->ltime;
1266 movetime = sv.frametime;
1269 // advances ent->fields.server->ltime if not blocked
1270 SV_PushMove (ent, movetime);
1272 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1274 ent->fields.server->nextthink = 0;
1275 prog->globals.server->time = sv.time;
1276 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1277 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1278 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1284 ===============================================================================
1288 ===============================================================================
1291 static float unstickoffsets[] =
1325 This is a big hack to try and fix the rare case of getting stuck in the world
1329 void SV_CheckStuck (prvm_edict_t *ent)
1334 if (!SV_TestEntityPosition(ent, vec3_origin))
1336 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1340 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1342 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1344 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]);
1345 SV_LinkEdict (ent, true);
1350 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1351 if (!SV_TestEntityPosition(ent, offset))
1353 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1354 SV_LinkEdict (ent, true);
1358 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1361 static void SV_UnstickEntity (prvm_edict_t *ent)
1365 // if not stuck in a bmodel, just return
1366 if (!SV_TestEntityPosition(ent, vec3_origin))
1369 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1371 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1373 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]);
1374 SV_LinkEdict (ent, true);
1379 if (developer.integer >= 100)
1380 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1389 qboolean SV_CheckWater (prvm_edict_t *ent)
1392 int nNativeContents;
1395 point[0] = ent->fields.server->origin[0];
1396 point[1] = ent->fields.server->origin[1];
1397 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1399 // DRESK - Support for Entity Contents Transition Event
1400 // NOTE: Some logic needed to be slightly re-ordered
1401 // to not affect performance and allow for the feature.
1403 // Acquire Super Contents Prior to Resets
1404 cont = SV_PointSuperContents(point);
1405 // Acquire Native Contents Here
1406 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1408 // DRESK - Support for Entity Contents Transition Event
1409 if(ent->fields.server->watertype)
1410 // Entity did NOT Spawn; Check
1411 SV_CheckContentsTransition(ent, nNativeContents);
1414 ent->fields.server->waterlevel = 0;
1415 ent->fields.server->watertype = CONTENTS_EMPTY;
1416 cont = SV_PointSuperContents(point);
1417 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1419 ent->fields.server->watertype = nNativeContents;
1420 ent->fields.server->waterlevel = 1;
1421 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1422 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1424 ent->fields.server->waterlevel = 2;
1425 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1426 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1427 ent->fields.server->waterlevel = 3;
1431 return ent->fields.server->waterlevel > 1;
1440 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1443 vec3_t forward, into, side;
1445 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1446 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1448 // cut the tangential velocity
1449 i = DotProduct (stepnormal, ent->fields.server->velocity);
1450 VectorScale (stepnormal, i, into);
1451 VectorSubtract (ent->fields.server->velocity, into, side);
1452 ent->fields.server->velocity[0] = side[0] * (1 + d);
1453 ent->fields.server->velocity[1] = side[1] * (1 + d);
1459 =====================
1462 Player has come to a dead stop, possibly due to the problem with limited
1463 float precision at some angle joins in the BSP hull.
1465 Try fixing by pushing one pixel in each direction.
1467 This is a hack, but in the interest of good gameplay...
1468 ======================
1470 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1475 VectorCopy (ent->fields.server->origin, oldorg);
1478 for (i=0 ; i<8 ; i++)
1480 // try pushing a little in an axial direction
1483 case 0: dir[0] = 2; dir[1] = 0; break;
1484 case 1: dir[0] = 0; dir[1] = 2; break;
1485 case 2: dir[0] = -2; dir[1] = 0; break;
1486 case 3: dir[0] = 0; dir[1] = -2; break;
1487 case 4: dir[0] = 2; dir[1] = 2; break;
1488 case 5: dir[0] = -2; dir[1] = 2; break;
1489 case 6: dir[0] = 2; dir[1] = -2; break;
1490 case 7: dir[0] = -2; dir[1] = -2; break;
1493 SV_PushEntity (ent, dir, false);
1495 // retry the original move
1496 ent->fields.server->velocity[0] = oldvel[0];
1497 ent->fields.server->velocity[1] = oldvel[1];
1498 ent->fields.server->velocity[2] = 0;
1499 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1501 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1502 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1504 Con_DPrint("TryUnstick - success.\n");
1508 // go back to the original pos and try again
1509 VectorCopy (oldorg, ent->fields.server->origin);
1513 VectorClear (ent->fields.server->velocity);
1514 Con_DPrint("TryUnstick - failure.\n");
1520 =====================
1523 Only used by players
1524 ======================
1526 void SV_WalkMove (prvm_edict_t *ent)
1528 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1529 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1532 // if frametime is 0 (due to client sending the same timestamp twice),
1534 if (sv.frametime <= 0)
1537 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1539 SV_CheckVelocity(ent);
1541 // do a regular slide move unless it looks like you ran into a step
1542 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1544 VectorCopy (ent->fields.server->origin, start_origin);
1545 VectorCopy (ent->fields.server->velocity, start_velocity);
1547 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1549 // if the move did not hit the ground at any point, we're not on ground
1551 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1553 SV_CheckVelocity(ent);
1555 VectorCopy(ent->fields.server->origin, originalmove_origin);
1556 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1557 originalmove_clip = clip;
1558 originalmove_flags = (int)ent->fields.server->flags;
1559 originalmove_groundentity = ent->fields.server->groundentity;
1561 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1564 if (sv_nostep.integer)
1567 // if move didn't block on a step, return
1570 // if move was not trying to move into the step, return
1571 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1574 if (ent->fields.server->movetype != MOVETYPE_FLY)
1576 // return if gibbed by a trigger
1577 if (ent->fields.server->movetype != MOVETYPE_WALK)
1580 // only step up while jumping if that is enabled
1581 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1582 if (!oldonground && ent->fields.server->waterlevel == 0)
1586 // try moving up and forward to go up a step
1587 // back to start pos
1588 VectorCopy (start_origin, ent->fields.server->origin);
1589 VectorCopy (start_velocity, ent->fields.server->velocity);
1592 VectorClear (upmove);
1593 upmove[2] = sv_stepheight.value;
1594 // FIXME: don't link?
1595 SV_PushEntity(ent, upmove, false);
1598 ent->fields.server->velocity[2] = 0;
1599 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1600 ent->fields.server->velocity[2] += start_velocity[2];
1602 SV_CheckVelocity(ent);
1604 // check for stuckness, possibly due to the limited precision of floats
1605 // in the clipping hulls
1607 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1608 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1610 //Con_Printf("wall\n");
1611 // stepping up didn't make any progress, revert to original move
1612 VectorCopy(originalmove_origin, ent->fields.server->origin);
1613 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1614 //clip = originalmove_clip;
1615 ent->fields.server->flags = originalmove_flags;
1616 ent->fields.server->groundentity = originalmove_groundentity;
1617 // now try to unstick if needed
1618 //clip = SV_TryUnstick (ent, oldvel);
1622 //Con_Printf("step - ");
1624 // extra friction based on view angle
1625 if (clip & 2 && sv_wallfriction.integer)
1626 SV_WallFriction (ent, stepnormal);
1628 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1629 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))
1633 VectorClear (downmove);
1634 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1635 // FIXME: don't link?
1636 downtrace = SV_PushEntity (ent, downmove, false);
1638 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1640 // this has been disabled so that you can't jump when you are stepping
1641 // up while already jumping (also known as the Quake2 double jump bug)
1643 // LordHavoc: disabled this check so you can walk on monsters/players
1644 //if (ent->fields.server->solid == SOLID_BSP)
1646 //Con_Printf("onground\n");
1647 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1648 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1654 //Con_Printf("slope\n");
1655 // if the push down didn't end up on good ground, use the move without
1656 // the step up. This happens near wall / slope combinations, and can
1657 // cause the player to hop up higher on a slope too steep to climb
1658 VectorCopy(originalmove_origin, ent->fields.server->origin);
1659 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1660 //clip = originalmove_clip;
1661 ent->fields.server->flags = originalmove_flags;
1662 ent->fields.server->groundentity = originalmove_groundentity;
1665 SV_CheckVelocity(ent);
1668 //============================================================================
1674 Entities that are "stuck" to another entity
1677 void SV_Physics_Follow (prvm_edict_t *ent)
1679 vec3_t vf, vr, vu, angles, v;
1683 if (!SV_RunThink (ent))
1686 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1687 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1688 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])
1690 // quick case for no rotation
1691 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1695 angles[0] = -ent->fields.server->punchangle[0];
1696 angles[1] = ent->fields.server->punchangle[1];
1697 angles[2] = ent->fields.server->punchangle[2];
1698 AngleVectors (angles, vf, vr, vu);
1699 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];
1700 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];
1701 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];
1702 angles[0] = -e->fields.server->angles[0];
1703 angles[1] = e->fields.server->angles[1];
1704 angles[2] = e->fields.server->angles[2];
1705 AngleVectors (angles, vf, vr, vu);
1706 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1707 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1708 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1710 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1711 SV_LinkEdict (ent, true);
1715 ==============================================================================
1719 ==============================================================================
1724 SV_CheckWaterTransition
1728 void SV_CheckWaterTransition (prvm_edict_t *ent)
1731 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1732 if (!ent->fields.server->watertype)
1734 // just spawned here
1735 ent->fields.server->watertype = cont;
1736 ent->fields.server->waterlevel = 1;
1740 // DRESK - Support for Entity Contents Transition Event
1741 // NOTE: Call here BEFORE updating the watertype below,
1742 // and suppress watersplash sound if a valid function
1743 // call was made to allow for custom "splash" sounds.
1744 if( !SV_CheckContentsTransition(ent, cont) )
1745 { // Contents Transition Function Invalid; Potentially Play Water Sound
1746 // check if the entity crossed into or out of water
1747 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1748 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1751 if (cont <= CONTENTS_WATER)
1753 ent->fields.server->watertype = cont;
1754 ent->fields.server->waterlevel = 1;
1758 ent->fields.server->watertype = CONTENTS_EMPTY;
1759 ent->fields.server->waterlevel = 0;
1767 Toss, bounce, and fly movement. When onground, do nothing.
1770 void SV_Physics_Toss (prvm_edict_t *ent)
1775 // if onground, return without moving
1776 if ((int)ent->fields.server->flags & FL_ONGROUND)
1778 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1780 // don't stick to ground if onground and moving upward
1781 ent->fields.server->flags -= FL_ONGROUND;
1783 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1785 // we can trust FL_ONGROUND if groundentity is world because it never moves
1788 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1790 // if ent was supported by a brush model on previous frame,
1791 // and groundentity is now freed, set groundentity to 0 (world)
1792 // which leaves it suspended in the air
1793 ent->fields.server->groundentity = 0;
1797 ent->priv.server->suspendedinairflag = false;
1799 SV_CheckVelocity (ent);
1802 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1803 SV_AddGravity (ent);
1806 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1809 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1810 trace = SV_PushEntity (ent, move, true);
1811 if (ent->priv.server->free)
1813 if (trace.bmodelstartsolid)
1815 // try to unstick the entity
1816 SV_UnstickEntity(ent);
1817 trace = SV_PushEntity (ent, move, false);
1818 if (ent->priv.server->free)
1822 if (trace.fraction < 1)
1824 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1826 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1827 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1829 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1832 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1833 // LordHavoc: fixed grenades not bouncing when fired down a slope
1834 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1836 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1837 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1839 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1840 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1841 VectorClear (ent->fields.server->velocity);
1842 VectorClear (ent->fields.server->avelocity);
1845 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1849 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1851 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1852 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1853 VectorClear (ent->fields.server->velocity);
1854 VectorClear (ent->fields.server->avelocity);
1857 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1862 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1863 if (trace.plane.normal[2] > 0.7)
1865 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1866 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1867 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1868 ent->priv.server->suspendedinairflag = true;
1869 VectorClear (ent->fields.server->velocity);
1870 VectorClear (ent->fields.server->avelocity);
1873 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1877 // check for in water
1878 SV_CheckWaterTransition (ent);
1882 ===============================================================================
1886 ===============================================================================
1893 Monsters freefall when they don't have a ground entity, otherwise
1894 all movement is done with discrete steps.
1896 This is also used for objects that have become still on the ground, but
1897 will fall if the floor is pulled out from under them.
1900 void SV_Physics_Step (prvm_edict_t *ent)
1902 int flags = (int)ent->fields.server->flags;
1903 // don't fall at all if fly/swim
1904 if (!(flags & (FL_FLY | FL_SWIM)))
1906 if (flags & FL_ONGROUND)
1908 // freefall if onground and moving upward
1909 // freefall if not standing on a world surface (it may be a lift or trap door)
1910 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1912 ent->fields.server->flags -= FL_ONGROUND;
1914 SV_CheckVelocity(ent);
1915 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1916 SV_LinkEdict(ent, true);
1921 // freefall if not onground
1922 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1925 SV_CheckVelocity(ent);
1926 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1927 SV_LinkEdict(ent, true);
1930 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1931 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1938 SV_CheckWaterTransition(ent);
1941 //============================================================================
1943 static void SV_Physics_Entity (prvm_edict_t *ent)
1945 // don't run a move on newly spawned projectiles as it messes up movement
1946 // interpolation and rocket trails
1947 qboolean runmove = ent->priv.server->move;
1948 ent->priv.server->move = true;
1949 switch ((int) ent->fields.server->movetype)
1952 case MOVETYPE_FAKEPUSH:
1953 SV_Physics_Pusher (ent);
1956 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1957 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1960 case MOVETYPE_FOLLOW:
1961 SV_Physics_Follow (ent);
1963 case MOVETYPE_NOCLIP:
1964 if (SV_RunThink(ent))
1967 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1968 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1970 SV_LinkEdict(ent, false);
1973 SV_Physics_Step (ent);
1976 if (SV_RunThink (ent))
1978 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1979 SV_AddGravity (ent);
1980 SV_CheckStuck (ent);
1982 SV_LinkEdict (ent, true);
1986 case MOVETYPE_BOUNCE:
1987 case MOVETYPE_BOUNCEMISSILE:
1988 case MOVETYPE_FLYMISSILE:
1991 if (SV_RunThink (ent) && runmove)
1992 SV_Physics_Toss (ent);
1995 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2000 void SV_Physics_ClientMove(void)
2003 ent = host_client->edict;
2005 // call player physics, this needs the proper frametime
2006 prog->globals.server->frametime = sv.frametime;
2009 // call standard client pre-think, with frametime = 0
2010 prog->globals.server->time = sv.time;
2011 prog->globals.server->frametime = 0;
2012 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2013 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2014 prog->globals.server->frametime = sv.frametime;
2016 // make sure the velocity is sane (not a NaN)
2017 SV_CheckVelocity(ent);
2018 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2019 // player_run/player_stand1 does not horribly malfunction if the
2020 // velocity becomes a number that is both == 0 and != 0
2021 // (sounds to me like NaN but to be absolutely safe...)
2022 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2023 VectorClear(ent->fields.server->velocity);
2025 // perform MOVETYPE_WALK behavior
2026 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2027 SV_AddGravity (ent);
2028 SV_CheckStuck (ent);
2031 SV_CheckVelocity (ent);
2033 SV_LinkEdict (ent, true);
2035 SV_CheckVelocity (ent);
2037 // call standard player post-think, with frametime = 0
2038 prog->globals.server->time = sv.time;
2039 prog->globals.server->frametime = 0;
2040 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2041 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2042 prog->globals.server->frametime = sv.frametime;
2044 if(ent->fields.server->fixangle)
2046 // angle fixing was requested by physics code...
2047 // so store the current angles for later use
2048 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2049 host_client->fixangle_angles_set = TRUE;
2051 // and clear fixangle for the next frame
2052 ent->fields.server->fixangle = 0;
2056 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2058 // don't do physics on disconnected clients, FrikBot relies on this
2059 if (!host_client->spawned)
2061 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2065 // don't run physics here if running asynchronously
2066 if (host_client->clmovement_skipphysicsframes <= 0)
2069 // make sure the velocity is sane (not a NaN)
2070 SV_CheckVelocity(ent);
2071 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2072 // player_run/player_stand1 does not horribly malfunction if the
2073 // velocity becomes a number that is both == 0 and != 0
2074 // (sounds to me like NaN but to be absolutely safe...)
2075 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2076 VectorClear(ent->fields.server->velocity);
2078 // call standard client pre-think
2079 prog->globals.server->time = sv.time;
2080 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2081 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2082 SV_CheckVelocity (ent);
2084 switch ((int) ent->fields.server->movetype)
2087 case MOVETYPE_FAKEPUSH:
2088 SV_Physics_Pusher (ent);
2091 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2092 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2095 case MOVETYPE_FOLLOW:
2096 SV_Physics_Follow (ent);
2098 case MOVETYPE_NOCLIP:
2101 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2102 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2105 SV_Physics_Step (ent);
2109 // don't run physics here if running asynchronously
2110 if (host_client->clmovement_skipphysicsframes <= 0)
2112 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2113 SV_AddGravity (ent);
2114 SV_CheckStuck (ent);
2119 case MOVETYPE_BOUNCE:
2120 case MOVETYPE_BOUNCEMISSILE:
2121 case MOVETYPE_FLYMISSILE:
2124 SV_Physics_Toss (ent);
2128 SV_CheckWater (ent);
2132 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2136 // decrement the countdown variable used to decide when to go back to
2137 // synchronous physics
2138 if (host_client->clmovement_skipphysicsframes > 0)
2139 host_client->clmovement_skipphysicsframes--;
2141 SV_CheckVelocity (ent);
2143 SV_LinkEdict (ent, true);
2145 SV_CheckVelocity (ent);
2147 // call standard player post-think
2148 prog->globals.server->time = sv.time;
2149 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2150 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2152 if(ent->fields.server->fixangle)
2154 // angle fixing was requested by physics code...
2155 // so store the current angles for later use
2156 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2157 host_client->fixangle_angles_set = TRUE;
2159 // and clear fixangle for the next frame
2160 ent->fields.server->fixangle = 0;
2170 void SV_Physics (void)
2175 // let the progs know that a new frame has started
2176 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2177 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2178 prog->globals.server->time = sv.time;
2179 prog->globals.server->frametime = sv.frametime;
2180 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2183 // treat each object in turn
2186 // if force_retouch, relink all the entities
2187 if (prog->globals.server->force_retouch > 0)
2188 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2189 if (!ent->priv.server->free)
2190 SV_LinkEdict (ent, true); // force retouch even for stationary
2192 // run physics on the client entities
2193 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2194 if (!ent->priv.server->free)
2195 SV_Physics_ClientEntity(ent);
2197 // run physics on all the non-client entities
2198 if (!sv_freezenonclients.integer)
2199 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2200 if (!ent->priv.server->free)
2201 SV_Physics_Entity(ent);
2203 if (prog->globals.server->force_retouch > 0)
2204 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2206 // LordHavoc: endframe support
2207 if (prog->funcoffsets.EndFrame)
2209 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2210 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2211 prog->globals.server->time = sv.time;
2212 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2215 // decrement prog->num_edicts if the highest number entities died
2216 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2218 if (!sv_freezenonclients.integer)
2219 sv.time += sv.frametime;