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;
124 int i, bodysupercontents;
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 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
245 // might interact, so do an exact clip
247 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
249 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
250 // if the modelindex is 0, it shouldn't be SOLID_BSP!
251 if (modelindex > 0 && modelindex < MAX_MODELS)
252 model = sv.models[(int)touch->fields.server->modelindex];
255 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);
257 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
258 Matrix4x4_Invert_Simple(&imatrix, &matrix);
259 if ((int)touch->fields.server->flags & FL_MONSTER)
260 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
262 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
264 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
270 #if COLLISIONPARANOID >= 1
271 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)
276 trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
279 VectorCopy(trace.endpos, temp);
280 endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
281 #if COLLISIONPARANOID < 3
282 if (trace.startsolid || endstuck)
284 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" : "");
291 ===============================================================================
293 Linking entities into the world culling system
295 ===============================================================================
298 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
300 int i, numtouchedicts, old_self, old_other;
301 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
303 // build a list of edicts to touch, because the link loop can be corrupted
304 // by SV_IncreaseEdicts called during touch functions
305 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
306 if (numtouchedicts > MAX_EDICTS)
308 // this never happens
309 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
310 numtouchedicts = MAX_EDICTS;
313 old_self = prog->globals.server->self;
314 old_other = prog->globals.server->other;
315 for (i = 0;i < numtouchedicts;i++)
317 touch = touchedicts[i];
318 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
321 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
322 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
323 prog->globals.server->time = sv.time;
324 prog->globals.server->trace_allsolid = false;
325 prog->globals.server->trace_startsolid = false;
326 prog->globals.server->trace_fraction = 1;
327 prog->globals.server->trace_inwater = false;
328 prog->globals.server->trace_inopen = true;
329 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
330 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
331 prog->globals.server->trace_plane_dist = 0;
332 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
333 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
335 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
337 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
339 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
341 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
344 prog->globals.server->self = old_self;
345 prog->globals.server->other = old_other;
354 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
359 if (ent == prog->edicts)
360 return; // don't add the world
362 if (ent->priv.server->free)
367 if (ent->fields.server->solid == SOLID_BSP)
369 int modelindex = (int)ent->fields.server->modelindex;
370 if (modelindex < 0 || modelindex > MAX_MODELS)
372 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
375 model = sv.models[modelindex];
378 if (!model->TraceBox)
379 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
381 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
383 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
384 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
386 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
388 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
389 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
393 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
394 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
399 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
400 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
401 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
406 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
407 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
411 // to make items easier to pick up and allow them to be grabbed off
412 // of shelves, the abs sizes are expanded
414 if ((int)ent->fields.server->flags & FL_ITEM)
425 // because movement is clipped an epsilon away from an actual edge,
426 // we must fully check even when bounding boxes don't quite touch
435 VectorCopy(mins, ent->fields.server->absmin);
436 VectorCopy(maxs, ent->fields.server->absmax);
438 World_LinkEdict(&sv.world, ent, mins, maxs);
440 // if touch_triggers, call touch on all entities overlapping this box
441 if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
442 SV_LinkEdict_TouchAreaGrid(ent);
446 ===============================================================================
450 ===============================================================================
455 SV_TestEntityPosition
457 returns true if the entity is in solid currently
460 static int SV_TestEntityPosition (prvm_edict_t *ent)
462 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);
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...
474 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, m1);
475 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, m2);
476 VectorSubtract(m2, m1, s);
477 #define EPSILON (1.0f / 32.0f)
478 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
479 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
480 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
481 for (i = 0;i < 8;i++)
483 v[0] = (i & 1) ? m2[0] : m1[0];
484 v[1] = (i & 2) ? m2[1] : m1[1];
485 v[2] = (i & 4) ? m2[2] : m1[2];
486 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
499 void SV_CheckAllEnts (void)
504 // see if any solid entities are inside the final position
505 check = PRVM_NEXT_EDICT(prog->edicts);
506 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
508 if (check->priv.server->free)
510 if (check->fields.server->movetype == MOVETYPE_PUSH
511 || check->fields.server->movetype == MOVETYPE_NONE
512 || check->fields.server->movetype == MOVETYPE_FOLLOW
513 || check->fields.server->movetype == MOVETYPE_NOCLIP)
516 if (SV_TestEntityPosition (check))
517 Con_Print("entity in invalid position\n");
521 // DRESK - Support for Entity Contents Transition Event
524 SV_CheckContentsTransition
526 returns true if entity had a valid contentstransition function call
529 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
531 int bValidFunctionCall;
532 prvm_eval_t *contentstransition;
534 // Default Valid Function Call to False
535 bValidFunctionCall = false;
537 if(ent->fields.server->watertype != nContents)
538 { // Changed Contents
539 // Acquire Contents Transition Function from QC
540 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
542 if(contentstransition->function)
543 { // Valid Function; Execute
544 // Assign Valid Function
545 bValidFunctionCall = true;
546 // Prepare Parameters (Original Contents, New Contents)
548 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
550 PRVM_G_FLOAT(OFS_PARM1) = nContents;
551 // Execute VM Function
552 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
556 // Return if Function Call was Valid
557 return bValidFunctionCall;
566 void SV_CheckVelocity (prvm_edict_t *ent)
574 for (i=0 ; i<3 ; i++)
576 if (IS_NAN(ent->fields.server->velocity[i]))
578 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
579 ent->fields.server->velocity[i] = 0;
581 if (IS_NAN(ent->fields.server->origin[i]))
583 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
584 ent->fields.server->origin[i] = 0;
588 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
589 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
590 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
592 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
593 ent->fields.server->velocity[0] *= wishspeed;
594 ent->fields.server->velocity[1] *= wishspeed;
595 ent->fields.server->velocity[2] *= wishspeed;
603 Runs thinking code if time. There is some play in the exact time the think
604 function will be called, because it is called before any movement is done
605 in a frame. Not used for pushmove objects, because they must be exact.
606 Returns false if the entity removed itself.
609 qboolean SV_RunThink (prvm_edict_t *ent)
613 thinktime = ent->fields.server->nextthink;
614 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
617 // don't let things stay in the past.
618 // it is possible to start that way by a trigger with a local time.
619 if (thinktime < sv.time)
622 ent->fields.server->nextthink = 0;
623 prog->globals.server->time = thinktime;
624 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
625 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
626 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
627 return !ent->priv.server->free;
634 Two entities have touched, so run their touch functions
637 extern void VM_SetTraceGlobals(const trace_t *trace);
638 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
640 int old_self, old_other;
641 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
644 old_self = prog->globals.server->self;
645 old_other = prog->globals.server->other;
647 VM_SetTraceGlobals(trace);
649 prog->globals.server->time = sv.time;
650 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
652 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
653 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
654 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
657 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
659 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
660 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
661 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
662 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
663 prog->globals.server->trace_plane_dist = -trace->plane.dist;
664 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
665 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
667 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
669 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
671 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
673 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
676 prog->globals.server->self = old_self;
677 prog->globals.server->other = old_other;
685 Slide off of the impacting object
686 returns the blocked flags (1 = floor, 2 = step / wall)
689 #define STOP_EPSILON 0.1
690 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
695 backoff = -DotProduct (in, normal) * overbounce;
696 VectorMA(in, backoff, normal, out);
698 for (i = 0;i < 3;i++)
699 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
708 The basic solid body movement clip that slides along multiple planes
709 Returns the clipflags if the velocity was modified (hit something solid)
713 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
716 // LordHavoc: increased from 5 to 32
717 #define MAX_CLIP_PLANES 32
718 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
720 int blocked, bumpcount;
721 int i, j, impact, numplanes;
723 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
728 VectorCopy(ent->fields.server->velocity, original_velocity);
729 VectorCopy(ent->fields.server->velocity, primal_velocity);
732 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
734 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
737 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
738 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
740 //if (trace.fraction < 0.002)
745 VectorCopy(ent->fields.server->origin, start);
746 start[2] += 3;//0.03125;
747 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
748 end[2] += 3;//0.03125;
749 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
750 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)))
752 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
758 for (i = 0;i < numplanes;i++)
760 VectorCopy(ent->fields.server->origin, start);
761 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
762 VectorMA(start, 3, planes[i], start);
763 VectorMA(end, 3, planes[i], end);
764 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
765 if (trace.fraction < testtrace.fraction)
768 VectorCopy(start, ent->fields.server->origin);
773 // VectorAdd(ent->fields.server->origin, planes[j], start);
779 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);
780 if (trace.fraction < 1)
781 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
786 if (trace.bmodelstartsolid)
788 // LordHavoc: note: this code is what makes entities stick in place
789 // if embedded in world only (you can walk through other objects if
791 // entity is trapped in another solid
792 VectorClear(ent->fields.server->velocity);
797 // break if it moved the entire distance
798 if (trace.fraction == 1)
800 VectorCopy(trace.endpos, ent->fields.server->origin);
806 Con_Printf ("SV_FlyMove: !trace.ent");
807 trace.ent = prog->edicts;
810 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
814 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
818 if (trace.plane.normal[2])
820 if (trace.plane.normal[2] > 0.7)
824 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
825 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
832 // save the trace for player extrafriction
834 VectorCopy(trace.plane.normal, stepnormal);
837 if (trace.fraction >= 0.001)
839 // actually covered some distance
840 VectorCopy(trace.endpos, ent->fields.server->origin);
841 VectorCopy(ent->fields.server->velocity, original_velocity);
845 // run the impact function
848 SV_Impact(ent, &trace);
850 // break if removed by the impact function
851 if (ent->priv.server->free)
855 time_left *= 1 - trace.fraction;
857 // clipped to another plane
858 if (numplanes >= MAX_CLIP_PLANES)
860 // this shouldn't really happen
861 VectorClear(ent->fields.server->velocity);
867 for (i = 0;i < numplanes;i++)
868 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
872 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
877 VectorCopy(trace.plane.normal, planes[numplanes]);
880 if (sv_newflymove.integer)
881 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
884 // modify original_velocity so it parallels all of the clip planes
885 for (i = 0;i < numplanes;i++)
887 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
888 for (j = 0;j < numplanes;j++)
893 if (DotProduct(new_velocity, planes[j]) < 0)
903 // go along this plane
904 VectorCopy(new_velocity, ent->fields.server->velocity);
908 // go along the crease
911 VectorClear(ent->fields.server->velocity);
915 CrossProduct(planes[0], planes[1], dir);
916 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
917 VectorNormalize(dir);
918 d = DotProduct(dir, ent->fields.server->velocity);
919 VectorScale(dir, d, ent->fields.server->velocity);
923 // if current velocity is against the original velocity,
924 // stop dead to avoid tiny occilations in sloping corners
925 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
927 VectorClear(ent->fields.server->velocity);
932 //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]);
935 if ((blocked & 1) == 0 && bumpcount > 1)
937 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
938 // flag ONGROUND if there's ground under it
939 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
943 // LordHavoc: this came from QW and allows you to get out of water more easily
944 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
945 VectorCopy(primal_velocity, ent->fields.server->velocity);
955 void SV_AddGravity (prvm_edict_t *ent)
960 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
961 if (val!=0 && val->_float)
962 ent_gravity = val->_float;
965 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
970 ===============================================================================
974 ===============================================================================
981 Does not change the entities velocity at all
984 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
990 VectorAdd (ent->fields.server->origin, push, end);
992 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
994 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
995 type = MOVE_NOMONSTERS; // only clip against bmodels
999 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1000 if (trace.bmodelstartsolid && failonbmodelstartsolid)
1003 VectorCopy (trace.endpos, ent->fields.server->origin);
1004 SV_LinkEdict (ent, true);
1006 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)))
1007 SV_Impact (ent, &trace);
1018 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1021 float savesolid, movetime2, pushltime;
1022 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1024 int numcheckentities;
1025 static prvm_edict_t *checkentities[MAX_EDICTS];
1026 model_t *pushermodel;
1028 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1030 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])
1032 pusher->fields.server->ltime += movetime;
1036 switch ((int) pusher->fields.server->solid)
1038 // LordHavoc: valid pusher types
1041 case SOLID_SLIDEBOX:
1042 case SOLID_CORPSE: // LordHavoc: this would be weird...
1044 // LordHavoc: no collisions
1047 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1048 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1049 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1050 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1051 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1052 pusher->fields.server->ltime += movetime;
1053 SV_LinkEdict (pusher, false);
1056 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1059 index = (int) pusher->fields.server->modelindex;
1060 if (index < 1 || index >= MAX_MODELS)
1062 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1065 pushermodel = sv.models[index];
1067 movetime2 = movetime;
1068 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1069 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1070 if (moveangle[0] || moveangle[2])
1072 for (i = 0;i < 3;i++)
1076 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1077 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1081 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1082 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1086 else if (moveangle[1])
1088 for (i = 0;i < 3;i++)
1092 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1093 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1097 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1098 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1104 for (i = 0;i < 3;i++)
1108 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1109 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1113 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1114 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1119 VectorNegate (moveangle, a);
1120 AngleVectorsFLU (a, forward, left, up);
1122 VectorCopy (pusher->fields.server->origin, pushorig);
1123 VectorCopy (pusher->fields.server->angles, pushang);
1124 pushltime = pusher->fields.server->ltime;
1126 // move the pusher to its final position
1128 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1129 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1130 pusher->fields.server->ltime += movetime;
1131 SV_LinkEdict (pusher, false);
1134 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1135 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1136 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);
1137 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1139 savesolid = pusher->fields.server->solid;
1141 // see if any solid entities are inside the final position
1144 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1145 for (e = 0;e < numcheckentities;e++)
1147 prvm_edict_t *check = checkentities[e];
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 (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
1158 // remove the onground flag for non-players
1159 if (check->fields.server->movetype != MOVETYPE_WALK)
1160 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1164 // if the entity is not inside the pusher's final position, leave it alone
1165 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);
1166 if (!trace.startsolid)
1171 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1174 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1175 org2[0] = DotProduct (org, forward);
1176 org2[1] = DotProduct (org, left);
1177 org2[2] = DotProduct (org, up);
1178 VectorSubtract (org2, org, move);
1179 VectorAdd (move, move1, move);
1182 VectorCopy (move1, move);
1184 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1185 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1186 sv.moved_edicts[num_moved++] = check;
1188 // try moving the contacted entity
1189 pusher->fields.server->solid = SOLID_NOT;
1190 trace = SV_PushEntity (check, move, true);
1191 // FIXME: turn players specially
1192 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1193 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1194 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1196 // if it is still inside the pusher, block
1197 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);
1198 if (trace.startsolid)
1200 // try moving the contacted entity a tiny bit further to account for precision errors
1202 pusher->fields.server->solid = SOLID_NOT;
1203 VectorScale(move, 1.1, move2);
1204 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1205 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1206 SV_PushEntity (check, move2, true);
1207 pusher->fields.server->solid = savesolid;
1208 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);
1209 if (trace.startsolid)
1211 // try moving the contacted entity a tiny bit less to account for precision errors
1212 pusher->fields.server->solid = SOLID_NOT;
1213 VectorScale(move, 0.9, move2);
1214 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1215 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1216 SV_PushEntity (check, move2, true);
1217 pusher->fields.server->solid = savesolid;
1218 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);
1219 if (trace.startsolid)
1221 // still inside pusher, so it's really blocked
1224 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1226 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1229 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1230 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1234 VectorCopy (pushorig, pusher->fields.server->origin);
1235 VectorCopy (pushang, pusher->fields.server->angles);
1236 pusher->fields.server->ltime = pushltime;
1237 SV_LinkEdict (pusher, false);
1239 // move back any entities we already moved
1240 for (i = 0;i < num_moved;i++)
1242 prvm_edict_t *ed = sv.moved_edicts[i];
1243 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1244 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1245 SV_LinkEdict (ed, false);
1248 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1249 if (pusher->fields.server->blocked)
1251 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1252 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1253 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1260 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1261 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1262 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1271 void SV_Physics_Pusher (prvm_edict_t *ent)
1273 float thinktime, oldltime, movetime;
1275 oldltime = ent->fields.server->ltime;
1277 thinktime = ent->fields.server->nextthink;
1278 if (thinktime < ent->fields.server->ltime + sv.frametime)
1280 movetime = thinktime - ent->fields.server->ltime;
1285 movetime = sv.frametime;
1288 // advances ent->fields.server->ltime if not blocked
1289 SV_PushMove (ent, movetime);
1291 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1293 ent->fields.server->nextthink = 0;
1294 prog->globals.server->time = sv.time;
1295 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1296 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1297 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1303 ===============================================================================
1307 ===============================================================================
1314 This is a big hack to try and fix the rare case of getting stuck in the world
1318 void SV_CheckStuck (prvm_edict_t *ent)
1323 if (!SV_TestEntityPosition(ent))
1325 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1329 VectorCopy (ent->fields.server->origin, org);
1331 for (z=-1 ; z< 18 ; z++)
1332 for (i=-1 ; i <= 1 ; i++)
1333 for (j=-1 ; j <= 1 ; j++)
1335 ent->fields.server->origin[0] = org[0] + i;
1336 ent->fields.server->origin[1] = org[1] + j;
1337 ent->fields.server->origin[2] = org[2] + z;
1338 if (!SV_TestEntityPosition(ent))
1340 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);
1341 SV_LinkEdict (ent, true);
1346 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
1347 if (!SV_TestEntityPosition(ent))
1349 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1350 SV_LinkEdict (ent, true);
1354 VectorCopy (org, ent->fields.server->origin);
1355 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1358 static void SV_UnstickEntity (prvm_edict_t *ent)
1363 // if not stuck in a bmodel, just return
1364 if (!SV_TestEntityPosition(ent))
1367 VectorCopy (ent->fields.server->origin, org);
1369 for (z=-1 ; z< 18 ; z += 6)
1370 for (i=-1 ; i <= 1 ; i++)
1371 for (j=-1 ; j <= 1 ; j++)
1373 ent->fields.server->origin[0] = org[0] + i;
1374 ent->fields.server->origin[1] = org[1] + j;
1375 ent->fields.server->origin[2] = org[2] + z;
1376 if (!SV_TestEntityPosition(ent))
1378 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);
1379 SV_LinkEdict (ent, true);
1384 VectorCopy (org, ent->fields.server->origin);
1385 if (developer.integer >= 100)
1386 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1395 qboolean SV_CheckWater (prvm_edict_t *ent)
1398 int nNativeContents;
1401 point[0] = ent->fields.server->origin[0];
1402 point[1] = ent->fields.server->origin[1];
1403 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1405 // DRESK - Support for Entity Contents Transition Event
1406 // NOTE: Some logic needed to be slightly re-ordered
1407 // to not affect performance and allow for the feature.
1409 // Acquire Super Contents Prior to Resets
1410 cont = SV_PointSuperContents(point);
1411 // Acquire Native Contents Here
1412 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1414 // DRESK - Support for Entity Contents Transition Event
1415 if(ent->fields.server->watertype)
1416 // Entity did NOT Spawn; Check
1417 SV_CheckContentsTransition(ent, nNativeContents);
1420 ent->fields.server->waterlevel = 0;
1421 ent->fields.server->watertype = CONTENTS_EMPTY;
1422 cont = SV_PointSuperContents(point);
1423 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1425 ent->fields.server->watertype = nNativeContents;
1426 ent->fields.server->waterlevel = 1;
1427 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1428 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1430 ent->fields.server->waterlevel = 2;
1431 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1432 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1433 ent->fields.server->waterlevel = 3;
1437 return ent->fields.server->waterlevel > 1;
1446 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1449 vec3_t forward, into, side;
1451 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1452 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1454 // cut the tangential velocity
1455 i = DotProduct (stepnormal, ent->fields.server->velocity);
1456 VectorScale (stepnormal, i, into);
1457 VectorSubtract (ent->fields.server->velocity, into, side);
1458 ent->fields.server->velocity[0] = side[0] * (1 + d);
1459 ent->fields.server->velocity[1] = side[1] * (1 + d);
1465 =====================
1468 Player has come to a dead stop, possibly due to the problem with limited
1469 float precision at some angle joins in the BSP hull.
1471 Try fixing by pushing one pixel in each direction.
1473 This is a hack, but in the interest of good gameplay...
1474 ======================
1476 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1481 VectorCopy (ent->fields.server->origin, oldorg);
1484 for (i=0 ; i<8 ; i++)
1486 // try pushing a little in an axial direction
1489 case 0: dir[0] = 2; dir[1] = 0; break;
1490 case 1: dir[0] = 0; dir[1] = 2; break;
1491 case 2: dir[0] = -2; dir[1] = 0; break;
1492 case 3: dir[0] = 0; dir[1] = -2; break;
1493 case 4: dir[0] = 2; dir[1] = 2; break;
1494 case 5: dir[0] = -2; dir[1] = 2; break;
1495 case 6: dir[0] = 2; dir[1] = -2; break;
1496 case 7: dir[0] = -2; dir[1] = -2; break;
1499 SV_PushEntity (ent, dir, false);
1501 // retry the original move
1502 ent->fields.server->velocity[0] = oldvel[0];
1503 ent->fields.server->velocity[1] = oldvel[1];
1504 ent->fields.server->velocity[2] = 0;
1505 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1507 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1508 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1510 Con_DPrint("TryUnstick - success.\n");
1514 // go back to the original pos and try again
1515 VectorCopy (oldorg, ent->fields.server->origin);
1519 VectorClear (ent->fields.server->velocity);
1520 Con_DPrint("TryUnstick - failure.\n");
1526 =====================
1529 Only used by players
1530 ======================
1532 void SV_WalkMove (prvm_edict_t *ent)
1534 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1535 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1538 // if frametime is 0 (due to client sending the same timestamp twice),
1540 if (sv.frametime <= 0)
1543 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1545 SV_CheckVelocity(ent);
1547 // do a regular slide move unless it looks like you ran into a step
1548 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1549 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1551 VectorCopy (ent->fields.server->origin, start_origin);
1552 VectorCopy (ent->fields.server->velocity, start_velocity);
1554 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1556 SV_CheckVelocity(ent);
1558 VectorCopy(ent->fields.server->origin, originalmove_origin);
1559 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1560 originalmove_clip = clip;
1561 originalmove_flags = (int)ent->fields.server->flags;
1562 originalmove_groundentity = ent->fields.server->groundentity;
1564 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1567 if (sv_nostep.integer)
1570 // if move didn't block on a step, return
1573 // if move was not trying to move into the step, return
1574 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1577 if (ent->fields.server->movetype != MOVETYPE_FLY)
1579 // return if gibbed by a trigger
1580 if (ent->fields.server->movetype != MOVETYPE_WALK)
1583 // only step up while jumping if that is enabled
1584 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1585 if (!oldonground && ent->fields.server->waterlevel == 0)
1589 // try moving up and forward to go up a step
1590 // back to start pos
1591 VectorCopy (start_origin, ent->fields.server->origin);
1592 VectorCopy (start_velocity, ent->fields.server->velocity);
1595 VectorClear (upmove);
1596 upmove[2] = sv_stepheight.value;
1597 // FIXME: don't link?
1598 SV_PushEntity(ent, upmove, false);
1601 ent->fields.server->velocity[2] = 0;
1602 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1603 ent->fields.server->velocity[2] += start_velocity[2];
1605 SV_CheckVelocity(ent);
1607 // check for stuckness, possibly due to the limited precision of floats
1608 // in the clipping hulls
1610 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1611 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1613 //Con_Printf("wall\n");
1614 // stepping up didn't make any progress, revert to original move
1615 VectorCopy(originalmove_origin, ent->fields.server->origin);
1616 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1617 //clip = originalmove_clip;
1618 ent->fields.server->flags = originalmove_flags;
1619 ent->fields.server->groundentity = originalmove_groundentity;
1620 // now try to unstick if needed
1621 //clip = SV_TryUnstick (ent, oldvel);
1625 //Con_Printf("step - ");
1627 // extra friction based on view angle
1628 if (clip & 2 && sv_wallfriction.integer)
1629 SV_WallFriction (ent, stepnormal);
1631 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1632 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)))
1636 VectorClear (downmove);
1637 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1638 // FIXME: don't link?
1639 downtrace = SV_PushEntity (ent, downmove, false);
1641 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1643 // this has been disabled so that you can't jump when you are stepping
1644 // up while already jumping (also known as the Quake2 stair jump bug)
1646 // LordHavoc: disabled this check so you can walk on monsters/players
1647 //if (ent->fields.server->solid == SOLID_BSP)
1649 //Con_Printf("onground\n");
1650 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1651 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1657 //Con_Printf("slope\n");
1658 // if the push down didn't end up on good ground, use the move without
1659 // the step up. This happens near wall / slope combinations, and can
1660 // cause the player to hop up higher on a slope too steep to climb
1661 VectorCopy(originalmove_origin, ent->fields.server->origin);
1662 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1663 //clip = originalmove_clip;
1664 ent->fields.server->flags = originalmove_flags;
1665 ent->fields.server->groundentity = originalmove_groundentity;
1668 SV_CheckVelocity(ent);
1671 //============================================================================
1677 Entities that are "stuck" to another entity
1680 void SV_Physics_Follow (prvm_edict_t *ent)
1682 vec3_t vf, vr, vu, angles, v;
1686 if (!SV_RunThink (ent))
1689 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1690 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1691 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])
1693 // quick case for no rotation
1694 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1698 angles[0] = -ent->fields.server->punchangle[0];
1699 angles[1] = ent->fields.server->punchangle[1];
1700 angles[2] = ent->fields.server->punchangle[2];
1701 AngleVectors (angles, vf, vr, vu);
1702 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];
1703 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];
1704 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];
1705 angles[0] = -e->fields.server->angles[0];
1706 angles[1] = e->fields.server->angles[1];
1707 angles[2] = e->fields.server->angles[2];
1708 AngleVectors (angles, vf, vr, vu);
1709 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1710 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1711 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1713 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1714 SV_LinkEdict (ent, true);
1718 ==============================================================================
1722 ==============================================================================
1727 SV_CheckWaterTransition
1731 void SV_CheckWaterTransition (prvm_edict_t *ent)
1734 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1735 if (!ent->fields.server->watertype)
1737 // just spawned here
1738 ent->fields.server->watertype = cont;
1739 ent->fields.server->waterlevel = 1;
1743 // DRESK - Support for Entity Contents Transition Event
1744 // NOTE: Call here BEFORE updating the watertype below,
1745 // and suppress watersplash sound if a valid function
1746 // call was made to allow for custom "splash" sounds.
1747 if( !SV_CheckContentsTransition(ent, cont) )
1748 { // Contents Transition Function Invalid; Potentially Play Water Sound
1749 // check if the entity crossed into or out of water
1750 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1751 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1754 if (cont <= CONTENTS_WATER)
1756 ent->fields.server->watertype = cont;
1757 ent->fields.server->waterlevel = 1;
1761 ent->fields.server->watertype = CONTENTS_EMPTY;
1762 ent->fields.server->waterlevel = 0;
1770 Toss, bounce, and fly movement. When onground, do nothing.
1773 void SV_Physics_Toss (prvm_edict_t *ent)
1778 // if onground, return without moving
1779 if ((int)ent->fields.server->flags & FL_ONGROUND)
1781 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1783 // don't stick to ground if onground and moving upward
1784 ent->fields.server->flags -= FL_ONGROUND;
1786 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1788 // we can trust FL_ONGROUND if groundentity is world because it never moves
1791 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1793 // if ent was supported by a brush model on previous frame,
1794 // and groundentity is now freed, set groundentity to 0 (world)
1795 // which leaves it suspended in the air
1796 ent->fields.server->groundentity = 0;
1800 ent->priv.server->suspendedinairflag = false;
1802 SV_CheckVelocity (ent);
1805 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1806 SV_AddGravity (ent);
1809 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1812 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1813 trace = SV_PushEntity (ent, move, true);
1814 if (ent->priv.server->free)
1816 if (trace.bmodelstartsolid)
1818 // try to unstick the entity
1819 SV_UnstickEntity(ent);
1820 trace = SV_PushEntity (ent, move, false);
1821 if (ent->priv.server->free)
1825 if (trace.fraction < 1)
1827 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1829 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1830 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1832 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1835 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1836 // LordHavoc: fixed grenades not bouncing when fired down a slope
1837 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1839 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1840 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1842 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1843 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1844 VectorClear (ent->fields.server->velocity);
1845 VectorClear (ent->fields.server->avelocity);
1848 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1852 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1854 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1855 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1856 VectorClear (ent->fields.server->velocity);
1857 VectorClear (ent->fields.server->avelocity);
1860 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1865 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1866 if (trace.plane.normal[2] > 0.7)
1868 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1869 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1870 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1871 ent->priv.server->suspendedinairflag = true;
1872 VectorClear (ent->fields.server->velocity);
1873 VectorClear (ent->fields.server->avelocity);
1876 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1880 // check for in water
1881 SV_CheckWaterTransition (ent);
1885 ===============================================================================
1889 ===============================================================================
1896 Monsters freefall when they don't have a ground entity, otherwise
1897 all movement is done with discrete steps.
1899 This is also used for objects that have become still on the ground, but
1900 will fall if the floor is pulled out from under them.
1903 void SV_Physics_Step (prvm_edict_t *ent)
1905 int flags = (int)ent->fields.server->flags;
1906 // don't fall at all if fly/swim
1907 if (!(flags & (FL_FLY | FL_SWIM)))
1909 if (flags & FL_ONGROUND)
1911 // freefall if onground and moving upward
1912 // freefall if not standing on a world surface (it may be a lift or trap door)
1913 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1915 ent->fields.server->flags -= FL_ONGROUND;
1917 SV_CheckVelocity(ent);
1918 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1919 SV_LinkEdict(ent, true);
1924 // freefall if not onground
1925 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1928 SV_CheckVelocity(ent);
1929 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1930 SV_LinkEdict(ent, true);
1933 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1934 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1941 SV_CheckWaterTransition(ent);
1944 //============================================================================
1946 static void SV_Physics_Entity (prvm_edict_t *ent)
1948 // don't run a move on newly spawned projectiles as it messes up movement
1949 // interpolation and rocket trails
1950 qboolean runmove = ent->priv.server->move;
1951 ent->priv.server->move = true;
1952 switch ((int) ent->fields.server->movetype)
1955 case MOVETYPE_FAKEPUSH:
1956 SV_Physics_Pusher (ent);
1959 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1960 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1963 case MOVETYPE_FOLLOW:
1964 SV_Physics_Follow (ent);
1966 case MOVETYPE_NOCLIP:
1967 if (SV_RunThink(ent))
1970 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1971 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1973 SV_LinkEdict(ent, false);
1976 SV_Physics_Step (ent);
1979 if (SV_RunThink (ent))
1981 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1982 SV_AddGravity (ent);
1983 SV_CheckStuck (ent);
1985 SV_LinkEdict (ent, true);
1989 case MOVETYPE_BOUNCE:
1990 case MOVETYPE_BOUNCEMISSILE:
1991 case MOVETYPE_FLYMISSILE:
1994 if (SV_RunThink (ent) && runmove)
1995 SV_Physics_Toss (ent);
1998 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2003 void SV_Physics_ClientEntity (prvm_edict_t *ent)
2005 SV_ApplyClientMove();
2006 // make sure the velocity is sane (not a NaN)
2007 SV_CheckVelocity(ent);
2008 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
2009 if (prog->funcoffsets.SV_PlayerPhysics && sv_playerphysicsqc.integer)
2011 prog->globals.server->time = sv.time;
2012 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2013 PRVM_ExecuteProgram (prog->funcoffsets.SV_PlayerPhysics, "QC function SV_PlayerPhysics is missing");
2017 // make sure the velocity is sane (not a NaN)
2018 SV_CheckVelocity(ent);
2019 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2020 // player_run/player_stand1 does not horribly malfunction if the
2021 // velocity becomes a number that is both == 0 and != 0
2022 // (sounds to me like NaN but to be absolutely safe...)
2023 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2024 VectorClear(ent->fields.server->velocity);
2025 // call standard client pre-think
2026 prog->globals.server->time = sv.time;
2027 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2028 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2029 SV_CheckVelocity (ent);
2031 switch ((int) ent->fields.server->movetype)
2034 case MOVETYPE_FAKEPUSH:
2035 SV_Physics_Pusher (ent);
2038 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2039 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2042 case MOVETYPE_FOLLOW:
2043 SV_Physics_Follow (ent);
2045 case MOVETYPE_NOCLIP:
2046 if (SV_RunThink(ent))
2049 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2050 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2054 SV_Physics_Step (ent);
2057 if (SV_RunThink (ent))
2059 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2060 SV_AddGravity (ent);
2061 SV_CheckStuck (ent);
2066 case MOVETYPE_BOUNCE:
2067 case MOVETYPE_BOUNCEMISSILE:
2068 case MOVETYPE_FLYMISSILE:
2070 if (SV_RunThink (ent))
2071 SV_Physics_Toss (ent);
2074 if (SV_RunThink (ent))
2076 SV_CheckWater (ent);
2081 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2085 SV_CheckVelocity (ent);
2087 // call standard player post-think
2088 SV_LinkEdict (ent, true);
2090 SV_CheckVelocity (ent);
2092 prog->globals.server->time = sv.time;
2093 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2094 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2103 void SV_Physics (void)
2108 // let the progs know that a new frame has started
2109 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2110 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2111 prog->globals.server->time = sv.time;
2112 prog->globals.server->frametime = sv.frametime;
2113 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2116 // treat each object in turn
2119 // if force_retouch, relink all the entities
2120 if (prog->globals.server->force_retouch > 0)
2121 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2122 if (!ent->priv.server->free)
2123 SV_LinkEdict (ent, true); // force retouch even for stationary
2125 // run physics on the client entities
2126 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2128 if (!ent->priv.server->free)
2130 // don't do physics on disconnected clients, FrikBot relies on this
2131 if (!host_client->spawned)
2132 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2133 // don't run physics here if running asynchronously
2134 else if (host_client->clmovement_skipphysicsframes > 0)
2135 host_client->clmovement_skipphysicsframes--;
2137 SV_Physics_ClientEntity(ent);
2141 // run physics on all the non-client entities
2142 if (!sv_freezenonclients.integer)
2143 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2144 if (!ent->priv.server->free)
2145 SV_Physics_Entity(ent);
2147 if (prog->globals.server->force_retouch > 0)
2148 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2150 // LordHavoc: endframe support
2151 if (prog->funcoffsets.EndFrame)
2153 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2154 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2155 prog->globals.server->time = sv.time;
2156 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2159 // decrement prog->num_edicts if the highest number entities died
2160 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2162 if (!sv_freezenonclients.integer)
2163 sv.time += sv.frametime;