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_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
43 cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
44 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
45 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
46 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
47 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
48 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
49 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
50 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
51 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
52 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
53 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
54 cvar_t sv_debugmove = {CVAR_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"};
56 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)"};
57 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)"};
59 // TODO: move this extern to server.h
60 extern cvar_t sv_clmovement_waitforinput;
62 #define MOVE_EPSILON 0.01
64 void SV_Physics_Toss (prvm_edict_t *ent);
66 void SV_Phys_Init (void)
68 Cvar_RegisterVariable(&sv_stepheight);
69 Cvar_RegisterVariable(&sv_jumpstep);
70 Cvar_RegisterVariable(&sv_wallfriction);
71 Cvar_RegisterVariable(&sv_newflymove);
72 Cvar_RegisterVariable(&sv_freezenonclients);
73 Cvar_RegisterVariable(&sv_playerphysicsqc);
74 Cvar_RegisterVariable(&sv_debugmove);
76 Cvar_RegisterVariable(&sv_sound_watersplash);
77 Cvar_RegisterVariable(&sv_sound_land);
81 ===============================================================================
85 ===============================================================================
88 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
93 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
94 if (val && val->_float)
95 return (int)val->_float;
96 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
98 if ((int)passedict->fields.server->flags & FL_MONSTER)
99 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
101 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
103 else if (passedict->fields.server->solid == SOLID_CORPSE)
104 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
106 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
109 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
117 #if COLLISIONPARANOID >= 1
118 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 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)
123 vec3_t hullmins, hullmaxs;
127 prvm_edict_t *traceowner, *touch;
129 // bounding box of entire move area
130 vec3_t clipboxmins, clipboxmaxs;
131 // size of the moving object
132 vec3_t clipmins, clipmaxs;
133 // size when clipping against monsters
134 vec3_t clipmins2, clipmaxs2;
135 // start and end origin of move
136 vec3_t clipstart, clipend;
139 // matrices to transform into/out of other entity's space
140 matrix4x4_t matrix, imatrix;
141 // model of other entity
143 // list of entities to test for collisions
145 prvm_edict_t *touchedicts[MAX_EDICTS];
147 VectorCopy(start, clipstart);
148 VectorCopy(end, clipend);
149 VectorCopy(mins, clipmins);
150 VectorCopy(maxs, clipmaxs);
151 VectorCopy(mins, clipmins2);
152 VectorCopy(maxs, clipmaxs2);
153 #if COLLISIONPARANOID >= 3
154 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
158 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
159 cliptrace.bmodelstartsolid = cliptrace.startsolid;
160 if (cliptrace.startsolid || cliptrace.fraction < 1)
161 cliptrace.ent = prog->edicts;
162 if (type == MOVE_WORLDONLY)
165 if (type == MOVE_MISSILE)
167 // LordHavoc: modified this, was = -15, now -= 15
168 for (i = 0;i < 3;i++)
175 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
176 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
177 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
180 VectorCopy(clipmins, hullmins);
181 VectorCopy(clipmaxs, hullmaxs);
184 // create the bounding box of the entire move
185 for (i = 0;i < 3;i++)
187 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
188 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
191 // debug override to test against everything
192 if (sv_debugmove.integer)
194 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
195 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
198 // if the passedict is world, make it NULL (to avoid two checks each time)
199 if (passedict == prog->edicts)
201 // precalculate prog value for passedict for comparisons
202 passedictprog = PRVM_EDICT_TO_PROG(passedict);
203 // figure out whether this is a point trace for comparisons
204 pointtrace = VectorCompare(clipmins, clipmaxs);
205 // precalculate passedict's owner edict pointer for comparisons
206 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
209 // because this uses World_EntitiestoBox, we know all entity boxes overlap
210 // the clip region, so we can skip culling checks in the loop below
211 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
212 if (numtouchedicts > MAX_EDICTS)
214 // this never happens
215 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
216 numtouchedicts = MAX_EDICTS;
218 for (i = 0;i < numtouchedicts;i++)
220 touch = touchedicts[i];
222 if (touch->fields.server->solid < SOLID_BBOX)
224 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
229 // don't clip against self
230 if (passedict == touch)
232 // don't clip owned entities against owner
233 if (traceowner == touch)
235 // don't clip owner against owned entities
236 if (passedictprog == touch->fields.server->owner)
238 // don't clip points against points (they can't collide)
239 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
243 // might interact, so do an exact clip
245 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
247 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
248 // if the modelindex is 0, it shouldn't be SOLID_BSP!
249 if (modelindex > 0 && modelindex < MAX_MODELS)
250 model = sv.models[(int)touch->fields.server->modelindex];
251 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, SUPERCONTENTS_BODY, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
259 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, SUPERCONTENTS_BODY, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
260 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
266 #if COLLISIONPARANOID >= 1
267 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)
272 trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
275 VectorCopy(trace.endpos, temp);
276 endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
277 #if COLLISIONPARANOID < 3
278 if (trace.startsolid || endstuck)
280 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" : "");
287 ===============================================================================
289 Linking entities into the world culling system
291 ===============================================================================
294 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
296 int i, numtouchedicts, old_self, old_other;
297 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
299 // build a list of edicts to touch, because the link loop can be corrupted
300 // by SV_IncreaseEdicts called during touch functions
301 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
302 if (numtouchedicts > MAX_EDICTS)
304 // this never happens
305 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
306 numtouchedicts = MAX_EDICTS;
309 old_self = prog->globals.server->self;
310 old_other = prog->globals.server->other;
311 for (i = 0;i < numtouchedicts;i++)
313 touch = touchedicts[i];
314 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
317 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
318 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
319 prog->globals.server->time = sv.time;
320 prog->globals.server->trace_allsolid = false;
321 prog->globals.server->trace_startsolid = false;
322 prog->globals.server->trace_fraction = 1;
323 prog->globals.server->trace_inwater = false;
324 prog->globals.server->trace_inopen = true;
325 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
326 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
327 prog->globals.server->trace_plane_dist = 0;
328 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
329 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
331 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
333 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
335 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
337 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
340 prog->globals.server->self = old_self;
341 prog->globals.server->other = old_other;
350 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
355 if (ent == prog->edicts)
356 return; // don't add the world
358 if (ent->priv.server->free)
363 if (ent->fields.server->solid == SOLID_BSP)
365 int modelindex = (int)ent->fields.server->modelindex;
366 if (modelindex < 0 || modelindex > MAX_MODELS)
368 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
371 model = sv.models[modelindex];
374 if (!model->TraceBox)
375 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
377 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
379 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
380 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
382 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
384 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
385 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
389 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
390 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
395 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
396 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
397 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
402 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
403 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
407 // to make items easier to pick up and allow them to be grabbed off
408 // of shelves, the abs sizes are expanded
410 if ((int)ent->fields.server->flags & FL_ITEM)
421 // because movement is clipped an epsilon away from an actual edge,
422 // we must fully check even when bounding boxes don't quite touch
431 VectorCopy(mins, ent->fields.server->absmin);
432 VectorCopy(maxs, ent->fields.server->absmax);
434 World_LinkEdict(&sv.world, ent, mins, maxs);
436 // if touch_triggers, call touch on all entities overlapping this box
437 if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
438 SV_LinkEdict_TouchAreaGrid(ent);
442 ===============================================================================
446 ===============================================================================
451 SV_TestEntityPosition
453 returns true if the entity is in solid currently
456 static int SV_TestEntityPosition (prvm_edict_t *ent)
458 trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
459 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
463 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
465 // q1bsp/hlbsp use hulls and if the entity does not exactly match
466 // a hull size it is incorrectly tested, so this code tries to
467 // 'fix' it slightly...
470 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, m1);
471 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, m2);
472 VectorSubtract(m2, m1, s);
473 #define EPSILON (1.0f / 32.0f)
474 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
475 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
476 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
477 for (i = 0;i < 8;i++)
479 v[0] = (i & 1) ? m2[0] : m1[0];
480 v[1] = (i & 2) ? m2[1] : m1[1];
481 v[2] = (i & 4) ? m2[2] : m1[2];
482 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
495 void SV_CheckAllEnts (void)
500 // see if any solid entities are inside the final position
501 check = PRVM_NEXT_EDICT(prog->edicts);
502 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
504 if (check->priv.server->free)
506 if (check->fields.server->movetype == MOVETYPE_PUSH
507 || check->fields.server->movetype == MOVETYPE_NONE
508 || check->fields.server->movetype == MOVETYPE_FOLLOW
509 || check->fields.server->movetype == MOVETYPE_NOCLIP)
512 if (SV_TestEntityPosition (check))
513 Con_Print("entity in invalid position\n");
517 // DRESK - Support for Entity Contents Transition Event
520 SV_CheckContentsTransition
522 returns true if entity had a valid contentstransition function call
525 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
527 int bValidFunctionCall;
528 prvm_eval_t *contentstransition;
530 // Default Valid Function Call to False
531 bValidFunctionCall = false;
533 if(ent->fields.server->watertype != nContents)
534 { // Changed Contents
535 // Acquire Contents Transition Function from QC
536 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
538 if(contentstransition->function)
539 { // Valid Function; Execute
540 // Assign Valid Function
541 bValidFunctionCall = true;
542 // Prepare Parameters (Original Contents, New Contents)
544 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
546 PRVM_G_FLOAT(OFS_PARM1) = nContents;
547 // Execute VM Function
548 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
552 // Return if Function Call was Valid
553 return bValidFunctionCall;
562 void SV_CheckVelocity (prvm_edict_t *ent)
570 for (i=0 ; i<3 ; i++)
572 if (IS_NAN(ent->fields.server->velocity[i]))
574 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
575 ent->fields.server->velocity[i] = 0;
577 if (IS_NAN(ent->fields.server->origin[i]))
579 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
580 ent->fields.server->origin[i] = 0;
584 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
585 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
586 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
588 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
589 ent->fields.server->velocity[0] *= wishspeed;
590 ent->fields.server->velocity[1] *= wishspeed;
591 ent->fields.server->velocity[2] *= wishspeed;
599 Runs thinking code if time. There is some play in the exact time the think
600 function will be called, because it is called before any movement is done
601 in a frame. Not used for pushmove objects, because they must be exact.
602 Returns false if the entity removed itself.
605 qboolean SV_RunThink (prvm_edict_t *ent)
609 thinktime = ent->fields.server->nextthink;
610 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
613 // don't let things stay in the past.
614 // it is possible to start that way by a trigger with a local time.
615 if (thinktime < sv.time)
618 ent->fields.server->nextthink = 0;
619 prog->globals.server->time = thinktime;
620 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
621 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
622 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
623 return !ent->priv.server->free;
630 Two entities have touched, so run their touch functions
633 extern void VM_SetTraceGlobals(const trace_t *trace);
634 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
636 int old_self, old_other;
637 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
640 old_self = prog->globals.server->self;
641 old_other = prog->globals.server->other;
643 VM_SetTraceGlobals(trace);
645 prog->globals.server->time = sv.time;
646 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
648 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
649 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
650 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
653 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
655 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
656 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
657 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
658 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
659 prog->globals.server->trace_plane_dist = -trace->plane.dist;
660 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
661 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
663 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
665 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
667 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
669 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
672 prog->globals.server->self = old_self;
673 prog->globals.server->other = old_other;
681 Slide off of the impacting object
682 returns the blocked flags (1 = floor, 2 = step / wall)
685 #define STOP_EPSILON 0.1
686 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
691 backoff = -DotProduct (in, normal) * overbounce;
692 VectorMA(in, backoff, normal, out);
694 for (i = 0;i < 3;i++)
695 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
704 The basic solid body movement clip that slides along multiple planes
705 Returns the clipflags if the velocity was modified (hit something solid)
709 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
712 // LordHavoc: increased from 5 to 32
713 #define MAX_CLIP_PLANES 32
714 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
716 int blocked, bumpcount;
717 int i, j, impact, numplanes;
719 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
724 VectorCopy(ent->fields.server->velocity, original_velocity);
725 VectorCopy(ent->fields.server->velocity, primal_velocity);
728 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
730 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
733 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
734 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
736 //if (trace.fraction < 0.002)
741 VectorCopy(ent->fields.server->origin, start);
742 start[2] += 3;//0.03125;
743 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
744 end[2] += 3;//0.03125;
745 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
746 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)))
748 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
754 for (i = 0;i < numplanes;i++)
756 VectorCopy(ent->fields.server->origin, start);
757 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
758 VectorMA(start, 3, planes[i], start);
759 VectorMA(end, 3, planes[i], end);
760 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
761 if (trace.fraction < testtrace.fraction)
764 VectorCopy(start, ent->fields.server->origin);
769 // VectorAdd(ent->fields.server->origin, planes[j], start);
775 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);
776 if (trace.fraction < 1)
777 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
782 if (trace.bmodelstartsolid)
784 // LordHavoc: note: this code is what makes entities stick in place
785 // if embedded in world only (you can walk through other objects if
787 // entity is trapped in another solid
788 VectorClear(ent->fields.server->velocity);
793 // break if it moved the entire distance
794 if (trace.fraction == 1)
796 VectorCopy(trace.endpos, ent->fields.server->origin);
802 Con_Printf ("SV_FlyMove: !trace.ent");
803 trace.ent = prog->edicts;
806 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
810 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
814 if (trace.plane.normal[2])
816 if (trace.plane.normal[2] > 0.7)
820 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
821 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
828 // save the trace for player extrafriction
830 VectorCopy(trace.plane.normal, stepnormal);
833 if (trace.fraction >= 0.001)
835 // actually covered some distance
836 VectorCopy(trace.endpos, ent->fields.server->origin);
837 VectorCopy(ent->fields.server->velocity, original_velocity);
841 // run the impact function
844 SV_Impact(ent, &trace);
846 // break if removed by the impact function
847 if (ent->priv.server->free)
851 time_left *= 1 - trace.fraction;
853 // clipped to another plane
854 if (numplanes >= MAX_CLIP_PLANES)
856 // this shouldn't really happen
857 VectorClear(ent->fields.server->velocity);
863 for (i = 0;i < numplanes;i++)
864 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
868 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
873 VectorCopy(trace.plane.normal, planes[numplanes]);
876 if (sv_newflymove.integer)
877 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
880 // modify original_velocity so it parallels all of the clip planes
881 for (i = 0;i < numplanes;i++)
883 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
884 for (j = 0;j < numplanes;j++)
889 if (DotProduct(new_velocity, planes[j]) < 0)
899 // go along this plane
900 VectorCopy(new_velocity, ent->fields.server->velocity);
904 // go along the crease
907 VectorClear(ent->fields.server->velocity);
911 CrossProduct(planes[0], planes[1], dir);
912 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
913 VectorNormalize(dir);
914 d = DotProduct(dir, ent->fields.server->velocity);
915 VectorScale(dir, d, ent->fields.server->velocity);
919 // if current velocity is against the original velocity,
920 // stop dead to avoid tiny occilations in sloping corners
921 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
923 VectorClear(ent->fields.server->velocity);
928 //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]);
931 if ((blocked & 1) == 0 && bumpcount > 1)
933 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
934 // flag ONGROUND if there's ground under it
935 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
939 // LordHavoc: this came from QW and allows you to get out of water more easily
940 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
941 VectorCopy(primal_velocity, ent->fields.server->velocity);
951 void SV_AddGravity (prvm_edict_t *ent)
956 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
957 if (val!=0 && val->_float)
958 ent_gravity = val->_float;
961 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
966 ===============================================================================
970 ===============================================================================
977 Does not change the entities velocity at all
980 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
986 VectorAdd (ent->fields.server->origin, push, end);
988 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
990 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
991 type = MOVE_NOMONSTERS; // only clip against bmodels
995 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
996 if (trace.bmodelstartsolid && failonbmodelstartsolid)
999 VectorCopy (trace.endpos, ent->fields.server->origin);
1000 SV_LinkEdict (ent, true);
1002 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)))
1003 SV_Impact (ent, &trace);
1014 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1017 float savesolid, movetime2, pushltime;
1018 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1020 int numcheckentities;
1021 static prvm_edict_t *checkentities[MAX_EDICTS];
1022 model_t *pushermodel;
1024 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1026 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])
1028 pusher->fields.server->ltime += movetime;
1032 switch ((int) pusher->fields.server->solid)
1034 // LordHavoc: valid pusher types
1037 case SOLID_SLIDEBOX:
1038 case SOLID_CORPSE: // LordHavoc: this would be weird...
1040 // LordHavoc: no collisions
1043 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1044 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1045 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1046 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1047 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1048 pusher->fields.server->ltime += movetime;
1049 SV_LinkEdict (pusher, false);
1052 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1055 index = (int) pusher->fields.server->modelindex;
1056 if (index < 1 || index >= MAX_MODELS)
1058 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1061 pushermodel = sv.models[index];
1063 movetime2 = movetime;
1064 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1065 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1066 if (moveangle[0] || moveangle[2])
1068 for (i = 0;i < 3;i++)
1072 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1073 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1077 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1078 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1082 else if (moveangle[1])
1084 for (i = 0;i < 3;i++)
1088 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1089 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1093 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1094 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1100 for (i = 0;i < 3;i++)
1104 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1105 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1109 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1110 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1115 VectorNegate (moveangle, a);
1116 AngleVectorsFLU (a, forward, left, up);
1118 VectorCopy (pusher->fields.server->origin, pushorig);
1119 VectorCopy (pusher->fields.server->angles, pushang);
1120 pushltime = pusher->fields.server->ltime;
1122 // move the pusher to its final position
1124 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1125 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1126 pusher->fields.server->ltime += movetime;
1127 SV_LinkEdict (pusher, false);
1130 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1131 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1132 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);
1133 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1135 savesolid = pusher->fields.server->solid;
1137 // see if any solid entities are inside the final position
1140 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1141 for (e = 0;e < numcheckentities;e++)
1143 prvm_edict_t *check = checkentities[e];
1144 if (check->fields.server->movetype == MOVETYPE_NONE
1145 || check->fields.server->movetype == MOVETYPE_PUSH
1146 || check->fields.server->movetype == MOVETYPE_FOLLOW
1147 || check->fields.server->movetype == MOVETYPE_NOCLIP
1148 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1151 // if the entity is standing on the pusher, it will definitely be moved
1152 if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
1154 // remove the onground flag for non-players
1155 if (check->fields.server->movetype != MOVETYPE_WALK)
1156 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1160 // if the entity is not inside the pusher's final position, leave it alone
1161 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, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
1162 if (!trace.startsolid)
1167 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1170 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1171 org2[0] = DotProduct (org, forward);
1172 org2[1] = DotProduct (org, left);
1173 org2[2] = DotProduct (org, up);
1174 VectorSubtract (org2, org, move);
1175 VectorAdd (move, move1, move);
1178 VectorCopy (move1, move);
1180 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1181 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1182 sv.moved_edicts[num_moved++] = check;
1184 // try moving the contacted entity
1185 pusher->fields.server->solid = SOLID_NOT;
1186 trace = SV_PushEntity (check, move, true);
1187 // FIXME: turn players specially
1188 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1189 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1190 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1192 // if it is still inside the pusher, block
1193 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, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
1194 if (trace.startsolid)
1196 // try moving the contacted entity a tiny bit further to account for precision errors
1198 pusher->fields.server->solid = SOLID_NOT;
1199 VectorScale(move, 1.1, move2);
1200 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1201 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1202 SV_PushEntity (check, move2, true);
1203 pusher->fields.server->solid = savesolid;
1204 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, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
1205 if (trace.startsolid)
1207 // try moving the contacted entity a tiny bit less to account for precision errors
1208 pusher->fields.server->solid = SOLID_NOT;
1209 VectorScale(move, 0.9, move2);
1210 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1211 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1212 SV_PushEntity (check, move2, true);
1213 pusher->fields.server->solid = savesolid;
1214 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, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
1215 if (trace.startsolid)
1217 // still inside pusher, so it's really blocked
1220 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1222 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1225 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1226 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1230 VectorCopy (pushorig, pusher->fields.server->origin);
1231 VectorCopy (pushang, pusher->fields.server->angles);
1232 pusher->fields.server->ltime = pushltime;
1233 SV_LinkEdict (pusher, false);
1235 // move back any entities we already moved
1236 for (i = 0;i < num_moved;i++)
1238 prvm_edict_t *ed = sv.moved_edicts[i];
1239 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1240 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1241 SV_LinkEdict (ed, false);
1244 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1245 if (pusher->fields.server->blocked)
1247 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1248 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1249 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1256 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1257 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1258 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1267 void SV_Physics_Pusher (prvm_edict_t *ent)
1269 float thinktime, oldltime, movetime;
1271 oldltime = ent->fields.server->ltime;
1273 thinktime = ent->fields.server->nextthink;
1274 if (thinktime < ent->fields.server->ltime + sv.frametime)
1276 movetime = thinktime - ent->fields.server->ltime;
1281 movetime = sv.frametime;
1284 // advances ent->fields.server->ltime if not blocked
1285 SV_PushMove (ent, movetime);
1287 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1289 ent->fields.server->nextthink = 0;
1290 prog->globals.server->time = sv.time;
1291 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1292 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1293 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1299 ===============================================================================
1303 ===============================================================================
1310 This is a big hack to try and fix the rare case of getting stuck in the world
1314 void SV_CheckStuck (prvm_edict_t *ent)
1319 if (!SV_TestEntityPosition(ent))
1321 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1325 VectorCopy (ent->fields.server->origin, org);
1327 for (z=-1 ; z< 18 ; z++)
1328 for (i=-1 ; i <= 1 ; i++)
1329 for (j=-1 ; j <= 1 ; j++)
1331 ent->fields.server->origin[0] = org[0] + i;
1332 ent->fields.server->origin[1] = org[1] + j;
1333 ent->fields.server->origin[2] = org[2] + z;
1334 if (!SV_TestEntityPosition(ent))
1336 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), (float)i, (float)j, (float)z);
1337 SV_LinkEdict (ent, true);
1342 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
1343 if (!SV_TestEntityPosition(ent))
1345 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1346 SV_LinkEdict (ent, true);
1350 VectorCopy (org, ent->fields.server->origin);
1351 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1354 static void SV_UnstickEntity (prvm_edict_t *ent)
1359 // if not stuck in a bmodel, just return
1360 if (!SV_TestEntityPosition(ent))
1363 VectorCopy (ent->fields.server->origin, org);
1365 for (z=-1 ; z< 18 ; z += 6)
1366 for (i=-1 ; i <= 1 ; i++)
1367 for (j=-1 ; j <= 1 ; j++)
1369 ent->fields.server->origin[0] = org[0] + i;
1370 ent->fields.server->origin[1] = org[1] + j;
1371 ent->fields.server->origin[2] = org[2] + z;
1372 if (!SV_TestEntityPosition(ent))
1374 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), (float)i, (float)j, (float)z);
1375 SV_LinkEdict (ent, true);
1380 VectorCopy (org, ent->fields.server->origin);
1381 if (developer.integer >= 100)
1382 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1391 qboolean SV_CheckWater (prvm_edict_t *ent)
1394 int nNativeContents;
1397 point[0] = ent->fields.server->origin[0];
1398 point[1] = ent->fields.server->origin[1];
1399 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1401 // DRESK - Support for Entity Contents Transition Event
1402 // NOTE: Some logic needed to be slightly re-ordered
1403 // to not affect performance and allow for the feature.
1405 // Acquire Super Contents Prior to Resets
1406 cont = SV_PointSuperContents(point);
1407 // Acquire Native Contents Here
1408 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1410 // DRESK - Support for Entity Contents Transition Event
1411 if(ent->fields.server->watertype)
1412 // Entity did NOT Spawn; Check
1413 SV_CheckContentsTransition(ent, nNativeContents);
1416 ent->fields.server->waterlevel = 0;
1417 ent->fields.server->watertype = CONTENTS_EMPTY;
1418 cont = SV_PointSuperContents(point);
1419 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1421 ent->fields.server->watertype = nNativeContents;
1422 ent->fields.server->waterlevel = 1;
1423 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1424 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1426 ent->fields.server->waterlevel = 2;
1427 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1428 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1429 ent->fields.server->waterlevel = 3;
1433 return ent->fields.server->waterlevel > 1;
1442 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1445 vec3_t forward, into, side;
1447 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1448 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1450 // cut the tangential velocity
1451 i = DotProduct (stepnormal, ent->fields.server->velocity);
1452 VectorScale (stepnormal, i, into);
1453 VectorSubtract (ent->fields.server->velocity, into, side);
1454 ent->fields.server->velocity[0] = side[0] * (1 + d);
1455 ent->fields.server->velocity[1] = side[1] * (1 + d);
1461 =====================
1464 Player has come to a dead stop, possibly due to the problem with limited
1465 float precision at some angle joins in the BSP hull.
1467 Try fixing by pushing one pixel in each direction.
1469 This is a hack, but in the interest of good gameplay...
1470 ======================
1472 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1477 VectorCopy (ent->fields.server->origin, oldorg);
1480 for (i=0 ; i<8 ; i++)
1482 // try pushing a little in an axial direction
1485 case 0: dir[0] = 2; dir[1] = 0; break;
1486 case 1: dir[0] = 0; dir[1] = 2; break;
1487 case 2: dir[0] = -2; dir[1] = 0; break;
1488 case 3: dir[0] = 0; dir[1] = -2; break;
1489 case 4: dir[0] = 2; dir[1] = 2; break;
1490 case 5: dir[0] = -2; dir[1] = 2; break;
1491 case 6: dir[0] = 2; dir[1] = -2; break;
1492 case 7: dir[0] = -2; dir[1] = -2; break;
1495 SV_PushEntity (ent, dir, false);
1497 // retry the original move
1498 ent->fields.server->velocity[0] = oldvel[0];
1499 ent->fields.server->velocity[1] = oldvel[1];
1500 ent->fields.server->velocity[2] = 0;
1501 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1503 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1504 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1506 Con_DPrint("TryUnstick - success.\n");
1510 // go back to the original pos and try again
1511 VectorCopy (oldorg, ent->fields.server->origin);
1515 VectorClear (ent->fields.server->velocity);
1516 Con_DPrint("TryUnstick - failure.\n");
1522 =====================
1525 Only used by players
1526 ======================
1528 void SV_WalkMove (prvm_edict_t *ent)
1530 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1531 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1534 // if frametime is 0 (due to client sending the same timestamp twice),
1536 if (sv.frametime <= 0)
1539 SV_CheckVelocity(ent);
1541 // do a regular slide move unless it looks like you ran into a step
1542 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1543 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1545 VectorCopy (ent->fields.server->origin, start_origin);
1546 VectorCopy (ent->fields.server->velocity, start_velocity);
1548 clip = SV_FlyMove (ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1550 SV_CheckVelocity(ent);
1552 VectorCopy(ent->fields.server->origin, originalmove_origin);
1553 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1554 originalmove_clip = clip;
1555 originalmove_flags = (int)ent->fields.server->flags;
1556 originalmove_groundentity = ent->fields.server->groundentity;
1558 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1561 if (sv_nostep.integer)
1564 // if move didn't block on a step, return
1567 // if move was not trying to move into the step, return
1568 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1571 if (ent->fields.server->movetype != MOVETYPE_FLY)
1573 // return if gibbed by a trigger
1574 if (ent->fields.server->movetype != MOVETYPE_WALK)
1577 // only step up while jumping if that is enabled
1578 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1579 if (!oldonground && ent->fields.server->waterlevel == 0)
1583 // try moving up and forward to go up a step
1584 // back to start pos
1585 VectorCopy (start_origin, ent->fields.server->origin);
1586 VectorCopy (start_velocity, ent->fields.server->velocity);
1589 VectorClear (upmove);
1590 upmove[2] = sv_stepheight.value;
1591 // FIXME: don't link?
1592 SV_PushEntity(ent, upmove, false);
1595 ent->fields.server->velocity[2] = 0;
1596 clip = SV_FlyMove (ent, sv.frametime, stepnormal, SV_GenericHitSuperContentsMask(ent));
1597 ent->fields.server->velocity[2] += start_velocity[2];
1599 SV_CheckVelocity(ent);
1601 // check for stuckness, possibly due to the limited precision of floats
1602 // in the clipping hulls
1604 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1605 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1607 //Con_Printf("wall\n");
1608 // stepping up didn't make any progress, revert to original move
1609 VectorCopy(originalmove_origin, ent->fields.server->origin);
1610 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1611 //clip = originalmove_clip;
1612 ent->fields.server->flags = originalmove_flags;
1613 ent->fields.server->groundentity = originalmove_groundentity;
1614 // now try to unstick if needed
1615 //clip = SV_TryUnstick (ent, oldvel);
1619 //Con_Printf("step - ");
1621 // extra friction based on view angle
1622 if (clip & 2 && sv_wallfriction.integer)
1623 SV_WallFriction (ent, stepnormal);
1625 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1626 else if (!(sv_gameplayfix_stepdown.integer && ent->fields.server->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->fields.server->flags & FL_ONGROUND)))
1630 VectorClear (downmove);
1631 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1632 // FIXME: don't link?
1633 downtrace = SV_PushEntity (ent, downmove, false);
1635 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1637 // this has been disabled so that you can't jump when you are stepping
1638 // up while already jumping (also known as the Quake2 stair jump bug)
1640 // LordHavoc: disabled this check so you can walk on monsters/players
1641 //if (ent->fields.server->solid == SOLID_BSP)
1643 //Con_Printf("onground\n");
1644 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1645 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1651 //Con_Printf("slope\n");
1652 // if the push down didn't end up on good ground, use the move without
1653 // the step up. This happens near wall / slope combinations, and can
1654 // cause the player to hop up higher on a slope too steep to climb
1655 VectorCopy(originalmove_origin, ent->fields.server->origin);
1656 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1657 //clip = originalmove_clip;
1658 ent->fields.server->flags = originalmove_flags;
1659 ent->fields.server->groundentity = originalmove_groundentity;
1662 SV_CheckVelocity(ent);
1665 //============================================================================
1671 Entities that are "stuck" to another entity
1674 void SV_Physics_Follow (prvm_edict_t *ent)
1676 vec3_t vf, vr, vu, angles, v;
1680 if (!SV_RunThink (ent))
1683 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1684 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1685 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])
1687 // quick case for no rotation
1688 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1692 angles[0] = -ent->fields.server->punchangle[0];
1693 angles[1] = ent->fields.server->punchangle[1];
1694 angles[2] = ent->fields.server->punchangle[2];
1695 AngleVectors (angles, vf, vr, vu);
1696 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];
1697 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];
1698 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];
1699 angles[0] = -e->fields.server->angles[0];
1700 angles[1] = e->fields.server->angles[1];
1701 angles[2] = e->fields.server->angles[2];
1702 AngleVectors (angles, vf, vr, vu);
1703 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1704 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1705 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1707 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1708 SV_LinkEdict (ent, true);
1712 ==============================================================================
1716 ==============================================================================
1721 SV_CheckWaterTransition
1725 void SV_CheckWaterTransition (prvm_edict_t *ent)
1728 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1729 if (!ent->fields.server->watertype)
1731 // just spawned here
1732 ent->fields.server->watertype = cont;
1733 ent->fields.server->waterlevel = 1;
1737 // DRESK - Support for Entity Contents Transition Event
1738 // NOTE: Call here BEFORE updating the watertype below,
1739 // and suppress watersplash sound if a valid function
1740 // call was made to allow for custom "splash" sounds.
1741 if( !SV_CheckContentsTransition(ent, cont) )
1742 { // Contents Transition Function Invalid; Potentially Play Water Sound
1743 // check if the entity crossed into or out of water
1744 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1745 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1748 if (cont <= CONTENTS_WATER)
1750 ent->fields.server->watertype = cont;
1751 ent->fields.server->waterlevel = 1;
1755 ent->fields.server->watertype = CONTENTS_EMPTY;
1756 ent->fields.server->waterlevel = 0;
1764 Toss, bounce, and fly movement. When onground, do nothing.
1767 void SV_Physics_Toss (prvm_edict_t *ent)
1772 // if onground, return without moving
1773 if ((int)ent->fields.server->flags & FL_ONGROUND)
1775 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1777 // don't stick to ground if onground and moving upward
1778 ent->fields.server->flags -= FL_ONGROUND;
1780 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1782 // we can trust FL_ONGROUND if groundentity is world because it never moves
1785 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1787 // if ent was supported by a brush model on previous frame,
1788 // and groundentity is now freed, set groundentity to 0 (world)
1789 // which leaves it suspended in the air
1790 ent->fields.server->groundentity = 0;
1794 ent->priv.server->suspendedinairflag = false;
1796 SV_CheckVelocity (ent);
1799 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1800 SV_AddGravity (ent);
1803 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1806 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1807 trace = SV_PushEntity (ent, move, true);
1808 if (ent->priv.server->free)
1810 if (trace.bmodelstartsolid)
1812 // try to unstick the entity
1813 SV_UnstickEntity(ent);
1814 trace = SV_PushEntity (ent, move, false);
1815 if (ent->priv.server->free)
1819 if (trace.fraction < 1)
1821 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1823 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1824 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1826 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1829 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1830 // LordHavoc: fixed grenades not bouncing when fired down a slope
1831 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1833 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1834 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1836 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1837 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1838 VectorClear (ent->fields.server->velocity);
1839 VectorClear (ent->fields.server->avelocity);
1842 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1846 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1848 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1849 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1850 VectorClear (ent->fields.server->velocity);
1851 VectorClear (ent->fields.server->avelocity);
1854 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1859 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1860 if (trace.plane.normal[2] > 0.7)
1862 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1863 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1864 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1865 ent->priv.server->suspendedinairflag = true;
1866 VectorClear (ent->fields.server->velocity);
1867 VectorClear (ent->fields.server->avelocity);
1870 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1874 // check for in water
1875 SV_CheckWaterTransition (ent);
1879 ===============================================================================
1883 ===============================================================================
1890 Monsters freefall when they don't have a ground entity, otherwise
1891 all movement is done with discrete steps.
1893 This is also used for objects that have become still on the ground, but
1894 will fall if the floor is pulled out from under them.
1897 void SV_Physics_Step (prvm_edict_t *ent)
1899 int flags = (int)ent->fields.server->flags;
1900 // don't fall at all if fly/swim
1901 if (!(flags & (FL_FLY | FL_SWIM)))
1903 if (flags & FL_ONGROUND)
1905 // freefall if onground and moving upward
1906 // freefall if not standing on a world surface (it may be a lift or trap door)
1907 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1909 ent->fields.server->flags -= FL_ONGROUND;
1911 SV_CheckVelocity(ent);
1912 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1913 SV_LinkEdict(ent, true);
1918 // freefall if not onground
1919 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1922 SV_CheckVelocity(ent);
1923 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1924 SV_LinkEdict(ent, true);
1927 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1928 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1935 SV_CheckWaterTransition(ent);
1938 //============================================================================
1940 static void SV_Physics_Entity (prvm_edict_t *ent)
1942 // don't run a move on newly spawned projectiles as it messes up movement
1943 // interpolation and rocket trails
1944 qboolean runmove = ent->priv.server->move;
1945 ent->priv.server->move = true;
1946 switch ((int) ent->fields.server->movetype)
1949 case MOVETYPE_FAKEPUSH:
1950 SV_Physics_Pusher (ent);
1953 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1954 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1957 case MOVETYPE_FOLLOW:
1958 SV_Physics_Follow (ent);
1960 case MOVETYPE_NOCLIP:
1961 if (SV_RunThink(ent))
1964 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1965 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1967 SV_LinkEdict(ent, false);
1970 SV_Physics_Step (ent);
1973 if (SV_RunThink (ent))
1975 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1976 SV_AddGravity (ent);
1977 SV_CheckStuck (ent);
1979 SV_LinkEdict (ent, true);
1983 case MOVETYPE_BOUNCE:
1984 case MOVETYPE_BOUNCEMISSILE:
1985 case MOVETYPE_FLYMISSILE:
1988 if (SV_RunThink (ent) && runmove)
1989 SV_Physics_Toss (ent);
1992 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1997 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1999 SV_ApplyClientMove();
2000 // make sure the velocity is sane (not a NaN)
2001 SV_CheckVelocity(ent);
2002 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
2003 if (prog->funcoffsets.SV_PlayerPhysics && sv_playerphysicsqc.integer)
2005 prog->globals.server->time = sv.time;
2006 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2007 PRVM_ExecuteProgram (prog->funcoffsets.SV_PlayerPhysics, "QC function SV_PlayerPhysics is missing");
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);
2019 // call standard client pre-think
2020 prog->globals.server->time = sv.time;
2021 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2022 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2023 SV_CheckVelocity (ent);
2025 switch ((int) ent->fields.server->movetype)
2028 case MOVETYPE_FAKEPUSH:
2029 SV_Physics_Pusher (ent);
2032 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2033 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2036 case MOVETYPE_FOLLOW:
2037 SV_Physics_Follow (ent);
2039 case MOVETYPE_NOCLIP:
2040 if (SV_RunThink(ent))
2043 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2044 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2048 SV_Physics_Step (ent);
2051 if (SV_RunThink (ent))
2053 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2054 SV_AddGravity (ent);
2055 SV_CheckStuck (ent);
2060 case MOVETYPE_BOUNCE:
2061 case MOVETYPE_BOUNCEMISSILE:
2062 case MOVETYPE_FLYMISSILE:
2064 if (SV_RunThink (ent))
2065 SV_Physics_Toss (ent);
2068 if (SV_RunThink (ent))
2070 SV_CheckWater (ent);
2075 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2079 SV_CheckVelocity (ent);
2081 // call standard player post-think
2082 SV_LinkEdict (ent, true);
2084 SV_CheckVelocity (ent);
2086 prog->globals.server->time = sv.time;
2087 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2088 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2097 void SV_Physics (void)
2102 // let the progs know that a new frame has started
2103 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2104 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2105 prog->globals.server->time = sv.time;
2106 prog->globals.server->frametime = sv.frametime;
2107 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2110 // treat each object in turn
2113 // if force_retouch, relink all the entities
2114 if (prog->globals.server->force_retouch > 0)
2115 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2116 if (!ent->priv.server->free)
2117 SV_LinkEdict (ent, true); // force retouch even for stationary
2119 // run physics on the client entities
2120 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2122 if (!ent->priv.server->free)
2124 // don't do physics on disconnected clients, FrikBot relies on this
2125 if (!host_client->spawned)
2126 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2127 // don't run physics here if running asynchronously
2128 else if (host_client->clmovement_skipphysicsframes > 0)
2129 host_client->clmovement_skipphysicsframes--;
2131 SV_Physics_ClientEntity(ent);
2135 // run physics on all the non-client entities
2136 if (!sv_freezenonclients.integer)
2137 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2138 if (!ent->priv.server->free)
2139 SV_Physics_Entity(ent);
2141 if (prog->globals.server->force_retouch > 0)
2142 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2144 // LordHavoc: endframe support
2145 if (prog->funcoffsets.EndFrame)
2147 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2148 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2149 prog->globals.server->time = sv.time;
2150 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2153 // decrement prog->num_edicts if the highest number entities died
2154 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2156 if (!sv_freezenonclients.integer)
2157 sv.time += sv.frametime;