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 thinktime = ent->fields.server->nextthink;
589 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
592 // don't let things stay in the past.
593 // it is possible to start that way by a trigger with a local time.
594 if (thinktime < sv.time)
597 ent->fields.server->nextthink = 0;
598 prog->globals.server->time = thinktime;
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 return !ent->priv.server->free;
609 Two entities have touched, so run their touch functions
612 extern void VM_SetTraceGlobals(const trace_t *trace);
613 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
615 int old_self, old_other;
616 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
619 old_self = prog->globals.server->self;
620 old_other = prog->globals.server->other;
622 VM_SetTraceGlobals(trace);
624 prog->globals.server->time = sv.time;
625 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
627 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
628 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
629 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
632 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
634 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
635 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
636 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
637 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
638 prog->globals.server->trace_plane_dist = -trace->plane.dist;
639 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
640 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
642 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
644 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
646 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
648 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
651 prog->globals.server->self = old_self;
652 prog->globals.server->other = old_other;
660 Slide off of the impacting object
661 returns the blocked flags (1 = floor, 2 = step / wall)
664 #define STOP_EPSILON 0.1
665 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
670 backoff = -DotProduct (in, normal) * overbounce;
671 VectorMA(in, backoff, normal, out);
673 for (i = 0;i < 3;i++)
674 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
683 The basic solid body movement clip that slides along multiple planes
684 Returns the clipflags if the velocity was modified (hit something solid)
688 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
691 // LordHavoc: increased from 5 to 32
692 #define MAX_CLIP_PLANES 32
693 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
695 int blocked, bumpcount;
696 int i, j, impact, numplanes;
698 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
703 VectorCopy(ent->fields.server->velocity, original_velocity);
704 VectorCopy(ent->fields.server->velocity, primal_velocity);
707 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
709 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
712 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
713 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
715 //if (trace.fraction < 0.002)
720 VectorCopy(ent->fields.server->origin, start);
721 start[2] += 3;//0.03125;
722 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
723 end[2] += 3;//0.03125;
724 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
725 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)))
727 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
733 for (i = 0;i < numplanes;i++)
735 VectorCopy(ent->fields.server->origin, start);
736 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
737 VectorMA(start, 3, planes[i], start);
738 VectorMA(end, 3, planes[i], end);
739 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
740 if (trace.fraction < testtrace.fraction)
743 VectorCopy(start, ent->fields.server->origin);
748 // VectorAdd(ent->fields.server->origin, planes[j], start);
754 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);
755 if (trace.fraction < 1)
756 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
761 if (trace.bmodelstartsolid)
763 // LordHavoc: note: this code is what makes entities stick in place
764 // if embedded in world only (you can walk through other objects if
766 // entity is trapped in another solid
767 VectorClear(ent->fields.server->velocity);
772 // break if it moved the entire distance
773 if (trace.fraction == 1)
775 VectorCopy(trace.endpos, ent->fields.server->origin);
781 Con_Printf ("SV_FlyMove: !trace.ent");
782 trace.ent = prog->edicts;
785 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
787 if (trace.plane.normal[2])
789 if (trace.plane.normal[2] > 0.7)
793 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
794 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
801 // save the trace for player extrafriction
803 VectorCopy(trace.plane.normal, stepnormal);
806 if (trace.fraction >= 0.001)
808 // actually covered some distance
809 VectorCopy(trace.endpos, ent->fields.server->origin);
810 VectorCopy(ent->fields.server->velocity, original_velocity);
814 // run the impact function
817 SV_Impact(ent, &trace);
819 // break if removed by the impact function
820 if (ent->priv.server->free)
824 time_left *= 1 - trace.fraction;
826 // clipped to another plane
827 if (numplanes >= MAX_CLIP_PLANES)
829 // this shouldn't really happen
830 VectorClear(ent->fields.server->velocity);
836 for (i = 0;i < numplanes;i++)
837 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
841 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
846 VectorCopy(trace.plane.normal, planes[numplanes]);
849 if (sv_newflymove.integer)
850 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
853 // modify original_velocity so it parallels all of the clip planes
854 for (i = 0;i < numplanes;i++)
856 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
857 for (j = 0;j < numplanes;j++)
862 if (DotProduct(new_velocity, planes[j]) < 0)
872 // go along this plane
873 VectorCopy(new_velocity, ent->fields.server->velocity);
877 // go along the crease
880 VectorClear(ent->fields.server->velocity);
884 CrossProduct(planes[0], planes[1], dir);
885 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
886 VectorNormalize(dir);
887 d = DotProduct(dir, ent->fields.server->velocity);
888 VectorScale(dir, d, ent->fields.server->velocity);
892 // if current velocity is against the original velocity,
893 // stop dead to avoid tiny occilations in sloping corners
894 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
896 VectorClear(ent->fields.server->velocity);
901 //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]);
904 if ((blocked & 1) == 0 && bumpcount > 1)
906 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
907 // flag ONGROUND if there's ground under it
908 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
912 // LordHavoc: this came from QW and allows you to get out of water more easily
913 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
914 VectorCopy(primal_velocity, ent->fields.server->velocity);
924 void SV_AddGravity (prvm_edict_t *ent)
929 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
930 if (val!=0 && val->_float)
931 ent_gravity = val->_float;
934 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
939 ===============================================================================
943 ===============================================================================
950 Does not change the entities velocity at all
953 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
959 VectorAdd (ent->fields.server->origin, push, end);
961 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
963 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
964 type = MOVE_NOMONSTERS; // only clip against bmodels
968 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
969 if (trace.bmodelstartsolid && failonbmodelstartsolid)
972 VectorCopy (trace.endpos, ent->fields.server->origin);
973 SV_LinkEdict (ent, true);
975 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)))
976 SV_Impact (ent, &trace);
987 void SV_PushMove (prvm_edict_t *pusher, float movetime)
990 float savesolid, movetime2, pushltime;
991 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
993 int numcheckentities;
994 static prvm_edict_t *checkentities[MAX_EDICTS];
995 model_t *pushermodel;
997 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
998 unsigned short moved_edicts[MAX_EDICTS];
1000 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])
1002 pusher->fields.server->ltime += movetime;
1006 switch ((int) pusher->fields.server->solid)
1008 // LordHavoc: valid pusher types
1011 case SOLID_SLIDEBOX:
1012 case SOLID_CORPSE: // LordHavoc: this would be weird...
1014 // LordHavoc: no collisions
1017 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1018 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1019 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1020 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1021 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1022 pusher->fields.server->ltime += movetime;
1023 SV_LinkEdict (pusher, false);
1026 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1029 index = (int) pusher->fields.server->modelindex;
1030 if (index < 1 || index >= MAX_MODELS)
1032 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1035 pushermodel = sv.models[index];
1037 movetime2 = movetime;
1038 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1039 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1040 if (moveangle[0] || moveangle[2])
1042 for (i = 0;i < 3;i++)
1046 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1047 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1051 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1052 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1056 else if (moveangle[1])
1058 for (i = 0;i < 3;i++)
1062 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1063 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1067 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1068 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1074 for (i = 0;i < 3;i++)
1078 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1079 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1083 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1084 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1089 VectorNegate (moveangle, a);
1090 AngleVectorsFLU (a, forward, left, up);
1092 VectorCopy (pusher->fields.server->origin, pushorig);
1093 VectorCopy (pusher->fields.server->angles, pushang);
1094 pushltime = pusher->fields.server->ltime;
1096 // move the pusher to its final position
1098 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1099 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1100 pusher->fields.server->ltime += movetime;
1101 SV_LinkEdict (pusher, false);
1104 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1105 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1106 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);
1107 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1109 savesolid = pusher->fields.server->solid;
1111 // see if any solid entities are inside the final position
1114 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1115 for (e = 0;e < numcheckentities;e++)
1117 prvm_edict_t *check = checkentities[e];
1118 int checkcontents = SV_GenericHitSuperContentsMask(check);
1119 if (check->fields.server->movetype == MOVETYPE_NONE
1120 || check->fields.server->movetype == MOVETYPE_PUSH
1121 || check->fields.server->movetype == MOVETYPE_FOLLOW
1122 || check->fields.server->movetype == MOVETYPE_NOCLIP
1123 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1126 // if the entity is standing on the pusher, it will definitely be moved
1127 // if the entity is not standing on the pusher, but is in the pusher's
1128 // final position, move it
1129 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1131 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);
1132 if (!trace.startsolid)
1137 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1140 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1141 org2[0] = DotProduct (org, forward);
1142 org2[1] = DotProduct (org, left);
1143 org2[2] = DotProduct (org, up);
1144 VectorSubtract (org2, org, move);
1145 VectorAdd (move, move1, move);
1148 VectorCopy (move1, move);
1150 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1151 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1152 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1154 // try moving the contacted entity
1155 pusher->fields.server->solid = SOLID_NOT;
1156 trace = SV_PushEntity (check, move, true);
1157 // FIXME: turn players specially
1158 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1159 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1160 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1162 // this trace.fraction < 1 check causes items to fall off of pushers
1163 // if they pass under or through a wall
1164 // the groundentity check causes items to fall off of ledges
1165 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1166 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1168 // if it is still inside the pusher, block
1169 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);
1170 if (trace.startsolid)
1172 // try moving the contacted entity a tiny bit further to account for precision errors
1174 pusher->fields.server->solid = SOLID_NOT;
1175 VectorScale(move, 1.1, move2);
1176 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1177 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1178 SV_PushEntity (check, move2, true);
1179 pusher->fields.server->solid = savesolid;
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 less to account for precision errors
1184 pusher->fields.server->solid = SOLID_NOT;
1185 VectorScale(move, 0.9, move2);
1186 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1187 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1188 SV_PushEntity (check, move2, true);
1189 pusher->fields.server->solid = savesolid;
1190 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);
1191 if (trace.startsolid)
1193 // still inside pusher, so it's really blocked
1196 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1198 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1201 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1202 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1206 VectorCopy (pushorig, pusher->fields.server->origin);
1207 VectorCopy (pushang, pusher->fields.server->angles);
1208 pusher->fields.server->ltime = pushltime;
1209 SV_LinkEdict (pusher, false);
1211 // move back any entities we already moved
1212 for (i = 0;i < num_moved;i++)
1214 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1215 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1216 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1217 SV_LinkEdict (ed, false);
1220 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1221 if (pusher->fields.server->blocked)
1223 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1224 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1225 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1232 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1233 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1234 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1243 void SV_Physics_Pusher (prvm_edict_t *ent)
1245 float thinktime, oldltime, movetime;
1247 oldltime = ent->fields.server->ltime;
1249 thinktime = ent->fields.server->nextthink;
1250 if (thinktime < ent->fields.server->ltime + sv.frametime)
1252 movetime = thinktime - ent->fields.server->ltime;
1257 movetime = sv.frametime;
1260 // advances ent->fields.server->ltime if not blocked
1261 SV_PushMove (ent, movetime);
1263 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1265 ent->fields.server->nextthink = 0;
1266 prog->globals.server->time = sv.time;
1267 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1268 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1269 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1275 ===============================================================================
1279 ===============================================================================
1282 static float unstickoffsets[] =
1316 This is a big hack to try and fix the rare case of getting stuck in the world
1320 void SV_CheckStuck (prvm_edict_t *ent)
1325 if (!SV_TestEntityPosition(ent, vec3_origin))
1327 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1331 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1333 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1335 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]);
1336 SV_LinkEdict (ent, true);
1341 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1342 if (!SV_TestEntityPosition(ent, offset))
1344 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1345 SV_LinkEdict (ent, true);
1349 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1352 static void SV_UnstickEntity (prvm_edict_t *ent)
1356 // if not stuck in a bmodel, just return
1357 if (!SV_TestEntityPosition(ent, vec3_origin))
1360 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1362 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1364 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]);
1365 SV_LinkEdict (ent, true);
1370 if (developer.integer >= 100)
1371 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1380 qboolean SV_CheckWater (prvm_edict_t *ent)
1383 int nNativeContents;
1386 point[0] = ent->fields.server->origin[0];
1387 point[1] = ent->fields.server->origin[1];
1388 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1390 // DRESK - Support for Entity Contents Transition Event
1391 // NOTE: Some logic needed to be slightly re-ordered
1392 // to not affect performance and allow for the feature.
1394 // Acquire Super Contents Prior to Resets
1395 cont = SV_PointSuperContents(point);
1396 // Acquire Native Contents Here
1397 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1399 // DRESK - Support for Entity Contents Transition Event
1400 if(ent->fields.server->watertype)
1401 // Entity did NOT Spawn; Check
1402 SV_CheckContentsTransition(ent, nNativeContents);
1405 ent->fields.server->waterlevel = 0;
1406 ent->fields.server->watertype = CONTENTS_EMPTY;
1407 cont = SV_PointSuperContents(point);
1408 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1410 ent->fields.server->watertype = nNativeContents;
1411 ent->fields.server->waterlevel = 1;
1412 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1413 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1415 ent->fields.server->waterlevel = 2;
1416 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1417 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1418 ent->fields.server->waterlevel = 3;
1422 return ent->fields.server->waterlevel > 1;
1431 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1434 vec3_t forward, into, side;
1436 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1437 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1439 // cut the tangential velocity
1440 i = DotProduct (stepnormal, ent->fields.server->velocity);
1441 VectorScale (stepnormal, i, into);
1442 VectorSubtract (ent->fields.server->velocity, into, side);
1443 ent->fields.server->velocity[0] = side[0] * (1 + d);
1444 ent->fields.server->velocity[1] = side[1] * (1 + d);
1450 =====================
1453 Player has come to a dead stop, possibly due to the problem with limited
1454 float precision at some angle joins in the BSP hull.
1456 Try fixing by pushing one pixel in each direction.
1458 This is a hack, but in the interest of good gameplay...
1459 ======================
1461 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1466 VectorCopy (ent->fields.server->origin, oldorg);
1469 for (i=0 ; i<8 ; i++)
1471 // try pushing a little in an axial direction
1474 case 0: dir[0] = 2; dir[1] = 0; break;
1475 case 1: dir[0] = 0; dir[1] = 2; break;
1476 case 2: dir[0] = -2; dir[1] = 0; break;
1477 case 3: dir[0] = 0; dir[1] = -2; break;
1478 case 4: dir[0] = 2; dir[1] = 2; break;
1479 case 5: dir[0] = -2; dir[1] = 2; break;
1480 case 6: dir[0] = 2; dir[1] = -2; break;
1481 case 7: dir[0] = -2; dir[1] = -2; break;
1484 SV_PushEntity (ent, dir, false);
1486 // retry the original move
1487 ent->fields.server->velocity[0] = oldvel[0];
1488 ent->fields.server->velocity[1] = oldvel[1];
1489 ent->fields.server->velocity[2] = 0;
1490 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1492 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1493 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1495 Con_DPrint("TryUnstick - success.\n");
1499 // go back to the original pos and try again
1500 VectorCopy (oldorg, ent->fields.server->origin);
1504 VectorClear (ent->fields.server->velocity);
1505 Con_DPrint("TryUnstick - failure.\n");
1511 =====================
1514 Only used by players
1515 ======================
1517 void SV_WalkMove (prvm_edict_t *ent)
1519 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1520 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1523 // if frametime is 0 (due to client sending the same timestamp twice),
1525 if (sv.frametime <= 0)
1528 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1530 SV_CheckVelocity(ent);
1532 // do a regular slide move unless it looks like you ran into a step
1533 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1535 VectorCopy (ent->fields.server->origin, start_origin);
1536 VectorCopy (ent->fields.server->velocity, start_velocity);
1538 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1540 // if the move did not hit the ground at any point, we're not on ground
1542 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1544 SV_CheckVelocity(ent);
1546 VectorCopy(ent->fields.server->origin, originalmove_origin);
1547 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1548 originalmove_clip = clip;
1549 originalmove_flags = (int)ent->fields.server->flags;
1550 originalmove_groundentity = ent->fields.server->groundentity;
1552 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1555 if (sv_nostep.integer)
1558 // if move didn't block on a step, return
1561 // if move was not trying to move into the step, return
1562 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1565 if (ent->fields.server->movetype != MOVETYPE_FLY)
1567 // return if gibbed by a trigger
1568 if (ent->fields.server->movetype != MOVETYPE_WALK)
1571 // only step up while jumping if that is enabled
1572 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1573 if (!oldonground && ent->fields.server->waterlevel == 0)
1577 // try moving up and forward to go up a step
1578 // back to start pos
1579 VectorCopy (start_origin, ent->fields.server->origin);
1580 VectorCopy (start_velocity, ent->fields.server->velocity);
1583 VectorClear (upmove);
1584 upmove[2] = sv_stepheight.value;
1585 // FIXME: don't link?
1586 SV_PushEntity(ent, upmove, false);
1589 ent->fields.server->velocity[2] = 0;
1590 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1591 ent->fields.server->velocity[2] += start_velocity[2];
1593 SV_CheckVelocity(ent);
1595 // check for stuckness, possibly due to the limited precision of floats
1596 // in the clipping hulls
1598 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1599 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1601 //Con_Printf("wall\n");
1602 // stepping up didn't make any progress, revert to original move
1603 VectorCopy(originalmove_origin, ent->fields.server->origin);
1604 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1605 //clip = originalmove_clip;
1606 ent->fields.server->flags = originalmove_flags;
1607 ent->fields.server->groundentity = originalmove_groundentity;
1608 // now try to unstick if needed
1609 //clip = SV_TryUnstick (ent, oldvel);
1613 //Con_Printf("step - ");
1615 // extra friction based on view angle
1616 if (clip & 2 && sv_wallfriction.integer)
1617 SV_WallFriction (ent, stepnormal);
1619 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1620 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))
1624 VectorClear (downmove);
1625 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1626 // FIXME: don't link?
1627 downtrace = SV_PushEntity (ent, downmove, false);
1629 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1631 // this has been disabled so that you can't jump when you are stepping
1632 // up while already jumping (also known as the Quake2 double jump bug)
1634 // LordHavoc: disabled this check so you can walk on monsters/players
1635 //if (ent->fields.server->solid == SOLID_BSP)
1637 //Con_Printf("onground\n");
1638 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1639 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1645 //Con_Printf("slope\n");
1646 // if the push down didn't end up on good ground, use the move without
1647 // the step up. This happens near wall / slope combinations, and can
1648 // cause the player to hop up higher on a slope too steep to climb
1649 VectorCopy(originalmove_origin, ent->fields.server->origin);
1650 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1651 //clip = originalmove_clip;
1652 ent->fields.server->flags = originalmove_flags;
1653 ent->fields.server->groundentity = originalmove_groundentity;
1656 SV_CheckVelocity(ent);
1659 //============================================================================
1665 Entities that are "stuck" to another entity
1668 void SV_Physics_Follow (prvm_edict_t *ent)
1670 vec3_t vf, vr, vu, angles, v;
1674 if (!SV_RunThink (ent))
1677 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1678 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1679 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])
1681 // quick case for no rotation
1682 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1686 angles[0] = -ent->fields.server->punchangle[0];
1687 angles[1] = ent->fields.server->punchangle[1];
1688 angles[2] = ent->fields.server->punchangle[2];
1689 AngleVectors (angles, vf, vr, vu);
1690 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];
1691 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];
1692 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];
1693 angles[0] = -e->fields.server->angles[0];
1694 angles[1] = e->fields.server->angles[1];
1695 angles[2] = e->fields.server->angles[2];
1696 AngleVectors (angles, vf, vr, vu);
1697 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1698 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1699 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1701 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1702 SV_LinkEdict (ent, true);
1706 ==============================================================================
1710 ==============================================================================
1715 SV_CheckWaterTransition
1719 void SV_CheckWaterTransition (prvm_edict_t *ent)
1722 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1723 if (!ent->fields.server->watertype)
1725 // just spawned here
1726 ent->fields.server->watertype = cont;
1727 ent->fields.server->waterlevel = 1;
1731 // DRESK - Support for Entity Contents Transition Event
1732 // NOTE: Call here BEFORE updating the watertype below,
1733 // and suppress watersplash sound if a valid function
1734 // call was made to allow for custom "splash" sounds.
1735 if( !SV_CheckContentsTransition(ent, cont) )
1736 { // Contents Transition Function Invalid; Potentially Play Water Sound
1737 // check if the entity crossed into or out of water
1738 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1739 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1742 if (cont <= CONTENTS_WATER)
1744 ent->fields.server->watertype = cont;
1745 ent->fields.server->waterlevel = 1;
1749 ent->fields.server->watertype = CONTENTS_EMPTY;
1750 ent->fields.server->waterlevel = 0;
1758 Toss, bounce, and fly movement. When onground, do nothing.
1761 void SV_Physics_Toss (prvm_edict_t *ent)
1766 // if onground, return without moving
1767 if ((int)ent->fields.server->flags & FL_ONGROUND)
1769 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1771 // don't stick to ground if onground and moving upward
1772 ent->fields.server->flags -= FL_ONGROUND;
1774 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1776 // we can trust FL_ONGROUND if groundentity is world because it never moves
1779 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1781 // if ent was supported by a brush model on previous frame,
1782 // and groundentity is now freed, set groundentity to 0 (world)
1783 // which leaves it suspended in the air
1784 ent->fields.server->groundentity = 0;
1788 ent->priv.server->suspendedinairflag = false;
1790 SV_CheckVelocity (ent);
1793 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1794 SV_AddGravity (ent);
1797 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1800 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1801 trace = SV_PushEntity (ent, move, true);
1802 if (ent->priv.server->free)
1804 if (trace.bmodelstartsolid)
1806 // try to unstick the entity
1807 SV_UnstickEntity(ent);
1808 trace = SV_PushEntity (ent, move, false);
1809 if (ent->priv.server->free)
1813 if (trace.fraction < 1)
1815 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1817 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1818 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1820 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1823 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1824 // LordHavoc: fixed grenades not bouncing when fired down a slope
1825 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1827 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1828 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1830 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1831 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1832 VectorClear (ent->fields.server->velocity);
1833 VectorClear (ent->fields.server->avelocity);
1836 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1840 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1842 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1843 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1844 VectorClear (ent->fields.server->velocity);
1845 VectorClear (ent->fields.server->avelocity);
1848 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1853 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1854 if (trace.plane.normal[2] > 0.7)
1856 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1857 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1858 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1859 ent->priv.server->suspendedinairflag = true;
1860 VectorClear (ent->fields.server->velocity);
1861 VectorClear (ent->fields.server->avelocity);
1864 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1868 // check for in water
1869 SV_CheckWaterTransition (ent);
1873 ===============================================================================
1877 ===============================================================================
1884 Monsters freefall when they don't have a ground entity, otherwise
1885 all movement is done with discrete steps.
1887 This is also used for objects that have become still on the ground, but
1888 will fall if the floor is pulled out from under them.
1891 void SV_Physics_Step (prvm_edict_t *ent)
1893 int flags = (int)ent->fields.server->flags;
1894 // don't fall at all if fly/swim
1895 if (!(flags & (FL_FLY | FL_SWIM)))
1897 if (flags & FL_ONGROUND)
1899 // freefall if onground and moving upward
1900 // freefall if not standing on a world surface (it may be a lift or trap door)
1901 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1903 ent->fields.server->flags -= FL_ONGROUND;
1905 SV_CheckVelocity(ent);
1906 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1907 SV_LinkEdict(ent, true);
1912 // freefall if not onground
1913 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1916 SV_CheckVelocity(ent);
1917 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1918 SV_LinkEdict(ent, true);
1921 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1922 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1929 SV_CheckWaterTransition(ent);
1932 //============================================================================
1934 static void SV_Physics_Entity (prvm_edict_t *ent)
1936 // don't run a move on newly spawned projectiles as it messes up movement
1937 // interpolation and rocket trails
1938 qboolean runmove = ent->priv.server->move;
1939 ent->priv.server->move = true;
1940 switch ((int) ent->fields.server->movetype)
1943 case MOVETYPE_FAKEPUSH:
1944 SV_Physics_Pusher (ent);
1947 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1948 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1951 case MOVETYPE_FOLLOW:
1952 SV_Physics_Follow (ent);
1954 case MOVETYPE_NOCLIP:
1955 if (SV_RunThink(ent))
1958 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1959 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1961 SV_LinkEdict(ent, false);
1964 SV_Physics_Step (ent);
1967 if (SV_RunThink (ent))
1969 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1970 SV_AddGravity (ent);
1971 SV_CheckStuck (ent);
1973 SV_LinkEdict (ent, true);
1977 case MOVETYPE_BOUNCE:
1978 case MOVETYPE_BOUNCEMISSILE:
1979 case MOVETYPE_FLYMISSILE:
1982 if (SV_RunThink (ent) && runmove)
1983 SV_Physics_Toss (ent);
1986 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1991 void SV_Physics_ClientMove(void)
1994 ent = host_client->edict;
1996 // call player physics, this needs the proper frametime
1997 prog->globals.server->frametime = sv.frametime;
2000 // call standard client pre-think, with frametime = 0
2001 prog->globals.server->time = sv.time;
2002 prog->globals.server->frametime = 0;
2003 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2004 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2005 prog->globals.server->frametime = sv.frametime;
2007 // make sure the velocity is sane (not a NaN)
2008 SV_CheckVelocity(ent);
2009 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2010 // player_run/player_stand1 does not horribly malfunction if the
2011 // velocity becomes a number that is both == 0 and != 0
2012 // (sounds to me like NaN but to be absolutely safe...)
2013 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2014 VectorClear(ent->fields.server->velocity);
2016 // perform MOVETYPE_WALK behavior
2017 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2018 SV_AddGravity (ent);
2019 SV_CheckStuck (ent);
2022 SV_CheckVelocity (ent);
2024 SV_LinkEdict (ent, true);
2026 SV_CheckVelocity (ent);
2028 // call standard player post-think, with frametime = 0
2029 prog->globals.server->time = sv.time;
2030 prog->globals.server->frametime = 0;
2031 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2032 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2033 prog->globals.server->frametime = sv.frametime;
2035 if(ent->fields.server->fixangle)
2037 // angle fixing was requested by physics code...
2038 // so store the current angles for later use
2039 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2040 host_client->fixangle_angles_set = TRUE;
2042 // and clear fixangle for the next frame
2043 ent->fields.server->fixangle = 0;
2047 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2049 // don't do physics on disconnected clients, FrikBot relies on this
2050 if (!host_client->spawned)
2052 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2056 // don't run physics here if running asynchronously
2057 if (host_client->clmovement_skipphysicsframes <= 0)
2060 // make sure the velocity is sane (not a NaN)
2061 SV_CheckVelocity(ent);
2062 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2063 // player_run/player_stand1 does not horribly malfunction if the
2064 // velocity becomes a number that is both == 0 and != 0
2065 // (sounds to me like NaN but to be absolutely safe...)
2066 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2067 VectorClear(ent->fields.server->velocity);
2069 // call standard client pre-think
2070 prog->globals.server->time = sv.time;
2071 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2072 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2073 SV_CheckVelocity (ent);
2075 switch ((int) ent->fields.server->movetype)
2078 case MOVETYPE_FAKEPUSH:
2079 SV_Physics_Pusher (ent);
2082 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2083 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2086 case MOVETYPE_FOLLOW:
2087 SV_Physics_Follow (ent);
2089 case MOVETYPE_NOCLIP:
2092 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2093 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2096 SV_Physics_Step (ent);
2100 // don't run physics here if running asynchronously
2101 if (host_client->clmovement_skipphysicsframes <= 0)
2103 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2104 SV_AddGravity (ent);
2105 SV_CheckStuck (ent);
2110 case MOVETYPE_BOUNCE:
2111 case MOVETYPE_BOUNCEMISSILE:
2112 case MOVETYPE_FLYMISSILE:
2115 SV_Physics_Toss (ent);
2119 SV_CheckWater (ent);
2123 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2127 // decrement the countdown variable used to decide when to go back to
2128 // synchronous physics
2129 if (host_client->clmovement_skipphysicsframes > 0)
2130 host_client->clmovement_skipphysicsframes--;
2132 SV_CheckVelocity (ent);
2134 SV_LinkEdict (ent, true);
2136 SV_CheckVelocity (ent);
2138 // call standard player post-think
2139 prog->globals.server->time = sv.time;
2140 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2141 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2143 if(ent->fields.server->fixangle)
2145 // angle fixing was requested by physics code...
2146 // so store the current angles for later use
2147 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2148 host_client->fixangle_angles_set = TRUE;
2150 // and clear fixangle for the next frame
2151 ent->fields.server->fixangle = 0;
2161 void SV_Physics (void)
2166 // let the progs know that a new frame has started
2167 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2168 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2169 prog->globals.server->time = sv.time;
2170 prog->globals.server->frametime = sv.frametime;
2171 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2174 // treat each object in turn
2177 // if force_retouch, relink all the entities
2178 if (prog->globals.server->force_retouch > 0)
2179 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2180 if (!ent->priv.server->free)
2181 SV_LinkEdict (ent, true); // force retouch even for stationary
2183 // run physics on the client entities
2184 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2185 if (!ent->priv.server->free)
2186 SV_Physics_ClientEntity(ent);
2188 // run physics on all the non-client entities
2189 if (!sv_freezenonclients.integer)
2190 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2191 if (!ent->priv.server->free)
2192 SV_Physics_Entity(ent);
2194 if (prog->globals.server->force_retouch > 0)
2195 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2197 // LordHavoc: endframe support
2198 if (prog->funcoffsets.EndFrame)
2200 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2201 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2202 prog->globals.server->time = sv.time;
2203 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2206 // decrement prog->num_edicts if the highest number entities died
2207 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2209 if (!sv_freezenonclients.integer)
2210 sv.time += sv.frametime;