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 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
43 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
44 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
45 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
46 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
47 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
48 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
49 cvar_t sv_debugmove = {CVAR_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"};
51 cvar_t sv_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"};
52 cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
54 // TODO: move this extern to server.h
55 extern cvar_t sv_clmovement_waitforinput;
57 #define MOVE_EPSILON 0.01
59 void SV_Physics_Toss (prvm_edict_t *ent);
61 void SV_Phys_Init (void)
63 Cvar_RegisterVariable(&sv_stepheight);
64 Cvar_RegisterVariable(&sv_jumpstep);
65 Cvar_RegisterVariable(&sv_wallfriction);
66 Cvar_RegisterVariable(&sv_newflymove);
67 Cvar_RegisterVariable(&sv_freezenonclients);
68 Cvar_RegisterVariable(&sv_playerphysicsqc);
69 Cvar_RegisterVariable(&sv_debugmove);
71 Cvar_RegisterVariable(&sv_sound_watersplash);
72 Cvar_RegisterVariable(&sv_sound_land);
76 ===============================================================================
80 ===============================================================================
83 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
88 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
89 if (val && val->_float)
90 return (int)val->_float;
91 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
93 if ((int)passedict->fields.server->flags & FL_MONSTER)
94 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
96 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
98 else if (passedict->fields.server->solid == SOLID_CORPSE)
99 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
100 else if (passedict->fields.server->solid == SOLID_TRIGGER)
101 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
103 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
106 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
114 #if COLLISIONPARANOID >= 1
115 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)
117 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)
120 vec3_t hullmins, hullmaxs;
121 int i, bodysupercontents;
124 prvm_edict_t *traceowner, *touch;
126 // bounding box of entire move area
127 vec3_t clipboxmins, clipboxmaxs;
128 // size of the moving object
129 vec3_t clipmins, clipmaxs;
130 // size when clipping against monsters
131 vec3_t clipmins2, clipmaxs2;
132 // start and end origin of move
133 vec3_t clipstart, clipend;
136 // matrices to transform into/out of other entity's space
137 matrix4x4_t matrix, imatrix;
138 // model of other entity
140 // list of entities to test for collisions
142 prvm_edict_t *touchedicts[MAX_EDICTS];
144 VectorCopy(start, clipstart);
145 VectorCopy(end, clipend);
146 VectorCopy(mins, clipmins);
147 VectorCopy(maxs, clipmaxs);
148 VectorCopy(mins, clipmins2);
149 VectorCopy(maxs, clipmaxs2);
150 #if COLLISIONPARANOID >= 3
151 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
155 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
156 cliptrace.bmodelstartsolid = cliptrace.startsolid;
157 if (cliptrace.startsolid || cliptrace.fraction < 1)
158 cliptrace.ent = prog->edicts;
159 if (type == MOVE_WORLDONLY)
162 if (type == MOVE_MISSILE)
164 // LordHavoc: modified this, was = -15, now -= 15
165 for (i = 0;i < 3;i++)
172 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
173 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
174 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
177 VectorCopy(clipmins, hullmins);
178 VectorCopy(clipmaxs, hullmaxs);
181 // create the bounding box of the entire move
182 for (i = 0;i < 3;i++)
184 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
185 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
188 // debug override to test against everything
189 if (sv_debugmove.integer)
191 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
192 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
195 // if the passedict is world, make it NULL (to avoid two checks each time)
196 if (passedict == prog->edicts)
198 // precalculate prog value for passedict for comparisons
199 passedictprog = PRVM_EDICT_TO_PROG(passedict);
200 // figure out whether this is a point trace for comparisons
201 pointtrace = VectorCompare(clipmins, clipmaxs);
202 // precalculate passedict's owner edict pointer for comparisons
203 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
206 // because this uses World_EntitiestoBox, we know all entity boxes overlap
207 // the clip region, so we can skip culling checks in the loop below
208 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
209 if (numtouchedicts > MAX_EDICTS)
211 // this never happens
212 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
213 numtouchedicts = MAX_EDICTS;
215 for (i = 0;i < numtouchedicts;i++)
217 touch = touchedicts[i];
219 if (touch->fields.server->solid < SOLID_BBOX)
221 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
226 // don't clip against self
227 if (passedict == touch)
229 // don't clip owned entities against owner
230 if (traceowner == touch)
232 // don't clip owner against owned entities
233 if (passedictprog == touch->fields.server->owner)
235 // don't clip points against points (they can't collide)
236 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
240 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
242 // might interact, so do an exact clip
244 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
246 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
247 // if the modelindex is 0, it shouldn't be SOLID_BSP!
248 if (modelindex > 0 && modelindex < MAX_MODELS)
249 model = sv.models[(int)touch->fields.server->modelindex];
252 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);
254 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
255 Matrix4x4_Invert_Simple(&imatrix, &matrix);
256 if ((int)touch->fields.server->flags & FL_MONSTER)
257 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
259 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
261 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
267 #if COLLISIONPARANOID >= 1
268 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)
273 trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
276 VectorCopy(trace.endpos, temp);
277 endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
278 #if COLLISIONPARANOID < 3
279 if (trace.startsolid || endstuck)
281 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" : "");
288 ===============================================================================
290 Linking entities into the world culling system
292 ===============================================================================
295 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
297 int i, numtouchedicts, old_self, old_other;
298 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
300 // build a list of edicts to touch, because the link loop can be corrupted
301 // by IncreaseEdicts called during touch functions
302 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
303 if (numtouchedicts > MAX_EDICTS)
305 // this never happens
306 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
307 numtouchedicts = MAX_EDICTS;
310 old_self = prog->globals.server->self;
311 old_other = prog->globals.server->other;
312 for (i = 0;i < numtouchedicts;i++)
314 touch = touchedicts[i];
315 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
318 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
319 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
320 prog->globals.server->time = sv.time;
321 prog->globals.server->trace_allsolid = false;
322 prog->globals.server->trace_startsolid = false;
323 prog->globals.server->trace_fraction = 1;
324 prog->globals.server->trace_inwater = false;
325 prog->globals.server->trace_inopen = true;
326 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
327 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
328 prog->globals.server->trace_plane_dist = 0;
329 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
330 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
332 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
334 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
336 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
338 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
341 prog->globals.server->self = old_self;
342 prog->globals.server->other = old_other;
351 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
356 if (ent == prog->edicts)
357 return; // don't add the world
359 if (ent->priv.server->free)
364 if (ent->fields.server->solid == SOLID_BSP)
366 int modelindex = (int)ent->fields.server->modelindex;
367 if (modelindex < 0 || modelindex > MAX_MODELS)
369 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
372 model = sv.models[modelindex];
375 if (!model->TraceBox)
376 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
378 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
380 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
381 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
383 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
385 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
386 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
390 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
391 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
396 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
397 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
398 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
403 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
404 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
408 // to make items easier to pick up and allow them to be grabbed off
409 // of shelves, the abs sizes are expanded
411 if ((int)ent->fields.server->flags & FL_ITEM)
422 // because movement is clipped an epsilon away from an actual edge,
423 // we must fully check even when bounding boxes don't quite touch
432 VectorCopy(mins, ent->fields.server->absmin);
433 VectorCopy(maxs, ent->fields.server->absmax);
435 World_LinkEdict(&sv.world, ent, mins, maxs);
437 // if touch_triggers, call touch on all entities overlapping this box
438 if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
439 SV_LinkEdict_TouchAreaGrid(ent);
443 ===============================================================================
447 ===============================================================================
452 SV_TestEntityPosition
454 returns true if the entity is in solid currently
457 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
461 VectorAdd(ent->fields.server->origin, offset, org);
462 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
463 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
467 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
469 // q1bsp/hlbsp use hulls and if the entity does not exactly match
470 // a hull size it is incorrectly tested, so this code tries to
471 // 'fix' it slightly...
472 // FIXME: this breaks entities larger than the hull size
475 VectorAdd(org, ent->fields.server->mins, m1);
476 VectorAdd(org, ent->fields.server->maxs, m2);
477 VectorSubtract(m2, m1, s);
478 #define EPSILON (1.0f / 32.0f)
479 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
480 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
481 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
482 for (i = 0;i < 8;i++)
484 v[0] = (i & 1) ? m2[0] : m1[0];
485 v[1] = (i & 2) ? m2[1] : m1[1];
486 v[2] = (i & 4) ? m2[2] : m1[2];
487 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
492 // if the trace found a better position for the entity, move it there
493 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
494 VectorCopy(trace.endpos, ent->fields.server->origin);
503 void SV_CheckAllEnts (void)
508 // see if any solid entities are inside the final position
509 check = PRVM_NEXT_EDICT(prog->edicts);
510 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
512 if (check->priv.server->free)
514 if (check->fields.server->movetype == MOVETYPE_PUSH
515 || check->fields.server->movetype == MOVETYPE_NONE
516 || check->fields.server->movetype == MOVETYPE_FOLLOW
517 || check->fields.server->movetype == MOVETYPE_NOCLIP)
520 if (SV_TestEntityPosition (check, vec3_origin))
521 Con_Print("entity in invalid position\n");
525 // DRESK - Support for Entity Contents Transition Event
528 SV_CheckContentsTransition
530 returns true if entity had a valid contentstransition function call
533 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
535 int bValidFunctionCall;
536 prvm_eval_t *contentstransition;
538 // Default Valid Function Call to False
539 bValidFunctionCall = false;
541 if(ent->fields.server->watertype != nContents)
542 { // Changed Contents
543 // Acquire Contents Transition Function from QC
544 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
546 if(contentstransition->function)
547 { // Valid Function; Execute
548 // Assign Valid Function
549 bValidFunctionCall = true;
550 // Prepare Parameters (Original Contents, New Contents)
552 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
554 PRVM_G_FLOAT(OFS_PARM1) = nContents;
555 // Execute VM Function
556 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
560 // Return if Function Call was Valid
561 return bValidFunctionCall;
570 void SV_CheckVelocity (prvm_edict_t *ent)
578 for (i=0 ; i<3 ; i++)
580 if (IS_NAN(ent->fields.server->velocity[i]))
582 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
583 ent->fields.server->velocity[i] = 0;
585 if (IS_NAN(ent->fields.server->origin[i]))
587 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
588 ent->fields.server->origin[i] = 0;
592 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
593 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
594 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
596 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
597 ent->fields.server->velocity[0] *= wishspeed;
598 ent->fields.server->velocity[1] *= wishspeed;
599 ent->fields.server->velocity[2] *= wishspeed;
607 Runs thinking code if time. There is some play in the exact time the think
608 function will be called, because it is called before any movement is done
609 in a frame. Not used for pushmove objects, because they must be exact.
610 Returns false if the entity removed itself.
613 qboolean SV_RunThink (prvm_edict_t *ent)
617 thinktime = ent->fields.server->nextthink;
618 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
621 // don't let things stay in the past.
622 // it is possible to start that way by a trigger with a local time.
623 if (thinktime < sv.time)
626 ent->fields.server->nextthink = 0;
627 prog->globals.server->time = thinktime;
628 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
629 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
630 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
631 return !ent->priv.server->free;
638 Two entities have touched, so run their touch functions
641 extern void VM_SetTraceGlobals(const trace_t *trace);
642 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
644 int old_self, old_other;
645 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
648 old_self = prog->globals.server->self;
649 old_other = prog->globals.server->other;
651 VM_SetTraceGlobals(trace);
653 prog->globals.server->time = sv.time;
654 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
656 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
657 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
658 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
661 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
663 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
664 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
665 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
666 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
667 prog->globals.server->trace_plane_dist = -trace->plane.dist;
668 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
669 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
671 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
673 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
675 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
677 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
680 prog->globals.server->self = old_self;
681 prog->globals.server->other = old_other;
689 Slide off of the impacting object
690 returns the blocked flags (1 = floor, 2 = step / wall)
693 #define STOP_EPSILON 0.1
694 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
699 backoff = -DotProduct (in, normal) * overbounce;
700 VectorMA(in, backoff, normal, out);
702 for (i = 0;i < 3;i++)
703 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
712 The basic solid body movement clip that slides along multiple planes
713 Returns the clipflags if the velocity was modified (hit something solid)
717 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
720 // LordHavoc: increased from 5 to 32
721 #define MAX_CLIP_PLANES 32
722 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
724 int blocked, bumpcount;
725 int i, j, impact, numplanes;
727 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
732 VectorCopy(ent->fields.server->velocity, original_velocity);
733 VectorCopy(ent->fields.server->velocity, primal_velocity);
736 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
738 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
741 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
742 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
744 //if (trace.fraction < 0.002)
749 VectorCopy(ent->fields.server->origin, start);
750 start[2] += 3;//0.03125;
751 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
752 end[2] += 3;//0.03125;
753 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
754 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)))
756 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
762 for (i = 0;i < numplanes;i++)
764 VectorCopy(ent->fields.server->origin, start);
765 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
766 VectorMA(start, 3, planes[i], start);
767 VectorMA(end, 3, planes[i], end);
768 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
769 if (trace.fraction < testtrace.fraction)
772 VectorCopy(start, ent->fields.server->origin);
777 // VectorAdd(ent->fields.server->origin, planes[j], start);
783 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);
784 if (trace.fraction < 1)
785 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
790 if (trace.bmodelstartsolid)
792 // LordHavoc: note: this code is what makes entities stick in place
793 // if embedded in world only (you can walk through other objects if
795 // entity is trapped in another solid
796 VectorClear(ent->fields.server->velocity);
801 // break if it moved the entire distance
802 if (trace.fraction == 1)
804 VectorCopy(trace.endpos, ent->fields.server->origin);
810 Con_Printf ("SV_FlyMove: !trace.ent");
811 trace.ent = prog->edicts;
814 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
816 if (trace.plane.normal[2])
818 if (trace.plane.normal[2] > 0.7)
822 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
823 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
830 // save the trace for player extrafriction
832 VectorCopy(trace.plane.normal, stepnormal);
835 if (trace.fraction >= 0.001)
837 // actually covered some distance
838 VectorCopy(trace.endpos, ent->fields.server->origin);
839 VectorCopy(ent->fields.server->velocity, original_velocity);
843 // run the impact function
846 SV_Impact(ent, &trace);
848 // break if removed by the impact function
849 if (ent->priv.server->free)
853 time_left *= 1 - trace.fraction;
855 // clipped to another plane
856 if (numplanes >= MAX_CLIP_PLANES)
858 // this shouldn't really happen
859 VectorClear(ent->fields.server->velocity);
865 for (i = 0;i < numplanes;i++)
866 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
870 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
875 VectorCopy(trace.plane.normal, planes[numplanes]);
878 if (sv_newflymove.integer)
879 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
882 // modify original_velocity so it parallels all of the clip planes
883 for (i = 0;i < numplanes;i++)
885 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
886 for (j = 0;j < numplanes;j++)
891 if (DotProduct(new_velocity, planes[j]) < 0)
901 // go along this plane
902 VectorCopy(new_velocity, ent->fields.server->velocity);
906 // go along the crease
909 VectorClear(ent->fields.server->velocity);
913 CrossProduct(planes[0], planes[1], dir);
914 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
915 VectorNormalize(dir);
916 d = DotProduct(dir, ent->fields.server->velocity);
917 VectorScale(dir, d, ent->fields.server->velocity);
921 // if current velocity is against the original velocity,
922 // stop dead to avoid tiny occilations in sloping corners
923 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
925 VectorClear(ent->fields.server->velocity);
930 //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]);
933 if ((blocked & 1) == 0 && bumpcount > 1)
935 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
936 // flag ONGROUND if there's ground under it
937 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
941 // LordHavoc: this came from QW and allows you to get out of water more easily
942 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
943 VectorCopy(primal_velocity, ent->fields.server->velocity);
953 void SV_AddGravity (prvm_edict_t *ent)
958 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
959 if (val!=0 && val->_float)
960 ent_gravity = val->_float;
963 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
968 ===============================================================================
972 ===============================================================================
979 Does not change the entities velocity at all
982 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
988 VectorAdd (ent->fields.server->origin, push, end);
990 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
992 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
993 type = MOVE_NOMONSTERS; // only clip against bmodels
997 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
998 if (trace.bmodelstartsolid && failonbmodelstartsolid)
1001 VectorCopy (trace.endpos, ent->fields.server->origin);
1002 SV_LinkEdict (ent, true);
1004 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)))
1005 SV_Impact (ent, &trace);
1016 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1019 float savesolid, movetime2, pushltime;
1020 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1022 int numcheckentities;
1023 static prvm_edict_t *checkentities[MAX_EDICTS];
1024 model_t *pushermodel;
1026 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1027 unsigned short moved_edicts[MAX_EDICTS];
1029 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])
1031 pusher->fields.server->ltime += movetime;
1035 switch ((int) pusher->fields.server->solid)
1037 // LordHavoc: valid pusher types
1040 case SOLID_SLIDEBOX:
1041 case SOLID_CORPSE: // LordHavoc: this would be weird...
1043 // LordHavoc: no collisions
1046 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1047 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1048 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1049 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1050 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1051 pusher->fields.server->ltime += movetime;
1052 SV_LinkEdict (pusher, false);
1055 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1058 index = (int) pusher->fields.server->modelindex;
1059 if (index < 1 || index >= MAX_MODELS)
1061 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1064 pushermodel = sv.models[index];
1066 movetime2 = movetime;
1067 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1068 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1069 if (moveangle[0] || moveangle[2])
1071 for (i = 0;i < 3;i++)
1075 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1076 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1080 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1081 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1085 else if (moveangle[1])
1087 for (i = 0;i < 3;i++)
1091 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1092 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1096 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1097 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1103 for (i = 0;i < 3;i++)
1107 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1108 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1112 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1113 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1118 VectorNegate (moveangle, a);
1119 AngleVectorsFLU (a, forward, left, up);
1121 VectorCopy (pusher->fields.server->origin, pushorig);
1122 VectorCopy (pusher->fields.server->angles, pushang);
1123 pushltime = pusher->fields.server->ltime;
1125 // move the pusher to its final position
1127 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1128 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1129 pusher->fields.server->ltime += movetime;
1130 SV_LinkEdict (pusher, false);
1133 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1134 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1135 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);
1136 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1138 savesolid = pusher->fields.server->solid;
1140 // see if any solid entities are inside the final position
1143 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1144 for (e = 0;e < numcheckentities;e++)
1146 prvm_edict_t *check = checkentities[e];
1147 int checkcontents = SV_GenericHitSuperContentsMask(check);
1148 if (check->fields.server->movetype == MOVETYPE_NONE
1149 || check->fields.server->movetype == MOVETYPE_PUSH
1150 || check->fields.server->movetype == MOVETYPE_FOLLOW
1151 || check->fields.server->movetype == MOVETYPE_NOCLIP
1152 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1155 // if the entity is standing on the pusher, it will definitely be moved
1156 // if the entity is not standing on the pusher, but is in the pusher's
1157 // final position, move it
1158 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1160 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);
1161 if (!trace.startsolid)
1166 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1169 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1170 org2[0] = DotProduct (org, forward);
1171 org2[1] = DotProduct (org, left);
1172 org2[2] = DotProduct (org, up);
1173 VectorSubtract (org2, org, move);
1174 VectorAdd (move, move1, move);
1177 VectorCopy (move1, move);
1179 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1180 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1181 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1183 // try moving the contacted entity
1184 pusher->fields.server->solid = SOLID_NOT;
1185 trace = SV_PushEntity (check, move, true);
1186 // FIXME: turn players specially
1187 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1188 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1189 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1191 // this trace.fraction < 1 check causes items to fall off of pushers
1192 // if they pass under or through a wall
1193 // the groundentity check causes items to fall off of ledges
1194 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1195 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1197 // if it is still inside the pusher, block
1198 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);
1199 if (trace.startsolid)
1201 // try moving the contacted entity a tiny bit further to account for precision errors
1203 pusher->fields.server->solid = SOLID_NOT;
1204 VectorScale(move, 1.1, move2);
1205 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1206 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1207 SV_PushEntity (check, move2, true);
1208 pusher->fields.server->solid = savesolid;
1209 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);
1210 if (trace.startsolid)
1212 // try moving the contacted entity a tiny bit less to account for precision errors
1213 pusher->fields.server->solid = SOLID_NOT;
1214 VectorScale(move, 0.9, move2);
1215 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1216 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1217 SV_PushEntity (check, move2, true);
1218 pusher->fields.server->solid = savesolid;
1219 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);
1220 if (trace.startsolid)
1222 // still inside pusher, so it's really blocked
1225 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1227 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1230 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1231 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1235 VectorCopy (pushorig, pusher->fields.server->origin);
1236 VectorCopy (pushang, pusher->fields.server->angles);
1237 pusher->fields.server->ltime = pushltime;
1238 SV_LinkEdict (pusher, false);
1240 // move back any entities we already moved
1241 for (i = 0;i < num_moved;i++)
1243 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1244 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1245 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1246 SV_LinkEdict (ed, false);
1249 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1250 if (pusher->fields.server->blocked)
1252 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1253 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1254 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1261 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1262 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1263 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1272 void SV_Physics_Pusher (prvm_edict_t *ent)
1274 float thinktime, oldltime, movetime;
1276 oldltime = ent->fields.server->ltime;
1278 thinktime = ent->fields.server->nextthink;
1279 if (thinktime < ent->fields.server->ltime + sv.frametime)
1281 movetime = thinktime - ent->fields.server->ltime;
1286 movetime = sv.frametime;
1289 // advances ent->fields.server->ltime if not blocked
1290 SV_PushMove (ent, movetime);
1292 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1294 ent->fields.server->nextthink = 0;
1295 prog->globals.server->time = sv.time;
1296 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1297 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1298 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1304 ===============================================================================
1308 ===============================================================================
1311 static float unstickoffsets[] =
1345 This is a big hack to try and fix the rare case of getting stuck in the world
1349 void SV_CheckStuck (prvm_edict_t *ent)
1354 if (!SV_TestEntityPosition(ent, vec3_origin))
1356 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1360 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1362 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1364 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]);
1365 SV_LinkEdict (ent, true);
1370 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1371 if (!SV_TestEntityPosition(ent, offset))
1373 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1374 SV_LinkEdict (ent, true);
1378 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1381 static void SV_UnstickEntity (prvm_edict_t *ent)
1385 // if not stuck in a bmodel, just return
1386 if (!SV_TestEntityPosition(ent, vec3_origin))
1389 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1391 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1393 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]);
1394 SV_LinkEdict (ent, true);
1399 if (developer.integer >= 100)
1400 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1409 qboolean SV_CheckWater (prvm_edict_t *ent)
1412 int nNativeContents;
1415 point[0] = ent->fields.server->origin[0];
1416 point[1] = ent->fields.server->origin[1];
1417 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1419 // DRESK - Support for Entity Contents Transition Event
1420 // NOTE: Some logic needed to be slightly re-ordered
1421 // to not affect performance and allow for the feature.
1423 // Acquire Super Contents Prior to Resets
1424 cont = SV_PointSuperContents(point);
1425 // Acquire Native Contents Here
1426 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1428 // DRESK - Support for Entity Contents Transition Event
1429 if(ent->fields.server->watertype)
1430 // Entity did NOT Spawn; Check
1431 SV_CheckContentsTransition(ent, nNativeContents);
1434 ent->fields.server->waterlevel = 0;
1435 ent->fields.server->watertype = CONTENTS_EMPTY;
1436 cont = SV_PointSuperContents(point);
1437 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1439 ent->fields.server->watertype = nNativeContents;
1440 ent->fields.server->waterlevel = 1;
1441 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1442 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1444 ent->fields.server->waterlevel = 2;
1445 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1446 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1447 ent->fields.server->waterlevel = 3;
1451 return ent->fields.server->waterlevel > 1;
1460 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1463 vec3_t forward, into, side;
1465 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1466 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1468 // cut the tangential velocity
1469 i = DotProduct (stepnormal, ent->fields.server->velocity);
1470 VectorScale (stepnormal, i, into);
1471 VectorSubtract (ent->fields.server->velocity, into, side);
1472 ent->fields.server->velocity[0] = side[0] * (1 + d);
1473 ent->fields.server->velocity[1] = side[1] * (1 + d);
1479 =====================
1482 Player has come to a dead stop, possibly due to the problem with limited
1483 float precision at some angle joins in the BSP hull.
1485 Try fixing by pushing one pixel in each direction.
1487 This is a hack, but in the interest of good gameplay...
1488 ======================
1490 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1495 VectorCopy (ent->fields.server->origin, oldorg);
1498 for (i=0 ; i<8 ; i++)
1500 // try pushing a little in an axial direction
1503 case 0: dir[0] = 2; dir[1] = 0; break;
1504 case 1: dir[0] = 0; dir[1] = 2; break;
1505 case 2: dir[0] = -2; dir[1] = 0; break;
1506 case 3: dir[0] = 0; dir[1] = -2; break;
1507 case 4: dir[0] = 2; dir[1] = 2; break;
1508 case 5: dir[0] = -2; dir[1] = 2; break;
1509 case 6: dir[0] = 2; dir[1] = -2; break;
1510 case 7: dir[0] = -2; dir[1] = -2; break;
1513 SV_PushEntity (ent, dir, false);
1515 // retry the original move
1516 ent->fields.server->velocity[0] = oldvel[0];
1517 ent->fields.server->velocity[1] = oldvel[1];
1518 ent->fields.server->velocity[2] = 0;
1519 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1521 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1522 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1524 Con_DPrint("TryUnstick - success.\n");
1528 // go back to the original pos and try again
1529 VectorCopy (oldorg, ent->fields.server->origin);
1533 VectorClear (ent->fields.server->velocity);
1534 Con_DPrint("TryUnstick - failure.\n");
1540 =====================
1543 Only used by players
1544 ======================
1546 void SV_WalkMove (prvm_edict_t *ent)
1548 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1549 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1552 // if frametime is 0 (due to client sending the same timestamp twice),
1554 if (sv.frametime <= 0)
1557 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1559 SV_CheckVelocity(ent);
1561 // do a regular slide move unless it looks like you ran into a step
1562 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1564 VectorCopy (ent->fields.server->origin, start_origin);
1565 VectorCopy (ent->fields.server->velocity, start_velocity);
1567 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1569 // if the move did not hit the ground at any point, we're not on ground
1571 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1573 SV_CheckVelocity(ent);
1575 VectorCopy(ent->fields.server->origin, originalmove_origin);
1576 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1577 originalmove_clip = clip;
1578 originalmove_flags = (int)ent->fields.server->flags;
1579 originalmove_groundentity = ent->fields.server->groundentity;
1581 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1584 if (sv_nostep.integer)
1587 // if move didn't block on a step, return
1590 // if move was not trying to move into the step, return
1591 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1594 if (ent->fields.server->movetype != MOVETYPE_FLY)
1596 // return if gibbed by a trigger
1597 if (ent->fields.server->movetype != MOVETYPE_WALK)
1600 // only step up while jumping if that is enabled
1601 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1602 if (!oldonground && ent->fields.server->waterlevel == 0)
1606 // try moving up and forward to go up a step
1607 // back to start pos
1608 VectorCopy (start_origin, ent->fields.server->origin);
1609 VectorCopy (start_velocity, ent->fields.server->velocity);
1612 VectorClear (upmove);
1613 upmove[2] = sv_stepheight.value;
1614 // FIXME: don't link?
1615 SV_PushEntity(ent, upmove, false);
1618 ent->fields.server->velocity[2] = 0;
1619 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1620 ent->fields.server->velocity[2] += start_velocity[2];
1622 SV_CheckVelocity(ent);
1624 // check for stuckness, possibly due to the limited precision of floats
1625 // in the clipping hulls
1627 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1628 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1630 //Con_Printf("wall\n");
1631 // stepping up didn't make any progress, revert to original move
1632 VectorCopy(originalmove_origin, ent->fields.server->origin);
1633 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1634 //clip = originalmove_clip;
1635 ent->fields.server->flags = originalmove_flags;
1636 ent->fields.server->groundentity = originalmove_groundentity;
1637 // now try to unstick if needed
1638 //clip = SV_TryUnstick (ent, oldvel);
1642 //Con_Printf("step - ");
1644 // extra friction based on view angle
1645 if (clip & 2 && sv_wallfriction.integer)
1646 SV_WallFriction (ent, stepnormal);
1648 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1649 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))
1653 VectorClear (downmove);
1654 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1655 // FIXME: don't link?
1656 downtrace = SV_PushEntity (ent, downmove, false);
1658 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1660 // this has been disabled so that you can't jump when you are stepping
1661 // up while already jumping (also known as the Quake2 double jump bug)
1663 // LordHavoc: disabled this check so you can walk on monsters/players
1664 //if (ent->fields.server->solid == SOLID_BSP)
1666 //Con_Printf("onground\n");
1667 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1668 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1674 //Con_Printf("slope\n");
1675 // if the push down didn't end up on good ground, use the move without
1676 // the step up. This happens near wall / slope combinations, and can
1677 // cause the player to hop up higher on a slope too steep to climb
1678 VectorCopy(originalmove_origin, ent->fields.server->origin);
1679 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1680 //clip = originalmove_clip;
1681 ent->fields.server->flags = originalmove_flags;
1682 ent->fields.server->groundentity = originalmove_groundentity;
1685 SV_CheckVelocity(ent);
1688 //============================================================================
1694 Entities that are "stuck" to another entity
1697 void SV_Physics_Follow (prvm_edict_t *ent)
1699 vec3_t vf, vr, vu, angles, v;
1703 if (!SV_RunThink (ent))
1706 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1707 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1708 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])
1710 // quick case for no rotation
1711 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1715 angles[0] = -ent->fields.server->punchangle[0];
1716 angles[1] = ent->fields.server->punchangle[1];
1717 angles[2] = ent->fields.server->punchangle[2];
1718 AngleVectors (angles, vf, vr, vu);
1719 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];
1720 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];
1721 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];
1722 angles[0] = -e->fields.server->angles[0];
1723 angles[1] = e->fields.server->angles[1];
1724 angles[2] = e->fields.server->angles[2];
1725 AngleVectors (angles, vf, vr, vu);
1726 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1727 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1728 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1730 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1731 SV_LinkEdict (ent, true);
1735 ==============================================================================
1739 ==============================================================================
1744 SV_CheckWaterTransition
1748 void SV_CheckWaterTransition (prvm_edict_t *ent)
1751 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1752 if (!ent->fields.server->watertype)
1754 // just spawned here
1755 ent->fields.server->watertype = cont;
1756 ent->fields.server->waterlevel = 1;
1760 // DRESK - Support for Entity Contents Transition Event
1761 // NOTE: Call here BEFORE updating the watertype below,
1762 // and suppress watersplash sound if a valid function
1763 // call was made to allow for custom "splash" sounds.
1764 if( !SV_CheckContentsTransition(ent, cont) )
1765 { // Contents Transition Function Invalid; Potentially Play Water Sound
1766 // check if the entity crossed into or out of water
1767 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1768 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1771 if (cont <= CONTENTS_WATER)
1773 ent->fields.server->watertype = cont;
1774 ent->fields.server->waterlevel = 1;
1778 ent->fields.server->watertype = CONTENTS_EMPTY;
1779 ent->fields.server->waterlevel = 0;
1787 Toss, bounce, and fly movement. When onground, do nothing.
1790 void SV_Physics_Toss (prvm_edict_t *ent)
1795 // if onground, return without moving
1796 if ((int)ent->fields.server->flags & FL_ONGROUND)
1798 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1800 // don't stick to ground if onground and moving upward
1801 ent->fields.server->flags -= FL_ONGROUND;
1803 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1805 // we can trust FL_ONGROUND if groundentity is world because it never moves
1808 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1810 // if ent was supported by a brush model on previous frame,
1811 // and groundentity is now freed, set groundentity to 0 (world)
1812 // which leaves it suspended in the air
1813 ent->fields.server->groundentity = 0;
1817 ent->priv.server->suspendedinairflag = false;
1819 SV_CheckVelocity (ent);
1822 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1823 SV_AddGravity (ent);
1826 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1829 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1830 trace = SV_PushEntity (ent, move, true);
1831 if (ent->priv.server->free)
1833 if (trace.bmodelstartsolid)
1835 // try to unstick the entity
1836 SV_UnstickEntity(ent);
1837 trace = SV_PushEntity (ent, move, false);
1838 if (ent->priv.server->free)
1842 if (trace.fraction < 1)
1844 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1846 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1847 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1849 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1852 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1853 // LordHavoc: fixed grenades not bouncing when fired down a slope
1854 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1856 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1857 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1859 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1860 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1861 VectorClear (ent->fields.server->velocity);
1862 VectorClear (ent->fields.server->avelocity);
1865 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1869 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1871 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1872 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1873 VectorClear (ent->fields.server->velocity);
1874 VectorClear (ent->fields.server->avelocity);
1877 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1882 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1883 if (trace.plane.normal[2] > 0.7)
1885 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1886 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1887 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1888 ent->priv.server->suspendedinairflag = true;
1889 VectorClear (ent->fields.server->velocity);
1890 VectorClear (ent->fields.server->avelocity);
1893 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1897 // check for in water
1898 SV_CheckWaterTransition (ent);
1902 ===============================================================================
1906 ===============================================================================
1913 Monsters freefall when they don't have a ground entity, otherwise
1914 all movement is done with discrete steps.
1916 This is also used for objects that have become still on the ground, but
1917 will fall if the floor is pulled out from under them.
1920 void SV_Physics_Step (prvm_edict_t *ent)
1922 int flags = (int)ent->fields.server->flags;
1923 // don't fall at all if fly/swim
1924 if (!(flags & (FL_FLY | FL_SWIM)))
1926 if (flags & FL_ONGROUND)
1928 // freefall if onground and moving upward
1929 // freefall if not standing on a world surface (it may be a lift or trap door)
1930 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1932 ent->fields.server->flags -= FL_ONGROUND;
1934 SV_CheckVelocity(ent);
1935 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1936 SV_LinkEdict(ent, true);
1941 // freefall if not onground
1942 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1945 SV_CheckVelocity(ent);
1946 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1947 SV_LinkEdict(ent, true);
1950 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1951 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1958 SV_CheckWaterTransition(ent);
1961 //============================================================================
1963 static void SV_Physics_Entity (prvm_edict_t *ent)
1965 // don't run a move on newly spawned projectiles as it messes up movement
1966 // interpolation and rocket trails
1967 qboolean runmove = ent->priv.server->move;
1968 ent->priv.server->move = true;
1969 switch ((int) ent->fields.server->movetype)
1972 case MOVETYPE_FAKEPUSH:
1973 SV_Physics_Pusher (ent);
1976 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1977 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1980 case MOVETYPE_FOLLOW:
1981 SV_Physics_Follow (ent);
1983 case MOVETYPE_NOCLIP:
1984 if (SV_RunThink(ent))
1987 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1988 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1990 SV_LinkEdict(ent, false);
1993 SV_Physics_Step (ent);
1996 if (SV_RunThink (ent))
1998 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1999 SV_AddGravity (ent);
2000 SV_CheckStuck (ent);
2002 SV_LinkEdict (ent, true);
2006 case MOVETYPE_BOUNCE:
2007 case MOVETYPE_BOUNCEMISSILE:
2008 case MOVETYPE_FLYMISSILE:
2011 if (SV_RunThink (ent) && runmove)
2012 SV_Physics_Toss (ent);
2015 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2020 void SV_Physics_ClientMove(void)
2023 ent = host_client->edict;
2025 // call player physics, this needs the proper frametime
2026 prog->globals.server->frametime = sv.frametime;
2029 // call standard client pre-think, with frametime = 0
2030 prog->globals.server->time = sv.time;
2031 prog->globals.server->frametime = 0;
2032 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2033 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2034 prog->globals.server->frametime = sv.frametime;
2036 // make sure the velocity is sane (not a NaN)
2037 SV_CheckVelocity(ent);
2038 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2039 // player_run/player_stand1 does not horribly malfunction if the
2040 // velocity becomes a number that is both == 0 and != 0
2041 // (sounds to me like NaN but to be absolutely safe...)
2042 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2043 VectorClear(ent->fields.server->velocity);
2045 // perform MOVETYPE_WALK behavior
2046 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2047 SV_AddGravity (ent);
2048 SV_CheckStuck (ent);
2051 SV_CheckVelocity (ent);
2053 SV_LinkEdict (ent, true);
2055 SV_CheckVelocity (ent);
2057 // call standard player post-think, with frametime = 0
2058 prog->globals.server->time = sv.time;
2059 prog->globals.server->frametime = 0;
2060 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2061 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2062 prog->globals.server->frametime = sv.frametime;
2064 if(ent->fields.server->fixangle)
2066 // angle fixing was requested by physics code...
2067 // so store the current angles for later use
2068 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2069 host_client->fixangle_angles_set = TRUE;
2071 // and clear fixangle for the next frame
2072 ent->fields.server->fixangle = 0;
2076 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2078 // don't do physics on disconnected clients, FrikBot relies on this
2079 if (!host_client->spawned)
2081 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2085 // don't run physics here if running asynchronously
2086 if (host_client->clmovement_skipphysicsframes <= 0)
2089 // make sure the velocity is sane (not a NaN)
2090 SV_CheckVelocity(ent);
2091 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2092 // player_run/player_stand1 does not horribly malfunction if the
2093 // velocity becomes a number that is both == 0 and != 0
2094 // (sounds to me like NaN but to be absolutely safe...)
2095 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2096 VectorClear(ent->fields.server->velocity);
2098 // call standard client pre-think
2099 prog->globals.server->time = sv.time;
2100 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2101 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2102 SV_CheckVelocity (ent);
2104 switch ((int) ent->fields.server->movetype)
2107 case MOVETYPE_FAKEPUSH:
2108 SV_Physics_Pusher (ent);
2111 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2112 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2115 case MOVETYPE_FOLLOW:
2116 SV_Physics_Follow (ent);
2118 case MOVETYPE_NOCLIP:
2121 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2122 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2125 SV_Physics_Step (ent);
2129 // don't run physics here if running asynchronously
2130 if (host_client->clmovement_skipphysicsframes <= 0)
2132 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2133 SV_AddGravity (ent);
2134 SV_CheckStuck (ent);
2139 case MOVETYPE_BOUNCE:
2140 case MOVETYPE_BOUNCEMISSILE:
2141 case MOVETYPE_FLYMISSILE:
2144 SV_Physics_Toss (ent);
2148 SV_CheckWater (ent);
2152 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2156 // decrement the countdown variable used to decide when to go back to
2157 // synchronous physics
2158 if (host_client->clmovement_skipphysicsframes > 0)
2159 host_client->clmovement_skipphysicsframes--;
2161 SV_CheckVelocity (ent);
2163 SV_LinkEdict (ent, true);
2165 SV_CheckVelocity (ent);
2167 // call standard player post-think
2168 prog->globals.server->time = sv.time;
2169 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2170 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2172 if(ent->fields.server->fixangle)
2174 // angle fixing was requested by physics code...
2175 // so store the current angles for later use
2176 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2177 host_client->fixangle_angles_set = TRUE;
2179 // and clear fixangle for the next frame
2180 ent->fields.server->fixangle = 0;
2190 void SV_Physics (void)
2195 // let the progs know that a new frame has started
2196 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2197 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2198 prog->globals.server->time = sv.time;
2199 prog->globals.server->frametime = sv.frametime;
2200 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2203 // treat each object in turn
2206 // if force_retouch, relink all the entities
2207 if (prog->globals.server->force_retouch > 0)
2208 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2209 if (!ent->priv.server->free)
2210 SV_LinkEdict (ent, true); // force retouch even for stationary
2212 // run physics on the client entities
2213 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2214 if (!ent->priv.server->free)
2215 SV_Physics_ClientEntity(ent);
2217 // run physics on all the non-client entities
2218 if (!sv_freezenonclients.integer)
2219 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2220 if (!ent->priv.server->free)
2221 SV_Physics_Entity(ent);
2223 if (prog->globals.server->force_retouch > 0)
2224 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2226 // LordHavoc: endframe support
2227 if (prog->funcoffsets.EndFrame)
2229 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2230 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2231 prog->globals.server->time = sv.time;
2232 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2235 // decrement prog->num_edicts if the highest number entities died
2236 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2238 if (!sv_freezenonclients.integer)
2239 sv.time += sv.frametime;