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 extern sizebuf_t vm_tempstringsbuf;
614 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
616 int restorevm_tempstringsbuf_cursize;
617 int old_self, old_other;
618 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
621 old_self = prog->globals.server->self;
622 old_other = prog->globals.server->other;
623 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
625 VM_SetTraceGlobals(trace);
627 prog->globals.server->time = sv.time;
628 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
630 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
631 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
632 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
635 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
637 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
638 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
639 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
640 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
641 prog->globals.server->trace_plane_dist = -trace->plane.dist;
642 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
643 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
645 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
647 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
649 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
651 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
654 prog->globals.server->self = old_self;
655 prog->globals.server->other = old_other;
656 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
664 Slide off of the impacting object
665 returns the blocked flags (1 = floor, 2 = step / wall)
668 #define STOP_EPSILON 0.1
669 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
674 backoff = -DotProduct (in, normal) * overbounce;
675 VectorMA(in, backoff, normal, out);
677 for (i = 0;i < 3;i++)
678 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
687 The basic solid body movement clip that slides along multiple planes
688 Returns the clipflags if the velocity was modified (hit something solid)
692 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
695 // LordHavoc: increased from 5 to 32
696 #define MAX_CLIP_PLANES 32
697 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
699 int blocked, bumpcount;
700 int i, j, impact, numplanes;
702 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
707 VectorCopy(ent->fields.server->velocity, original_velocity);
708 VectorCopy(ent->fields.server->velocity, primal_velocity);
711 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
713 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
716 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
717 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
719 //if (trace.fraction < 0.002)
724 VectorCopy(ent->fields.server->origin, start);
725 start[2] += 3;//0.03125;
726 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
727 end[2] += 3;//0.03125;
728 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
729 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)))
731 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
737 for (i = 0;i < numplanes;i++)
739 VectorCopy(ent->fields.server->origin, start);
740 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
741 VectorMA(start, 3, planes[i], start);
742 VectorMA(end, 3, planes[i], end);
743 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
744 if (trace.fraction < testtrace.fraction)
747 VectorCopy(start, ent->fields.server->origin);
752 // VectorAdd(ent->fields.server->origin, planes[j], start);
758 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);
759 if (trace.fraction < 1)
760 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
765 if (trace.bmodelstartsolid)
767 // LordHavoc: note: this code is what makes entities stick in place
768 // if embedded in world only (you can walk through other objects if
770 // entity is trapped in another solid
771 VectorClear(ent->fields.server->velocity);
776 // break if it moved the entire distance
777 if (trace.fraction == 1)
779 VectorCopy(trace.endpos, ent->fields.server->origin);
785 Con_Printf ("SV_FlyMove: !trace.ent");
786 trace.ent = prog->edicts;
789 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
791 if (trace.plane.normal[2])
793 if (trace.plane.normal[2] > 0.7)
797 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
798 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
805 // save the trace for player extrafriction
807 VectorCopy(trace.plane.normal, stepnormal);
810 if (trace.fraction >= 0.001)
812 // actually covered some distance
813 VectorCopy(trace.endpos, ent->fields.server->origin);
814 VectorCopy(ent->fields.server->velocity, original_velocity);
818 // run the impact function
821 SV_Impact(ent, &trace);
823 // break if removed by the impact function
824 if (ent->priv.server->free)
828 time_left *= 1 - trace.fraction;
830 // clipped to another plane
831 if (numplanes >= MAX_CLIP_PLANES)
833 // this shouldn't really happen
834 VectorClear(ent->fields.server->velocity);
840 for (i = 0;i < numplanes;i++)
841 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
845 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
850 VectorCopy(trace.plane.normal, planes[numplanes]);
853 if (sv_newflymove.integer)
854 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
857 // modify original_velocity so it parallels all of the clip planes
858 for (i = 0;i < numplanes;i++)
860 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
861 for (j = 0;j < numplanes;j++)
866 if (DotProduct(new_velocity, planes[j]) < 0)
876 // go along this plane
877 VectorCopy(new_velocity, ent->fields.server->velocity);
881 // go along the crease
884 VectorClear(ent->fields.server->velocity);
888 CrossProduct(planes[0], planes[1], dir);
889 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
890 VectorNormalize(dir);
891 d = DotProduct(dir, ent->fields.server->velocity);
892 VectorScale(dir, d, ent->fields.server->velocity);
896 // if current velocity is against the original velocity,
897 // stop dead to avoid tiny occilations in sloping corners
898 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
900 VectorClear(ent->fields.server->velocity);
905 //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]);
908 if ((blocked & 1) == 0 && bumpcount > 1)
910 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
911 // flag ONGROUND if there's ground under it
912 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
916 // LordHavoc: this came from QW and allows you to get out of water more easily
917 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
918 VectorCopy(primal_velocity, ent->fields.server->velocity);
928 void SV_AddGravity (prvm_edict_t *ent)
933 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
934 if (val!=0 && val->_float)
935 ent_gravity = val->_float;
938 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
943 ===============================================================================
947 ===============================================================================
954 Does not change the entities velocity at all
957 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
963 VectorAdd (ent->fields.server->origin, push, end);
965 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
967 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
968 type = MOVE_NOMONSTERS; // only clip against bmodels
972 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
973 if (trace.bmodelstartsolid && failonbmodelstartsolid)
976 VectorCopy (trace.endpos, ent->fields.server->origin);
977 SV_LinkEdict (ent, true);
979 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)))
980 SV_Impact (ent, &trace);
991 void SV_PushMove (prvm_edict_t *pusher, float movetime)
994 float savesolid, movetime2, pushltime;
995 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
997 int numcheckentities;
998 static prvm_edict_t *checkentities[MAX_EDICTS];
999 model_t *pushermodel;
1001 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1002 unsigned short moved_edicts[MAX_EDICTS];
1004 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])
1006 pusher->fields.server->ltime += movetime;
1010 switch ((int) pusher->fields.server->solid)
1012 // LordHavoc: valid pusher types
1015 case SOLID_SLIDEBOX:
1016 case SOLID_CORPSE: // LordHavoc: this would be weird...
1018 // LordHavoc: no collisions
1021 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1022 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1023 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1024 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1025 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1026 pusher->fields.server->ltime += movetime;
1027 SV_LinkEdict (pusher, false);
1030 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1033 index = (int) pusher->fields.server->modelindex;
1034 if (index < 1 || index >= MAX_MODELS)
1036 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1039 pushermodel = sv.models[index];
1041 movetime2 = movetime;
1042 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1043 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1044 if (moveangle[0] || moveangle[2])
1046 for (i = 0;i < 3;i++)
1050 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1051 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1055 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1056 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1060 else if (moveangle[1])
1062 for (i = 0;i < 3;i++)
1066 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1067 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1071 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1072 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1078 for (i = 0;i < 3;i++)
1082 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1083 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1087 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1088 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1093 VectorNegate (moveangle, a);
1094 AngleVectorsFLU (a, forward, left, up);
1096 VectorCopy (pusher->fields.server->origin, pushorig);
1097 VectorCopy (pusher->fields.server->angles, pushang);
1098 pushltime = pusher->fields.server->ltime;
1100 // move the pusher to its final position
1102 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1103 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1104 pusher->fields.server->ltime += movetime;
1105 SV_LinkEdict (pusher, false);
1108 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1109 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1110 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);
1111 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1113 savesolid = pusher->fields.server->solid;
1115 // see if any solid entities are inside the final position
1118 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1119 for (e = 0;e < numcheckentities;e++)
1121 prvm_edict_t *check = checkentities[e];
1122 int checkcontents = SV_GenericHitSuperContentsMask(check);
1123 if (check->fields.server->movetype == MOVETYPE_NONE
1124 || check->fields.server->movetype == MOVETYPE_PUSH
1125 || check->fields.server->movetype == MOVETYPE_FOLLOW
1126 || check->fields.server->movetype == MOVETYPE_NOCLIP
1127 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1130 // if the entity is standing on the pusher, it will definitely be moved
1131 // if the entity is not standing on the pusher, but is in the pusher's
1132 // final position, move it
1133 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1135 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);
1136 if (!trace.startsolid)
1141 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1144 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1145 org2[0] = DotProduct (org, forward);
1146 org2[1] = DotProduct (org, left);
1147 org2[2] = DotProduct (org, up);
1148 VectorSubtract (org2, org, move);
1149 VectorAdd (move, move1, move);
1152 VectorCopy (move1, move);
1154 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1155 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1156 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1158 // try moving the contacted entity
1159 pusher->fields.server->solid = SOLID_NOT;
1160 trace = SV_PushEntity (check, move, true);
1161 // FIXME: turn players specially
1162 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1163 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1164 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1166 // this trace.fraction < 1 check causes items to fall off of pushers
1167 // if they pass under or through a wall
1168 // the groundentity check causes items to fall off of ledges
1169 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1170 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1172 // if it is still inside the pusher, block
1173 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);
1174 if (trace.startsolid)
1176 // try moving the contacted entity a tiny bit further to account for precision errors
1178 pusher->fields.server->solid = SOLID_NOT;
1179 VectorScale(move, 1.1, move2);
1180 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1181 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1182 SV_PushEntity (check, move2, true);
1183 pusher->fields.server->solid = savesolid;
1184 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);
1185 if (trace.startsolid)
1187 // try moving the contacted entity a tiny bit less to account for precision errors
1188 pusher->fields.server->solid = SOLID_NOT;
1189 VectorScale(move, 0.9, move2);
1190 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1191 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1192 SV_PushEntity (check, move2, true);
1193 pusher->fields.server->solid = savesolid;
1194 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);
1195 if (trace.startsolid)
1197 // still inside pusher, so it's really blocked
1200 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1202 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1205 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1206 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1210 VectorCopy (pushorig, pusher->fields.server->origin);
1211 VectorCopy (pushang, pusher->fields.server->angles);
1212 pusher->fields.server->ltime = pushltime;
1213 SV_LinkEdict (pusher, false);
1215 // move back any entities we already moved
1216 for (i = 0;i < num_moved;i++)
1218 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1219 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1220 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1221 SV_LinkEdict (ed, false);
1224 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1225 if (pusher->fields.server->blocked)
1227 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1228 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1229 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1236 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1237 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1238 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1247 void SV_Physics_Pusher (prvm_edict_t *ent)
1249 float thinktime, oldltime, movetime;
1251 oldltime = ent->fields.server->ltime;
1253 thinktime = ent->fields.server->nextthink;
1254 if (thinktime < ent->fields.server->ltime + sv.frametime)
1256 movetime = thinktime - ent->fields.server->ltime;
1261 movetime = sv.frametime;
1264 // advances ent->fields.server->ltime if not blocked
1265 SV_PushMove (ent, movetime);
1267 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1269 ent->fields.server->nextthink = 0;
1270 prog->globals.server->time = sv.time;
1271 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1272 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1273 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1279 ===============================================================================
1283 ===============================================================================
1286 static float unstickoffsets[] =
1320 This is a big hack to try and fix the rare case of getting stuck in the world
1324 void SV_CheckStuck (prvm_edict_t *ent)
1329 if (!SV_TestEntityPosition(ent, vec3_origin))
1331 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1335 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1337 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1339 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]);
1340 SV_LinkEdict (ent, true);
1345 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1346 if (!SV_TestEntityPosition(ent, offset))
1348 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1349 SV_LinkEdict (ent, true);
1353 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1356 static void SV_UnstickEntity (prvm_edict_t *ent)
1360 // if not stuck in a bmodel, just return
1361 if (!SV_TestEntityPosition(ent, vec3_origin))
1364 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1366 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1368 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]);
1369 SV_LinkEdict (ent, true);
1374 if (developer.integer >= 100)
1375 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1384 qboolean SV_CheckWater (prvm_edict_t *ent)
1387 int nNativeContents;
1390 point[0] = ent->fields.server->origin[0];
1391 point[1] = ent->fields.server->origin[1];
1392 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1394 // DRESK - Support for Entity Contents Transition Event
1395 // NOTE: Some logic needed to be slightly re-ordered
1396 // to not affect performance and allow for the feature.
1398 // Acquire Super Contents Prior to Resets
1399 cont = SV_PointSuperContents(point);
1400 // Acquire Native Contents Here
1401 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1403 // DRESK - Support for Entity Contents Transition Event
1404 if(ent->fields.server->watertype)
1405 // Entity did NOT Spawn; Check
1406 SV_CheckContentsTransition(ent, nNativeContents);
1409 ent->fields.server->waterlevel = 0;
1410 ent->fields.server->watertype = CONTENTS_EMPTY;
1411 cont = SV_PointSuperContents(point);
1412 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1414 ent->fields.server->watertype = nNativeContents;
1415 ent->fields.server->waterlevel = 1;
1416 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1417 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1419 ent->fields.server->waterlevel = 2;
1420 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1421 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1422 ent->fields.server->waterlevel = 3;
1426 return ent->fields.server->waterlevel > 1;
1435 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1438 vec3_t forward, into, side;
1440 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1441 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1443 // cut the tangential velocity
1444 i = DotProduct (stepnormal, ent->fields.server->velocity);
1445 VectorScale (stepnormal, i, into);
1446 VectorSubtract (ent->fields.server->velocity, into, side);
1447 ent->fields.server->velocity[0] = side[0] * (1 + d);
1448 ent->fields.server->velocity[1] = side[1] * (1 + d);
1454 =====================
1457 Player has come to a dead stop, possibly due to the problem with limited
1458 float precision at some angle joins in the BSP hull.
1460 Try fixing by pushing one pixel in each direction.
1462 This is a hack, but in the interest of good gameplay...
1463 ======================
1465 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1470 VectorCopy (ent->fields.server->origin, oldorg);
1473 for (i=0 ; i<8 ; i++)
1475 // try pushing a little in an axial direction
1478 case 0: dir[0] = 2; dir[1] = 0; break;
1479 case 1: dir[0] = 0; dir[1] = 2; break;
1480 case 2: dir[0] = -2; dir[1] = 0; break;
1481 case 3: dir[0] = 0; dir[1] = -2; break;
1482 case 4: dir[0] = 2; dir[1] = 2; break;
1483 case 5: dir[0] = -2; dir[1] = 2; break;
1484 case 6: dir[0] = 2; dir[1] = -2; break;
1485 case 7: dir[0] = -2; dir[1] = -2; break;
1488 SV_PushEntity (ent, dir, false);
1490 // retry the original move
1491 ent->fields.server->velocity[0] = oldvel[0];
1492 ent->fields.server->velocity[1] = oldvel[1];
1493 ent->fields.server->velocity[2] = 0;
1494 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1496 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1497 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1499 Con_DPrint("TryUnstick - success.\n");
1503 // go back to the original pos and try again
1504 VectorCopy (oldorg, ent->fields.server->origin);
1508 VectorClear (ent->fields.server->velocity);
1509 Con_DPrint("TryUnstick - failure.\n");
1515 =====================
1518 Only used by players
1519 ======================
1521 void SV_WalkMove (prvm_edict_t *ent)
1523 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1524 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1527 // if frametime is 0 (due to client sending the same timestamp twice),
1529 if (sv.frametime <= 0)
1532 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1534 SV_CheckVelocity(ent);
1536 // do a regular slide move unless it looks like you ran into a step
1537 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1539 VectorCopy (ent->fields.server->origin, start_origin);
1540 VectorCopy (ent->fields.server->velocity, start_velocity);
1542 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1544 // if the move did not hit the ground at any point, we're not on ground
1546 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1548 SV_CheckVelocity(ent);
1550 VectorCopy(ent->fields.server->origin, originalmove_origin);
1551 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1552 originalmove_clip = clip;
1553 originalmove_flags = (int)ent->fields.server->flags;
1554 originalmove_groundentity = ent->fields.server->groundentity;
1556 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1559 if (sv_nostep.integer)
1562 // if move didn't block on a step, return
1565 // if move was not trying to move into the step, return
1566 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1569 if (ent->fields.server->movetype != MOVETYPE_FLY)
1571 // return if gibbed by a trigger
1572 if (ent->fields.server->movetype != MOVETYPE_WALK)
1575 // only step up while jumping if that is enabled
1576 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1577 if (!oldonground && ent->fields.server->waterlevel == 0)
1581 // try moving up and forward to go up a step
1582 // back to start pos
1583 VectorCopy (start_origin, ent->fields.server->origin);
1584 VectorCopy (start_velocity, ent->fields.server->velocity);
1587 VectorClear (upmove);
1588 upmove[2] = sv_stepheight.value;
1589 // FIXME: don't link?
1590 SV_PushEntity(ent, upmove, false);
1593 ent->fields.server->velocity[2] = 0;
1594 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1595 ent->fields.server->velocity[2] += start_velocity[2];
1597 SV_CheckVelocity(ent);
1599 // check for stuckness, possibly due to the limited precision of floats
1600 // in the clipping hulls
1602 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1603 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1605 //Con_Printf("wall\n");
1606 // stepping up didn't make any progress, revert to original move
1607 VectorCopy(originalmove_origin, ent->fields.server->origin);
1608 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1609 //clip = originalmove_clip;
1610 ent->fields.server->flags = originalmove_flags;
1611 ent->fields.server->groundentity = originalmove_groundentity;
1612 // now try to unstick if needed
1613 //clip = SV_TryUnstick (ent, oldvel);
1617 //Con_Printf("step - ");
1619 // extra friction based on view angle
1620 if (clip & 2 && sv_wallfriction.integer)
1621 SV_WallFriction (ent, stepnormal);
1623 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1624 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))
1628 VectorClear (downmove);
1629 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1630 // FIXME: don't link?
1631 downtrace = SV_PushEntity (ent, downmove, false);
1633 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1635 // this has been disabled so that you can't jump when you are stepping
1636 // up while already jumping (also known as the Quake2 double jump bug)
1638 // LordHavoc: disabled this check so you can walk on monsters/players
1639 //if (ent->fields.server->solid == SOLID_BSP)
1641 //Con_Printf("onground\n");
1642 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1643 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1649 //Con_Printf("slope\n");
1650 // if the push down didn't end up on good ground, use the move without
1651 // the step up. This happens near wall / slope combinations, and can
1652 // cause the player to hop up higher on a slope too steep to climb
1653 VectorCopy(originalmove_origin, ent->fields.server->origin);
1654 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1655 //clip = originalmove_clip;
1656 ent->fields.server->flags = originalmove_flags;
1657 ent->fields.server->groundentity = originalmove_groundentity;
1660 SV_CheckVelocity(ent);
1663 //============================================================================
1669 Entities that are "stuck" to another entity
1672 void SV_Physics_Follow (prvm_edict_t *ent)
1674 vec3_t vf, vr, vu, angles, v;
1678 if (!SV_RunThink (ent))
1681 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1682 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1683 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])
1685 // quick case for no rotation
1686 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1690 angles[0] = -ent->fields.server->punchangle[0];
1691 angles[1] = ent->fields.server->punchangle[1];
1692 angles[2] = ent->fields.server->punchangle[2];
1693 AngleVectors (angles, vf, vr, vu);
1694 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];
1695 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];
1696 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];
1697 angles[0] = -e->fields.server->angles[0];
1698 angles[1] = e->fields.server->angles[1];
1699 angles[2] = e->fields.server->angles[2];
1700 AngleVectors (angles, vf, vr, vu);
1701 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1702 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1703 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1705 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1706 SV_LinkEdict (ent, true);
1710 ==============================================================================
1714 ==============================================================================
1719 SV_CheckWaterTransition
1723 void SV_CheckWaterTransition (prvm_edict_t *ent)
1726 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1727 if (!ent->fields.server->watertype)
1729 // just spawned here
1730 ent->fields.server->watertype = cont;
1731 ent->fields.server->waterlevel = 1;
1735 // DRESK - Support for Entity Contents Transition Event
1736 // NOTE: Call here BEFORE updating the watertype below,
1737 // and suppress watersplash sound if a valid function
1738 // call was made to allow for custom "splash" sounds.
1739 if( !SV_CheckContentsTransition(ent, cont) )
1740 { // Contents Transition Function Invalid; Potentially Play Water Sound
1741 // check if the entity crossed into or out of water
1742 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1743 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1746 if (cont <= CONTENTS_WATER)
1748 ent->fields.server->watertype = cont;
1749 ent->fields.server->waterlevel = 1;
1753 ent->fields.server->watertype = CONTENTS_EMPTY;
1754 ent->fields.server->waterlevel = 0;
1762 Toss, bounce, and fly movement. When onground, do nothing.
1765 void SV_Physics_Toss (prvm_edict_t *ent)
1770 // if onground, return without moving
1771 if ((int)ent->fields.server->flags & FL_ONGROUND)
1773 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1775 // don't stick to ground if onground and moving upward
1776 ent->fields.server->flags -= FL_ONGROUND;
1778 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1780 // we can trust FL_ONGROUND if groundentity is world because it never moves
1783 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1785 // if ent was supported by a brush model on previous frame,
1786 // and groundentity is now freed, set groundentity to 0 (world)
1787 // which leaves it suspended in the air
1788 ent->fields.server->groundentity = 0;
1792 ent->priv.server->suspendedinairflag = false;
1794 SV_CheckVelocity (ent);
1797 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1798 SV_AddGravity (ent);
1801 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1804 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1805 trace = SV_PushEntity (ent, move, true);
1806 if (ent->priv.server->free)
1808 if (trace.bmodelstartsolid)
1810 // try to unstick the entity
1811 SV_UnstickEntity(ent);
1812 trace = SV_PushEntity (ent, move, false);
1813 if (ent->priv.server->free)
1817 if (trace.fraction < 1)
1819 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1821 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1822 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1824 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1827 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1828 // LordHavoc: fixed grenades not bouncing when fired down a slope
1829 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1831 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1832 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1834 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1835 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1836 VectorClear (ent->fields.server->velocity);
1837 VectorClear (ent->fields.server->avelocity);
1840 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1844 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1846 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1847 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1848 VectorClear (ent->fields.server->velocity);
1849 VectorClear (ent->fields.server->avelocity);
1852 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1857 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1858 if (trace.plane.normal[2] > 0.7)
1860 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1861 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1862 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1863 ent->priv.server->suspendedinairflag = true;
1864 VectorClear (ent->fields.server->velocity);
1865 VectorClear (ent->fields.server->avelocity);
1868 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1872 // check for in water
1873 SV_CheckWaterTransition (ent);
1877 ===============================================================================
1881 ===============================================================================
1888 Monsters freefall when they don't have a ground entity, otherwise
1889 all movement is done with discrete steps.
1891 This is also used for objects that have become still on the ground, but
1892 will fall if the floor is pulled out from under them.
1895 void SV_Physics_Step (prvm_edict_t *ent)
1897 int flags = (int)ent->fields.server->flags;
1898 // don't fall at all if fly/swim
1899 if (!(flags & (FL_FLY | FL_SWIM)))
1901 if (flags & FL_ONGROUND)
1903 // freefall if onground and moving upward
1904 // freefall if not standing on a world surface (it may be a lift or trap door)
1905 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1907 ent->fields.server->flags -= FL_ONGROUND;
1909 SV_CheckVelocity(ent);
1910 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1911 SV_LinkEdict(ent, true);
1916 // freefall if not onground
1917 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1920 SV_CheckVelocity(ent);
1921 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1922 SV_LinkEdict(ent, true);
1925 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1926 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1933 SV_CheckWaterTransition(ent);
1936 //============================================================================
1938 static void SV_Physics_Entity (prvm_edict_t *ent)
1940 // don't run a move on newly spawned projectiles as it messes up movement
1941 // interpolation and rocket trails
1942 qboolean runmove = ent->priv.server->move;
1943 ent->priv.server->move = true;
1944 switch ((int) ent->fields.server->movetype)
1947 case MOVETYPE_FAKEPUSH:
1948 SV_Physics_Pusher (ent);
1951 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1952 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1955 case MOVETYPE_FOLLOW:
1956 SV_Physics_Follow (ent);
1958 case MOVETYPE_NOCLIP:
1959 if (SV_RunThink(ent))
1962 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1963 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1965 SV_LinkEdict(ent, false);
1968 SV_Physics_Step (ent);
1971 if (SV_RunThink (ent))
1973 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1974 SV_AddGravity (ent);
1975 SV_CheckStuck (ent);
1977 SV_LinkEdict (ent, true);
1981 case MOVETYPE_BOUNCE:
1982 case MOVETYPE_BOUNCEMISSILE:
1983 case MOVETYPE_FLYMISSILE:
1986 if (SV_RunThink (ent) && runmove)
1987 SV_Physics_Toss (ent);
1990 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1995 void SV_Physics_ClientMove(void)
1998 ent = host_client->edict;
2000 // call player physics, this needs the proper frametime
2001 prog->globals.server->frametime = sv.frametime;
2004 // call standard client pre-think, with frametime = 0
2005 prog->globals.server->time = sv.time;
2006 prog->globals.server->frametime = 0;
2007 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2008 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2009 prog->globals.server->frametime = sv.frametime;
2011 // make sure the velocity is sane (not a NaN)
2012 SV_CheckVelocity(ent);
2013 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2014 // player_run/player_stand1 does not horribly malfunction if the
2015 // velocity becomes a number that is both == 0 and != 0
2016 // (sounds to me like NaN but to be absolutely safe...)
2017 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2018 VectorClear(ent->fields.server->velocity);
2020 // perform MOVETYPE_WALK behavior
2021 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2022 SV_AddGravity (ent);
2023 SV_CheckStuck (ent);
2026 SV_CheckVelocity (ent);
2028 SV_LinkEdict (ent, true);
2030 SV_CheckVelocity (ent);
2032 // call standard player post-think, with frametime = 0
2033 prog->globals.server->time = sv.time;
2034 prog->globals.server->frametime = 0;
2035 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2036 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2037 prog->globals.server->frametime = sv.frametime;
2039 if(ent->fields.server->fixangle)
2041 // angle fixing was requested by physics code...
2042 // so store the current angles for later use
2043 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2044 host_client->fixangle_angles_set = TRUE;
2046 // and clear fixangle for the next frame
2047 ent->fields.server->fixangle = 0;
2051 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2053 // don't do physics on disconnected clients, FrikBot relies on this
2054 if (!host_client->spawned)
2056 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2060 // don't run physics here if running asynchronously
2061 if (host_client->clmovement_skipphysicsframes <= 0)
2064 // make sure the velocity is sane (not a NaN)
2065 SV_CheckVelocity(ent);
2066 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2067 // player_run/player_stand1 does not horribly malfunction if the
2068 // velocity becomes a number that is both == 0 and != 0
2069 // (sounds to me like NaN but to be absolutely safe...)
2070 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2071 VectorClear(ent->fields.server->velocity);
2073 // call standard client pre-think
2074 prog->globals.server->time = sv.time;
2075 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2076 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2077 SV_CheckVelocity (ent);
2079 switch ((int) ent->fields.server->movetype)
2082 case MOVETYPE_FAKEPUSH:
2083 SV_Physics_Pusher (ent);
2086 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2087 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2090 case MOVETYPE_FOLLOW:
2091 SV_Physics_Follow (ent);
2093 case MOVETYPE_NOCLIP:
2096 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2097 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2100 SV_Physics_Step (ent);
2104 // don't run physics here if running asynchronously
2105 if (host_client->clmovement_skipphysicsframes <= 0)
2107 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2108 SV_AddGravity (ent);
2109 SV_CheckStuck (ent);
2114 case MOVETYPE_BOUNCE:
2115 case MOVETYPE_BOUNCEMISSILE:
2116 case MOVETYPE_FLYMISSILE:
2119 SV_Physics_Toss (ent);
2123 SV_CheckWater (ent);
2127 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2131 // decrement the countdown variable used to decide when to go back to
2132 // synchronous physics
2133 if (host_client->clmovement_skipphysicsframes > 0)
2134 host_client->clmovement_skipphysicsframes--;
2136 SV_CheckVelocity (ent);
2138 SV_LinkEdict (ent, true);
2140 SV_CheckVelocity (ent);
2142 // call standard player post-think
2143 prog->globals.server->time = sv.time;
2144 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2145 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2147 if(ent->fields.server->fixangle)
2149 // angle fixing was requested by physics code...
2150 // so store the current angles for later use
2151 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2152 host_client->fixangle_angles_set = TRUE;
2154 // and clear fixangle for the next frame
2155 ent->fields.server->fixangle = 0;
2165 void SV_Physics (void)
2170 // let the progs know that a new frame has started
2171 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2172 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2173 prog->globals.server->time = sv.time;
2174 prog->globals.server->frametime = sv.frametime;
2175 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2178 // treat each object in turn
2181 // if force_retouch, relink all the entities
2182 if (prog->globals.server->force_retouch > 0)
2183 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2184 if (!ent->priv.server->free)
2185 SV_LinkEdict (ent, true); // force retouch even for stationary
2187 // run physics on the client entities
2188 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2189 if (!ent->priv.server->free)
2190 SV_Physics_ClientEntity(ent);
2192 // run physics on all the non-client entities
2193 if (!sv_freezenonclients.integer)
2194 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2195 if (!ent->priv.server->free)
2196 SV_Physics_Entity(ent);
2198 if (prog->globals.server->force_retouch > 0)
2199 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2201 // LordHavoc: endframe support
2202 if (prog->funcoffsets.EndFrame)
2204 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2205 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2206 prog->globals.server->time = sv.time;
2207 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2210 // decrement prog->num_edicts if the highest number entities died
2211 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2213 if (!sv_freezenonclients.integer)
2214 sv.time += sv.frametime;