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"};
43 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100"};
44 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800"};
45 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000"};
46 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0"};
47 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18"};
48 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "1"};
49 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1"};
50 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0"};
51 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0"};
53 #define MOVE_EPSILON 0.01
55 void SV_Physics_Toss (prvm_edict_t *ent);
57 void SV_Phys_Init (void)
59 Cvar_RegisterVariable(&sv_stepheight);
60 Cvar_RegisterVariable(&sv_jumpstep);
61 Cvar_RegisterVariable(&sv_wallfriction);
62 Cvar_RegisterVariable(&sv_newflymove);
63 Cvar_RegisterVariable(&sv_freezenonclients);
71 void SV_CheckAllEnts (void)
76 // see if any solid entities are inside the final position
77 check = PRVM_NEXT_EDICT(prog->edicts);
78 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
80 if (check->priv.server->free)
82 if (check->fields.server->movetype == MOVETYPE_PUSH
83 || check->fields.server->movetype == MOVETYPE_NONE
84 || check->fields.server->movetype == MOVETYPE_FOLLOW
85 || check->fields.server->movetype == MOVETYPE_NOCLIP)
88 if (SV_TestEntityPosition (check))
89 Con_Print("entity in invalid position\n");
98 void SV_CheckVelocity (prvm_edict_t *ent)
106 for (i=0 ; i<3 ; i++)
108 if (IS_NAN(ent->fields.server->velocity[i]))
110 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
111 ent->fields.server->velocity[i] = 0;
113 if (IS_NAN(ent->fields.server->origin[i]))
115 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
116 ent->fields.server->origin[i] = 0;
120 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
121 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
122 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
124 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
125 ent->fields.server->velocity[0] *= wishspeed;
126 ent->fields.server->velocity[1] *= wishspeed;
127 ent->fields.server->velocity[2] *= wishspeed;
135 Runs thinking code if time. There is some play in the exact time the think
136 function will be called, because it is called before any movement is done
137 in a frame. Not used for pushmove objects, because they must be exact.
138 Returns false if the entity removed itself.
141 qboolean SV_RunThink (prvm_edict_t *ent)
145 thinktime = ent->fields.server->nextthink;
146 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
149 // don't let things stay in the past.
150 // it is possible to start that way by a trigger with a local time.
151 if (thinktime < sv.time)
154 ent->fields.server->nextthink = 0;
155 prog->globals.server->time = thinktime;
156 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
157 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
158 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
159 return !ent->priv.server->free;
166 Two entities have touched, so run their touch functions
169 void SV_Impact (prvm_edict_t *e1, prvm_edict_t *e2)
171 int old_self, old_other;
173 old_self = prog->globals.server->self;
174 old_other = prog->globals.server->other;
176 prog->globals.server->time = sv.time;
177 if (e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
179 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
180 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
181 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
184 if (e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
186 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
187 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
188 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
191 prog->globals.server->self = old_self;
192 prog->globals.server->other = old_other;
200 Slide off of the impacting object
201 returns the blocked flags (1 = floor, 2 = step / wall)
204 #define STOP_EPSILON 0.1
205 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
210 backoff = -DotProduct (in, normal) * overbounce;
211 VectorMA(in, backoff, normal, out);
213 for (i = 0;i < 3;i++)
214 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
223 The basic solid body movement clip that slides along multiple planes
224 Returns the clipflags if the velocity was modified (hit something solid)
228 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
231 // LordHavoc: increased from 5 to 32
232 #define MAX_CLIP_PLANES 32
233 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
235 int blocked, bumpcount;
236 int i, j, impact, numplanes;
238 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
241 VectorCopy(ent->fields.server->velocity, original_velocity);
242 VectorCopy(ent->fields.server->velocity, primal_velocity);
245 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
247 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
250 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
251 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
253 //if (trace.fraction < 0.002)
258 VectorCopy(ent->fields.server->origin, start);
259 start[2] += 3;//0.03125;
260 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
261 end[2] += 3;//0.03125;
262 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
263 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)))
265 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
271 for (i = 0;i < numplanes;i++)
273 VectorCopy(ent->fields.server->origin, start);
274 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
275 VectorMA(start, 3, planes[i], start);
276 VectorMA(end, 3, planes[i], end);
277 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
278 if (trace.fraction < testtrace.fraction)
281 VectorCopy(start, ent->fields.server->origin);
286 // VectorAdd(ent->fields.server->origin, planes[j], start);
292 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);
293 if (trace.fraction < 1)
294 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
299 if (trace.startsolid)
301 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
302 // entity is trapped in another solid
303 VectorClear(ent->fields.server->velocity);
308 // break if it moved the entire distance
309 if (trace.fraction == 1)
311 VectorCopy(trace.endpos, ent->fields.server->origin);
317 Con_Printf ("SV_FlyMove: !trace.ent");
318 trace.ent = prog->edicts;
321 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
325 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
329 if (trace.plane.normal[2])
331 if (trace.plane.normal[2] > 0.7)
335 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
336 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
343 // save the trace for player extrafriction
345 VectorCopy(trace.plane.normal, stepnormal);
348 if (trace.fraction >= 0.001)
350 // actually covered some distance
351 VectorCopy(trace.endpos, ent->fields.server->origin);
352 VectorCopy(ent->fields.server->velocity, original_velocity);
356 // run the impact function
359 SV_Impact(ent, trace.ent);
361 // break if removed by the impact function
362 if (ent->priv.server->free)
366 time_left *= 1 - trace.fraction;
368 // clipped to another plane
369 if (numplanes >= MAX_CLIP_PLANES)
371 // this shouldn't really happen
372 VectorClear(ent->fields.server->velocity);
378 for (i = 0;i < numplanes;i++)
379 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
383 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
388 VectorCopy(trace.plane.normal, planes[numplanes]);
391 if (sv_newflymove.integer)
392 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
395 // modify original_velocity so it parallels all of the clip planes
396 for (i = 0;i < numplanes;i++)
398 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
399 for (j = 0;j < numplanes;j++)
404 if (DotProduct(new_velocity, planes[j]) < 0)
414 // go along this plane
415 VectorCopy(new_velocity, ent->fields.server->velocity);
419 // go along the crease
422 VectorClear(ent->fields.server->velocity);
426 CrossProduct(planes[0], planes[1], dir);
427 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
428 VectorNormalize(dir);
429 d = DotProduct(dir, ent->fields.server->velocity);
430 VectorScale(dir, d, ent->fields.server->velocity);
434 // if current velocity is against the original velocity,
435 // stop dead to avoid tiny occilations in sloping corners
436 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
438 VectorClear(ent->fields.server->velocity);
443 //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]);
446 if ((blocked & 1) == 0 && bumpcount > 1)
448 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
449 // flag ONGROUND if there's ground under it
450 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
456 int SV_SetOnGround (prvm_edict_t *ent)
460 if ((int)ent->fields.server->flags & FL_ONGROUND)
462 VectorSet(end, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
463 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
464 if (trace.fraction < 1 && trace.plane.normal[2] >= 0.7)
466 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
467 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
479 void SV_AddGravity (prvm_edict_t *ent)
484 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
485 if (val!=0 && val->_float)
486 ent_gravity = val->_float;
489 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
494 ===============================================================================
498 ===============================================================================
505 Does not change the entities velocity at all
508 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
514 VectorAdd (ent->fields.server->origin, push, end);
516 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
518 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
519 type = MOVE_NOMONSTERS; // only clip against bmodels
523 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
525 VectorCopy (trace.endpos, ent->fields.server->origin);
526 SV_LinkEdict (ent, true);
528 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
529 SV_Impact (ent, trace.ent);
540 trace_t SV_ClipMoveToEntity (prvm_edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
541 void SV_PushMove (prvm_edict_t *pusher, float movetime)
544 prvm_edict_t *check, *ed;
545 float savesolid, movetime2, pushltime;
546 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
548 int numcheckentities;
549 static prvm_edict_t *checkentities[MAX_EDICTS];
550 model_t *pushermodel;
553 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])
555 pusher->fields.server->ltime += movetime;
559 switch ((int) pusher->fields.server->solid)
561 // LordHavoc: valid pusher types
565 case SOLID_CORPSE: // LordHavoc: this would be weird...
567 // LordHavoc: no collisions
570 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
571 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
572 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
573 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
574 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
575 pusher->fields.server->ltime += movetime;
576 SV_LinkEdict (pusher, false);
579 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
582 index = (int) pusher->fields.server->modelindex;
583 if (index < 1 || index >= MAX_MODELS)
585 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
588 pushermodel = sv.models[index];
590 movetime2 = movetime;
591 VectorScale(pusher->fields.server->velocity, movetime2, move1);
592 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
593 if (moveangle[0] || moveangle[2])
595 for (i = 0;i < 3;i++)
599 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
600 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
604 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
605 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
609 else if (moveangle[1])
611 for (i = 0;i < 3;i++)
615 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
616 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
620 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
621 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
627 for (i = 0;i < 3;i++)
631 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
632 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
636 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
637 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
642 VectorNegate (moveangle, a);
643 AngleVectorsFLU (a, forward, left, up);
645 VectorCopy (pusher->fields.server->origin, pushorig);
646 VectorCopy (pusher->fields.server->angles, pushang);
647 pushltime = pusher->fields.server->ltime;
649 // move the pusher to it's final position
651 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
652 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
653 pusher->fields.server->ltime += movetime;
654 SV_LinkEdict (pusher, false);
656 savesolid = pusher->fields.server->solid;
658 // see if any solid entities are inside the final position
661 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
662 for (e = 0;e < numcheckentities;e++)
664 check = checkentities[e];
665 if (check->fields.server->movetype == MOVETYPE_PUSH
666 || check->fields.server->movetype == MOVETYPE_NONE
667 || check->fields.server->movetype == MOVETYPE_FOLLOW
668 || check->fields.server->movetype == MOVETYPE_NOCLIP
669 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
672 // if the entity is standing on the pusher, it will definitely be moved
673 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
674 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
677 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
679 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
680 org2[0] = DotProduct (org, forward);
681 org2[1] = DotProduct (org, left);
682 org2[2] = DotProduct (org, up);
683 VectorSubtract (org2, org, move);
684 VectorAdd (move, move1, move);
687 VectorCopy (move1, move);
689 // remove the onground flag for non-players
690 if (check->fields.server->movetype != MOVETYPE_WALK)
691 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
693 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
694 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
695 sv.moved_edicts[num_moved++] = check;
697 // try moving the contacted entity
698 pusher->fields.server->solid = SOLID_NOT;
699 trace = SV_PushEntity (check, move);
700 // FIXME: turn players specially
701 check->fields.server->angles[1] += trace.fraction * moveangle[1];
702 pusher->fields.server->solid = savesolid; // was SOLID_BSP
704 // if it is still inside the pusher, block
705 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
707 // try moving the contacted entity a tiny bit further to account for precision errors
708 pusher->fields.server->solid = SOLID_NOT;
709 VectorScale(move, 0.1, move);
710 SV_PushEntity (check, move);
711 pusher->fields.server->solid = savesolid;
712 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
714 // still inside pusher, so it's really blocked
717 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
719 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
722 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
723 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
727 VectorCopy (pushorig, pusher->fields.server->origin);
728 VectorCopy (pushang, pusher->fields.server->angles);
729 pusher->fields.server->ltime = pushltime;
730 SV_LinkEdict (pusher, false);
732 // move back any entities we already moved
733 for (i = 0;i < num_moved;i++)
735 ed = sv.moved_edicts[i];
736 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
737 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
738 SV_LinkEdict (ed, false);
741 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
742 if (pusher->fields.server->blocked)
744 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
745 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
746 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
752 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
753 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
754 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
763 void SV_Physics_Pusher (prvm_edict_t *ent)
765 float thinktime, oldltime, movetime;
767 oldltime = ent->fields.server->ltime;
769 thinktime = ent->fields.server->nextthink;
770 if (thinktime < ent->fields.server->ltime + sv.frametime)
772 movetime = thinktime - ent->fields.server->ltime;
777 movetime = sv.frametime;
780 // advances ent->fields.server->ltime if not blocked
781 SV_PushMove (ent, movetime);
783 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
785 ent->fields.server->nextthink = 0;
786 prog->globals.server->time = sv.time;
787 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
788 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
789 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
795 ===============================================================================
799 ===============================================================================
806 This is a big hack to try and fix the rare case of getting stuck in the world
810 void SV_CheckStuck (prvm_edict_t *ent)
815 if (!SV_TestEntityPosition(ent))
817 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
821 VectorCopy (ent->fields.server->origin, org);
822 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
823 if (!SV_TestEntityPosition(ent))
825 Con_DPrint("Unstuck.\n");
826 SV_LinkEdict (ent, true);
830 for (z=0 ; z< 18 ; z++)
831 for (i=-1 ; i <= 1 ; i++)
832 for (j=-1 ; j <= 1 ; j++)
834 ent->fields.server->origin[0] = org[0] + i;
835 ent->fields.server->origin[1] = org[1] + j;
836 ent->fields.server->origin[2] = org[2] + z;
837 if (!SV_TestEntityPosition(ent))
839 Con_DPrint("Unstuck.\n");
840 SV_LinkEdict (ent, true);
845 VectorCopy (org, ent->fields.server->origin);
846 Con_DPrint("player is stuck.\n");
855 qboolean SV_CheckWater (prvm_edict_t *ent)
860 point[0] = ent->fields.server->origin[0];
861 point[1] = ent->fields.server->origin[1];
862 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
864 ent->fields.server->waterlevel = 0;
865 ent->fields.server->watertype = CONTENTS_EMPTY;
866 cont = SV_PointSuperContents(point);
867 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
869 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
870 ent->fields.server->waterlevel = 1;
871 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
872 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
874 ent->fields.server->waterlevel = 2;
875 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
876 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
877 ent->fields.server->waterlevel = 3;
881 return ent->fields.server->waterlevel > 1;
890 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
893 vec3_t forward, into, side;
895 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
896 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
898 // cut the tangential velocity
899 i = DotProduct (stepnormal, ent->fields.server->velocity);
900 VectorScale (stepnormal, i, into);
901 VectorSubtract (ent->fields.server->velocity, into, side);
902 ent->fields.server->velocity[0] = side[0] * (1 + d);
903 ent->fields.server->velocity[1] = side[1] * (1 + d);
908 =====================
911 Player has come to a dead stop, possibly due to the problem with limited
912 float precision at some angle joins in the BSP hull.
914 Try fixing by pushing one pixel in each direction.
916 This is a hack, but in the interest of good gameplay...
917 ======================
919 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
924 VectorCopy (ent->fields.server->origin, oldorg);
927 for (i=0 ; i<8 ; i++)
929 // try pushing a little in an axial direction
932 case 0: dir[0] = 2; dir[1] = 0; break;
933 case 1: dir[0] = 0; dir[1] = 2; break;
934 case 2: dir[0] = -2; dir[1] = 0; break;
935 case 3: dir[0] = 0; dir[1] = -2; break;
936 case 4: dir[0] = 2; dir[1] = 2; break;
937 case 5: dir[0] = -2; dir[1] = 2; break;
938 case 6: dir[0] = 2; dir[1] = -2; break;
939 case 7: dir[0] = -2; dir[1] = -2; break;
942 SV_PushEntity (ent, dir);
944 // retry the original move
945 ent->fields.server->velocity[0] = oldvel[0];
946 ent->fields.server->velocity[1] = oldvel[1];
947 ent->fields.server->velocity[2] = 0;
948 clip = SV_FlyMove (ent, 0.1, NULL);
950 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
951 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
953 Con_DPrint("TryUnstick - success.\n");
957 // go back to the original pos and try again
958 VectorCopy (oldorg, ent->fields.server->origin);
962 VectorClear (ent->fields.server->velocity);
963 Con_DPrint("TryUnstick - failure.\n");
968 =====================
972 ======================
974 void SV_WalkMove (prvm_edict_t *ent)
976 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
977 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
980 SV_CheckVelocity(ent);
982 // do a regular slide move unless it looks like you ran into a step
983 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
984 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
986 VectorCopy (ent->fields.server->origin, start_origin);
987 VectorCopy (ent->fields.server->velocity, start_velocity);
989 clip = SV_FlyMove (ent, sv.frametime, NULL);
991 SV_SetOnGround (ent);
992 SV_CheckVelocity(ent);
994 VectorCopy(ent->fields.server->origin, originalmove_origin);
995 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
996 originalmove_clip = clip;
997 originalmove_flags = (int)ent->fields.server->flags;
998 originalmove_groundentity = ent->fields.server->groundentity;
1000 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1003 if (sv_nostep.integer)
1006 // if move didn't block on a step, return
1009 // if move was not trying to move into the step, return
1010 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1013 if (ent->fields.server->movetype != MOVETYPE_FLY)
1015 // return if gibbed by a trigger
1016 if (ent->fields.server->movetype != MOVETYPE_WALK)
1019 // only step up while jumping if that is enabled
1020 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1021 if (!oldonground && ent->fields.server->waterlevel == 0)
1025 // try moving up and forward to go up a step
1026 // back to start pos
1027 VectorCopy (start_origin, ent->fields.server->origin);
1028 VectorCopy (start_velocity, ent->fields.server->velocity);
1031 VectorClear (upmove);
1032 upmove[2] = sv_stepheight.value;
1033 // FIXME: don't link?
1034 SV_PushEntity(ent, upmove);
1037 ent->fields.server->velocity[2] = 0;
1038 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1039 ent->fields.server->velocity[2] += start_velocity[2];
1041 SV_CheckVelocity(ent);
1043 // check for stuckness, possibly due to the limited precision of floats
1044 // in the clipping hulls
1046 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1047 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1049 //Con_Printf("wall\n");
1050 // stepping up didn't make any progress, revert to original move
1051 VectorCopy(originalmove_origin, ent->fields.server->origin);
1052 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1053 //clip = originalmove_clip;
1054 ent->fields.server->flags = originalmove_flags;
1055 ent->fields.server->groundentity = originalmove_groundentity;
1056 // now try to unstick if needed
1057 //clip = SV_TryUnstick (ent, oldvel);
1061 //Con_Printf("step - ");
1063 // extra friction based on view angle
1064 if (clip & 2 && sv_wallfriction.integer)
1065 SV_WallFriction (ent, stepnormal);
1067 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1068 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)))
1072 VectorClear (downmove);
1073 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1074 // FIXME: don't link?
1075 downtrace = SV_PushEntity (ent, downmove);
1077 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1079 // LordHavoc: disabled this check so you can walk on monsters/players
1080 //if (ent->fields.server->solid == SOLID_BSP)
1082 //Con_Printf("onground\n");
1083 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1084 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1089 //Con_Printf("slope\n");
1090 // if the push down didn't end up on good ground, use the move without
1091 // the step up. This happens near wall / slope combinations, and can
1092 // cause the player to hop up higher on a slope too steep to climb
1093 VectorCopy(originalmove_origin, ent->fields.server->origin);
1094 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1095 //clip = originalmove_clip;
1096 ent->fields.server->flags = originalmove_flags;
1097 ent->fields.server->groundentity = originalmove_groundentity;
1100 SV_SetOnGround (ent);
1101 SV_CheckVelocity(ent);
1104 //============================================================================
1110 Entities that are "stuck" to another entity
1113 void SV_Physics_Follow (prvm_edict_t *ent)
1115 vec3_t vf, vr, vu, angles, v;
1119 if (!SV_RunThink (ent))
1122 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1123 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1124 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])
1126 // quick case for no rotation
1127 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1131 angles[0] = -ent->fields.server->punchangle[0];
1132 angles[1] = ent->fields.server->punchangle[1];
1133 angles[2] = ent->fields.server->punchangle[2];
1134 AngleVectors (angles, vf, vr, vu);
1135 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];
1136 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];
1137 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];
1138 angles[0] = -e->fields.server->angles[0];
1139 angles[1] = e->fields.server->angles[1];
1140 angles[2] = e->fields.server->angles[2];
1141 AngleVectors (angles, vf, vr, vu);
1142 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1143 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1144 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1146 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1147 SV_LinkEdict (ent, true);
1151 ==============================================================================
1155 ==============================================================================
1160 SV_CheckWaterTransition
1164 void SV_CheckWaterTransition (prvm_edict_t *ent)
1167 cont = SV_PointQ1Contents(ent->fields.server->origin);
1168 if (!ent->fields.server->watertype)
1170 // just spawned here
1171 ent->fields.server->watertype = cont;
1172 ent->fields.server->waterlevel = 1;
1176 // check if the entity crossed into or out of water
1177 if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1178 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1180 if (cont <= CONTENTS_WATER)
1182 ent->fields.server->watertype = cont;
1183 ent->fields.server->waterlevel = 1;
1187 ent->fields.server->watertype = CONTENTS_EMPTY;
1188 ent->fields.server->waterlevel = 0;
1196 Toss, bounce, and fly movement. When onground, do nothing.
1199 void SV_Physics_Toss (prvm_edict_t *ent)
1204 // don't stick to ground if onground and moving upward
1205 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1206 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1208 // if onground, return without moving
1209 if ((int)ent->fields.server->flags & FL_ONGROUND)
1211 if (ent->fields.server->groundentity == 0 || sv_gameplayfix_noairborncorpse.integer)
1213 // if ent was supported by a brush model on previous frame,
1214 // and groundentity is now freed, set groundentity to 0 (floating)
1215 if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1217 // leave it suspended in the air
1218 ent->fields.server->groundentity = 0;
1222 ent->priv.server->suspendedinairflag = false;
1224 SV_CheckVelocity (ent);
1227 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1228 SV_AddGravity (ent);
1231 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1234 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1235 trace = SV_PushEntity (ent, move);
1236 if (ent->priv.server->free)
1239 if (trace.fraction < 1)
1241 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1243 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1244 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1246 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1249 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1250 // LordHavoc: fixed grenades not bouncing when fired down a slope
1251 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1253 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1254 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1256 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1257 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1258 VectorClear (ent->fields.server->velocity);
1259 VectorClear (ent->fields.server->avelocity);
1262 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1266 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1268 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1269 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1270 VectorClear (ent->fields.server->velocity);
1271 VectorClear (ent->fields.server->avelocity);
1274 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1279 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1280 if (trace.plane.normal[2] > 0.7)
1282 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1283 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1284 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1285 ent->priv.server->suspendedinairflag = true;
1286 VectorClear (ent->fields.server->velocity);
1287 VectorClear (ent->fields.server->avelocity);
1290 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1294 // check for in water
1295 SV_CheckWaterTransition (ent);
1299 ===============================================================================
1303 ===============================================================================
1310 Monsters freefall when they don't have a ground entity, otherwise
1311 all movement is done with discrete steps.
1313 This is also used for objects that have become still on the ground, but
1314 will fall if the floor is pulled out from under them.
1317 void SV_Physics_Step (prvm_edict_t *ent)
1319 // don't stick to ground if onground and moving upward
1320 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1321 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1323 // freefall if not onground/fly/swim
1324 if (!((int)ent->fields.server->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1326 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1329 SV_CheckVelocity(ent);
1330 SV_FlyMove(ent, sv.frametime, NULL);
1331 SV_LinkEdict(ent, true);
1334 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1335 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1341 SV_CheckWaterTransition(ent);
1344 //============================================================================
1346 void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove)
1348 int i = ent - prog->edicts;
1349 if (i >= 1 && i <= svs.maxclients)
1351 // make sure the velocity is sane (not a NaN)
1352 SV_CheckVelocity(ent);
1353 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1354 if (SV_PlayerPhysicsQC)
1356 prog->globals.server->time = sv.time;
1357 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1358 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1362 // make sure the velocity is sane (not a NaN)
1363 SV_CheckVelocity(ent);
1364 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1365 // player_run/player_stand1 does not horribly malfunction if the
1366 // velocity becomes a number that is both == 0 and != 0
1367 // (sounds to me like NaN but to be absolutely safe...)
1368 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1369 VectorClear(ent->fields.server->velocity);
1370 // call standard client pre-think
1371 prog->globals.server->time = sv.time;
1372 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1373 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1374 SV_CheckVelocity (ent);
1377 // LordHavoc: merged client and normal entity physics
1378 switch ((int) ent->fields.server->movetype)
1381 case MOVETYPE_FAKEPUSH:
1382 SV_Physics_Pusher (ent);
1385 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1386 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1389 case MOVETYPE_FOLLOW:
1390 SV_Physics_Follow (ent);
1392 case MOVETYPE_NOCLIP:
1393 if (SV_RunThink(ent))
1396 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1397 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1399 // relink normal entities here, players always get relinked so don't relink twice
1400 if (!(i > 0 && i <= svs.maxclients))
1401 SV_LinkEdict(ent, false);
1404 SV_Physics_Step (ent);
1407 if (SV_RunThink (ent))
1409 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1410 SV_AddGravity (ent);
1411 SV_CheckStuck (ent);
1413 // relink normal entities here, players always get relinked so don't relink twice
1414 if (!(i > 0 && i <= svs.maxclients))
1415 SV_LinkEdict (ent, true);
1419 case MOVETYPE_BOUNCE:
1420 case MOVETYPE_BOUNCEMISSILE:
1421 case MOVETYPE_FLYMISSILE:
1423 if (SV_RunThink (ent) && runmove)
1424 SV_Physics_Toss (ent);
1427 if (SV_RunThink (ent) && runmove)
1429 if (i > 0 && i <= svs.maxclients)
1431 SV_CheckWater (ent);
1435 SV_Physics_Toss (ent);
1439 Con_Printf ("SV_Physics: bad movetype %i", (int)ent->fields.server->movetype);
1443 if (i >= 1 && i <= svs.maxclients)
1445 SV_CheckVelocity (ent);
1447 // call standard player post-think
1448 SV_LinkEdict (ent, true);
1450 SV_CheckVelocity (ent);
1452 prog->globals.server->time = sv.time;
1453 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1454 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1465 void SV_Physics (void)
1467 int i, newnum_edicts;
1469 qbyte runmove[MAX_EDICTS];
1471 // let the progs know that a new frame has started
1472 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1473 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1474 prog->globals.server->time = sv.time;
1475 prog->globals.server->frametime = sv.frametime;
1476 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1479 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1480 if ((runmove[i] = !ent->priv.server->free))
1481 newnum_edicts = i + 1;
1482 prog->num_edicts = max(svs.maxclients + 1, newnum_edicts);
1485 // treat each object in turn
1488 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1490 if (ent->priv.server->free)
1493 if (prog->globals.server->force_retouch)
1494 SV_LinkEdict (ent, true); // force retouch even for stationary
1496 if (i >= 1 && i <= svs.maxclients)
1498 host_client = svs.clients + i - 1;
1499 // don't do physics on disconnected clients, FrikBot relies on this
1500 if (!host_client->spawned)
1502 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1506 if (host_client->movesequence)
1507 continue; // return if running asynchronously
1509 else if (sv_freezenonclients.integer)
1512 SV_Physics_Entity(ent, runmove[i]);
1515 if (prog->globals.server->force_retouch > 0)
1516 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1518 // LordHavoc: endframe support
1521 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1522 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1523 prog->globals.server->time = sv.time;
1524 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1527 if (!sv_freezenonclients.integer)
1528 sv.time += sv.frametime;
1532 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1535 float gravity, savesolid;
1537 prvm_edict_t tempent, *tent;
1542 // copy the vars over
1543 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1544 // set up the temp entity to point to the copied vars
1546 tent->fields.server = &vars;
1548 savesolid = tossent->fields.server->solid;
1549 tossent->fields.server->solid = SOLID_NOT;
1551 // this has to fetch the field from the original edict, since our copy is truncated
1552 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1553 if (val != NULL && val->_float != 0)
1554 gravity = val->_float;
1557 gravity *= sv_gravity.value * 0.05;
1559 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1561 SV_CheckVelocity (tent);
1562 tent->fields.server->velocity[2] -= gravity;
1563 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1564 VectorScale (tent->fields.server->velocity, 0.05, move);
1565 VectorAdd (tent->fields.server->origin, move, end);
1566 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1567 VectorCopy (trace.endpos, tent->fields.server->origin);
1569 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1572 tossent->fields.server->solid = savesolid;
1573 trace.fraction = 0; // not relevant