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);
316 Host_Error("SV_FlyMove: !trace.ent");
318 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
322 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
326 if (trace.plane.normal[2])
328 if (trace.plane.normal[2] > 0.7)
332 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
333 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
340 // save the trace for player extrafriction
342 VectorCopy(trace.plane.normal, stepnormal);
345 if (trace.fraction >= 0.001)
347 // actually covered some distance
348 VectorCopy(trace.endpos, ent->fields.server->origin);
349 VectorCopy(ent->fields.server->velocity, original_velocity);
353 // run the impact function
356 SV_Impact(ent, trace.ent);
358 // break if removed by the impact function
359 if (ent->priv.server->free)
363 time_left *= 1 - trace.fraction;
365 // clipped to another plane
366 if (numplanes >= MAX_CLIP_PLANES)
368 // this shouldn't really happen
369 VectorClear(ent->fields.server->velocity);
375 for (i = 0;i < numplanes;i++)
376 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
380 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
385 VectorCopy(trace.plane.normal, planes[numplanes]);
388 if (sv_newflymove.integer)
389 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
392 // modify original_velocity so it parallels all of the clip planes
393 for (i = 0;i < numplanes;i++)
395 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
396 for (j = 0;j < numplanes;j++)
401 if (DotProduct(new_velocity, planes[j]) < 0)
411 // go along this plane
412 VectorCopy(new_velocity, ent->fields.server->velocity);
416 // go along the crease
419 VectorClear(ent->fields.server->velocity);
423 CrossProduct(planes[0], planes[1], dir);
424 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
425 VectorNormalize(dir);
426 d = DotProduct(dir, ent->fields.server->velocity);
427 VectorScale(dir, d, ent->fields.server->velocity);
431 // if current velocity is against the original velocity,
432 // stop dead to avoid tiny occilations in sloping corners
433 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
435 VectorClear(ent->fields.server->velocity);
440 //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]);
443 if ((blocked & 1) == 0 && bumpcount > 1)
445 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
446 // flag ONGROUND if there's ground under it
447 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
453 int SV_SetOnGround (prvm_edict_t *ent)
457 if ((int)ent->fields.server->flags & FL_ONGROUND)
459 VectorSet(end, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] - 1);
460 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
461 if (trace.fraction < 1 && trace.plane.normal[2] >= 0.7)
463 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
464 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
476 void SV_AddGravity (prvm_edict_t *ent)
481 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
482 if (val!=0 && val->_float)
483 ent_gravity = val->_float;
486 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
491 ===============================================================================
495 ===============================================================================
502 Does not change the entities velocity at all
505 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
511 VectorAdd (ent->fields.server->origin, push, end);
513 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
515 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
516 type = MOVE_NOMONSTERS; // only clip against bmodels
520 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
522 VectorCopy (trace.endpos, ent->fields.server->origin);
523 SV_LinkEdict (ent, true);
525 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
526 SV_Impact (ent, trace.ent);
537 trace_t SV_ClipMoveToEntity (prvm_edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
538 void SV_PushMove (prvm_edict_t *pusher, float movetime)
541 prvm_edict_t *check, *ed;
542 float savesolid, movetime2, pushltime;
543 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
545 int numcheckentities;
546 static prvm_edict_t *checkentities[MAX_EDICTS];
547 model_t *pushermodel;
550 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])
552 pusher->fields.server->ltime += movetime;
556 switch ((int) pusher->fields.server->solid)
558 // LordHavoc: valid pusher types
562 case SOLID_CORPSE: // LordHavoc: this would be weird...
564 // LordHavoc: no collisions
567 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
568 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
569 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
570 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
571 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
572 pusher->fields.server->ltime += movetime;
573 SV_LinkEdict (pusher, false);
576 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
579 index = (int) pusher->fields.server->modelindex;
580 if (index < 1 || index >= MAX_MODELS)
582 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
585 pushermodel = sv.models[index];
587 movetime2 = movetime;
588 VectorScale(pusher->fields.server->velocity, movetime2, move1);
589 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
590 if (moveangle[0] || moveangle[2])
592 for (i = 0;i < 3;i++)
596 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
597 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
601 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
602 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
606 else if (moveangle[1])
608 for (i = 0;i < 3;i++)
612 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
613 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
617 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
618 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
624 for (i = 0;i < 3;i++)
628 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
629 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
633 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
634 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
639 VectorNegate (moveangle, a);
640 AngleVectorsFLU (a, forward, left, up);
642 VectorCopy (pusher->fields.server->origin, pushorig);
643 VectorCopy (pusher->fields.server->angles, pushang);
644 pushltime = pusher->fields.server->ltime;
646 // move the pusher to it's final position
648 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
649 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
650 pusher->fields.server->ltime += movetime;
651 SV_LinkEdict (pusher, false);
653 savesolid = pusher->fields.server->solid;
655 // see if any solid entities are inside the final position
658 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
659 for (e = 0;e < numcheckentities;e++)
661 check = checkentities[e];
662 if (check->fields.server->movetype == MOVETYPE_PUSH
663 || check->fields.server->movetype == MOVETYPE_NONE
664 || check->fields.server->movetype == MOVETYPE_FOLLOW
665 || check->fields.server->movetype == MOVETYPE_NOCLIP
666 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
669 // if the entity is standing on the pusher, it will definitely be moved
670 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
671 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
674 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
676 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
677 org2[0] = DotProduct (org, forward);
678 org2[1] = DotProduct (org, left);
679 org2[2] = DotProduct (org, up);
680 VectorSubtract (org2, org, move);
681 VectorAdd (move, move1, move);
684 VectorCopy (move1, move);
686 // remove the onground flag for non-players
687 if (check->fields.server->movetype != MOVETYPE_WALK)
688 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
690 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
691 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
692 sv.moved_edicts[num_moved++] = check;
694 // try moving the contacted entity
695 pusher->fields.server->solid = SOLID_NOT;
696 trace = SV_PushEntity (check, move);
697 // FIXME: turn players specially
698 check->fields.server->angles[1] += trace.fraction * moveangle[1];
699 pusher->fields.server->solid = savesolid; // was SOLID_BSP
701 // if it is still inside the pusher, block
702 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
704 // try moving the contacted entity a tiny bit further to account for precision errors
705 pusher->fields.server->solid = SOLID_NOT;
706 VectorScale(move, 0.1, move);
707 SV_PushEntity (check, move);
708 pusher->fields.server->solid = savesolid;
709 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
711 // still inside pusher, so it's really blocked
714 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
716 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
719 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
720 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
724 VectorCopy (pushorig, pusher->fields.server->origin);
725 VectorCopy (pushang, pusher->fields.server->angles);
726 pusher->fields.server->ltime = pushltime;
727 SV_LinkEdict (pusher, false);
729 // move back any entities we already moved
730 for (i = 0;i < num_moved;i++)
732 ed = sv.moved_edicts[i];
733 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
734 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
735 SV_LinkEdict (ed, false);
738 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
739 if (pusher->fields.server->blocked)
741 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
742 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
743 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
749 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
750 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
751 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
760 void SV_Physics_Pusher (prvm_edict_t *ent)
762 float thinktime, oldltime, movetime;
764 oldltime = ent->fields.server->ltime;
766 thinktime = ent->fields.server->nextthink;
767 if (thinktime < ent->fields.server->ltime + sv.frametime)
769 movetime = thinktime - ent->fields.server->ltime;
774 movetime = sv.frametime;
777 // advances ent->fields.server->ltime if not blocked
778 SV_PushMove (ent, movetime);
780 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
782 ent->fields.server->nextthink = 0;
783 prog->globals.server->time = sv.time;
784 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
785 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
786 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
792 ===============================================================================
796 ===============================================================================
803 This is a big hack to try and fix the rare case of getting stuck in the world
807 void SV_CheckStuck (prvm_edict_t *ent)
812 if (!SV_TestEntityPosition(ent))
814 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
818 VectorCopy (ent->fields.server->origin, org);
819 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
820 if (!SV_TestEntityPosition(ent))
822 Con_DPrint("Unstuck.\n");
823 SV_LinkEdict (ent, true);
827 for (z=0 ; z< 18 ; z++)
828 for (i=-1 ; i <= 1 ; i++)
829 for (j=-1 ; j <= 1 ; j++)
831 ent->fields.server->origin[0] = org[0] + i;
832 ent->fields.server->origin[1] = org[1] + j;
833 ent->fields.server->origin[2] = org[2] + z;
834 if (!SV_TestEntityPosition(ent))
836 Con_DPrint("Unstuck.\n");
837 SV_LinkEdict (ent, true);
842 VectorCopy (org, ent->fields.server->origin);
843 Con_DPrint("player is stuck.\n");
852 qboolean SV_CheckWater (prvm_edict_t *ent)
857 point[0] = ent->fields.server->origin[0];
858 point[1] = ent->fields.server->origin[1];
859 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
861 ent->fields.server->waterlevel = 0;
862 ent->fields.server->watertype = CONTENTS_EMPTY;
863 cont = SV_PointSuperContents(point);
864 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
866 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
867 ent->fields.server->waterlevel = 1;
868 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
869 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
871 ent->fields.server->waterlevel = 2;
872 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
873 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
874 ent->fields.server->waterlevel = 3;
878 return ent->fields.server->waterlevel > 1;
887 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
890 vec3_t forward, into, side;
892 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
893 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
895 // cut the tangential velocity
896 i = DotProduct (stepnormal, ent->fields.server->velocity);
897 VectorScale (stepnormal, i, into);
898 VectorSubtract (ent->fields.server->velocity, into, side);
899 ent->fields.server->velocity[0] = side[0] * (1 + d);
900 ent->fields.server->velocity[1] = side[1] * (1 + d);
905 =====================
908 Player has come to a dead stop, possibly due to the problem with limited
909 float precision at some angle joins in the BSP hull.
911 Try fixing by pushing one pixel in each direction.
913 This is a hack, but in the interest of good gameplay...
914 ======================
916 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
921 VectorCopy (ent->fields.server->origin, oldorg);
924 for (i=0 ; i<8 ; i++)
926 // try pushing a little in an axial direction
929 case 0: dir[0] = 2; dir[1] = 0; break;
930 case 1: dir[0] = 0; dir[1] = 2; break;
931 case 2: dir[0] = -2; dir[1] = 0; break;
932 case 3: dir[0] = 0; dir[1] = -2; break;
933 case 4: dir[0] = 2; dir[1] = 2; break;
934 case 5: dir[0] = -2; dir[1] = 2; break;
935 case 6: dir[0] = 2; dir[1] = -2; break;
936 case 7: dir[0] = -2; dir[1] = -2; break;
939 SV_PushEntity (ent, dir);
941 // retry the original move
942 ent->fields.server->velocity[0] = oldvel[0];
943 ent->fields.server->velocity[1] = oldvel[1];
944 ent->fields.server->velocity[2] = 0;
945 clip = SV_FlyMove (ent, 0.1, NULL);
947 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
948 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
950 Con_DPrint("TryUnstick - success.\n");
954 // go back to the original pos and try again
955 VectorCopy (oldorg, ent->fields.server->origin);
959 VectorClear (ent->fields.server->velocity);
960 Con_DPrint("TryUnstick - failure.\n");
965 =====================
969 ======================
971 void SV_WalkMove (prvm_edict_t *ent)
973 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
974 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
977 SV_CheckVelocity(ent);
979 // do a regular slide move unless it looks like you ran into a step
980 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
981 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
983 VectorCopy (ent->fields.server->origin, start_origin);
984 VectorCopy (ent->fields.server->velocity, start_velocity);
986 clip = SV_FlyMove (ent, sv.frametime, NULL);
988 SV_SetOnGround (ent);
989 SV_CheckVelocity(ent);
991 VectorCopy(ent->fields.server->origin, originalmove_origin);
992 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
993 originalmove_clip = clip;
994 originalmove_flags = (int)ent->fields.server->flags;
995 originalmove_groundentity = ent->fields.server->groundentity;
997 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1000 if (sv_nostep.integer)
1003 // if move didn't block on a step, return
1006 // if move was not trying to move into the step, return
1007 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1010 if (ent->fields.server->movetype != MOVETYPE_FLY)
1012 // return if gibbed by a trigger
1013 if (ent->fields.server->movetype != MOVETYPE_WALK)
1016 // only step up while jumping if that is enabled
1017 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1018 if (!oldonground && ent->fields.server->waterlevel == 0)
1022 // try moving up and forward to go up a step
1023 // back to start pos
1024 VectorCopy (start_origin, ent->fields.server->origin);
1025 VectorCopy (start_velocity, ent->fields.server->velocity);
1028 VectorClear (upmove);
1029 upmove[2] = sv_stepheight.value;
1030 // FIXME: don't link?
1031 SV_PushEntity(ent, upmove);
1034 ent->fields.server->velocity[2] = 0;
1035 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1036 ent->fields.server->velocity[2] += start_velocity[2];
1038 SV_CheckVelocity(ent);
1040 // check for stuckness, possibly due to the limited precision of floats
1041 // in the clipping hulls
1043 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1044 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1046 //Con_Printf("wall\n");
1047 // stepping up didn't make any progress, revert to original move
1048 VectorCopy(originalmove_origin, ent->fields.server->origin);
1049 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1050 //clip = originalmove_clip;
1051 ent->fields.server->flags = originalmove_flags;
1052 ent->fields.server->groundentity = originalmove_groundentity;
1053 // now try to unstick if needed
1054 //clip = SV_TryUnstick (ent, oldvel);
1058 //Con_Printf("step - ");
1060 // extra friction based on view angle
1061 if (clip & 2 && sv_wallfriction.integer)
1062 SV_WallFriction (ent, stepnormal);
1064 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1065 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)))
1069 VectorClear (downmove);
1070 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1071 // FIXME: don't link?
1072 downtrace = SV_PushEntity (ent, downmove);
1074 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1076 // LordHavoc: disabled this check so you can walk on monsters/players
1077 //if (ent->fields.server->solid == SOLID_BSP)
1079 //Con_Printf("onground\n");
1080 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1081 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1086 //Con_Printf("slope\n");
1087 // if the push down didn't end up on good ground, use the move without
1088 // the step up. This happens near wall / slope combinations, and can
1089 // cause the player to hop up higher on a slope too steep to climb
1090 VectorCopy(originalmove_origin, ent->fields.server->origin);
1091 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1092 //clip = originalmove_clip;
1093 ent->fields.server->flags = originalmove_flags;
1094 ent->fields.server->groundentity = originalmove_groundentity;
1097 SV_SetOnGround (ent);
1098 SV_CheckVelocity(ent);
1101 //============================================================================
1107 Entities that are "stuck" to another entity
1110 void SV_Physics_Follow (prvm_edict_t *ent)
1112 vec3_t vf, vr, vu, angles, v;
1116 if (!SV_RunThink (ent))
1119 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1120 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1121 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])
1123 // quick case for no rotation
1124 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1128 angles[0] = -ent->fields.server->punchangle[0];
1129 angles[1] = ent->fields.server->punchangle[1];
1130 angles[2] = ent->fields.server->punchangle[2];
1131 AngleVectors (angles, vf, vr, vu);
1132 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];
1133 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];
1134 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];
1135 angles[0] = -e->fields.server->angles[0];
1136 angles[1] = e->fields.server->angles[1];
1137 angles[2] = e->fields.server->angles[2];
1138 AngleVectors (angles, vf, vr, vu);
1139 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1140 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1141 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1143 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1144 SV_LinkEdict (ent, true);
1148 ==============================================================================
1152 ==============================================================================
1157 SV_CheckWaterTransition
1161 void SV_CheckWaterTransition (prvm_edict_t *ent)
1164 cont = SV_PointQ1Contents(ent->fields.server->origin);
1165 if (!ent->fields.server->watertype)
1167 // just spawned here
1168 ent->fields.server->watertype = cont;
1169 ent->fields.server->waterlevel = 1;
1173 // check if the entity crossed into or out of water
1174 if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1175 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1177 if (cont <= CONTENTS_WATER)
1179 ent->fields.server->watertype = cont;
1180 ent->fields.server->waterlevel = 1;
1184 ent->fields.server->watertype = CONTENTS_EMPTY;
1185 ent->fields.server->waterlevel = 0;
1193 Toss, bounce, and fly movement. When onground, do nothing.
1196 void SV_Physics_Toss (prvm_edict_t *ent)
1201 // don't stick to ground if onground and moving upward
1202 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1203 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1205 // if onground, return without moving
1206 if ((int)ent->fields.server->flags & FL_ONGROUND)
1208 if (ent->fields.server->groundentity == 0 || sv_gameplayfix_noairborncorpse.integer)
1210 // if ent was supported by a brush model on previous frame,
1211 // and groundentity is now freed, set groundentity to 0 (floating)
1212 if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1214 // leave it suspended in the air
1215 ent->fields.server->groundentity = 0;
1219 ent->priv.server->suspendedinairflag = false;
1221 SV_CheckVelocity (ent);
1224 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1225 SV_AddGravity (ent);
1228 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1231 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1232 trace = SV_PushEntity (ent, move);
1233 if (ent->priv.server->free)
1236 if (trace.fraction < 1)
1238 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1240 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1241 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1243 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1246 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1247 // LordHavoc: fixed grenades not bouncing when fired down a slope
1248 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1250 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1251 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1253 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1254 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1255 VectorClear (ent->fields.server->velocity);
1256 VectorClear (ent->fields.server->avelocity);
1259 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1263 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1265 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1266 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1267 VectorClear (ent->fields.server->velocity);
1268 VectorClear (ent->fields.server->avelocity);
1271 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1276 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1277 if (trace.plane.normal[2] > 0.7)
1279 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1280 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1281 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1282 ent->priv.server->suspendedinairflag = true;
1283 VectorClear (ent->fields.server->velocity);
1284 VectorClear (ent->fields.server->avelocity);
1287 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1291 // check for in water
1292 SV_CheckWaterTransition (ent);
1296 ===============================================================================
1300 ===============================================================================
1307 Monsters freefall when they don't have a ground entity, otherwise
1308 all movement is done with discrete steps.
1310 This is also used for objects that have become still on the ground, but
1311 will fall if the floor is pulled out from under them.
1314 void SV_Physics_Step (prvm_edict_t *ent)
1316 // don't stick to ground if onground and moving upward
1317 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1318 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1320 // freefall if not onground/fly/swim
1321 if (!((int)ent->fields.server->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1323 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1326 SV_CheckVelocity(ent);
1327 SV_FlyMove(ent, sv.frametime, NULL);
1328 SV_LinkEdict(ent, true);
1331 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1332 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1338 SV_CheckWaterTransition(ent);
1341 //============================================================================
1343 void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove)
1345 int i = ent - prog->edicts;
1346 if (i >= 1 && i <= svs.maxclients)
1348 // apply the latest accepted move to the entity fields
1349 SV_ApplyClientMove();
1350 // make sure the velocity is sane (not a NaN)
1351 SV_CheckVelocity(ent);
1352 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1353 if (SV_PlayerPhysicsQC)
1355 prog->globals.server->time = sv.time;
1356 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1357 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1361 // make sure the velocity is sane (not a NaN)
1362 SV_CheckVelocity(ent);
1363 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1364 // player_run/player_stand1 does not horribly malfunction if the
1365 // velocity becomes a number that is both == 0 and != 0
1366 // (sounds to me like NaN but to be absolutely safe...)
1367 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1368 VectorClear(ent->fields.server->velocity);
1369 // call standard client pre-think
1370 prog->globals.server->time = sv.time;
1371 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1372 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1373 SV_CheckVelocity (ent);
1376 // LordHavoc: merged client and normal entity physics
1377 switch ((int) ent->fields.server->movetype)
1380 case MOVETYPE_FAKEPUSH:
1381 SV_Physics_Pusher (ent);
1384 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1385 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1388 case MOVETYPE_FOLLOW:
1389 SV_Physics_Follow (ent);
1391 case MOVETYPE_NOCLIP:
1392 if (SV_RunThink(ent))
1395 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1396 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1398 // relink normal entities here, players always get relinked so don't relink twice
1399 if (!(i > 0 && i <= svs.maxclients))
1400 SV_LinkEdict(ent, false);
1403 SV_Physics_Step (ent);
1406 if (SV_RunThink (ent))
1408 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1409 SV_AddGravity (ent);
1410 SV_CheckStuck (ent);
1412 // relink normal entities here, players always get relinked so don't relink twice
1413 if (!(i > 0 && i <= svs.maxclients))
1414 SV_LinkEdict (ent, true);
1418 case MOVETYPE_BOUNCE:
1419 case MOVETYPE_BOUNCEMISSILE:
1420 case MOVETYPE_FLYMISSILE:
1422 if (SV_RunThink (ent) && runmove)
1423 SV_Physics_Toss (ent);
1426 if (SV_RunThink (ent) && runmove)
1428 if (i > 0 && i <= svs.maxclients)
1430 SV_CheckWater (ent);
1434 SV_Physics_Toss (ent);
1438 Host_Error ("SV_Physics: bad movetype %i", (int)ent->fields.server->movetype);
1442 if (i >= 1 && i <= svs.maxclients)
1444 SV_CheckVelocity (ent);
1446 // call standard player post-think
1447 SV_LinkEdict (ent, true);
1449 SV_CheckVelocity (ent);
1451 prog->globals.server->time = sv.time;
1452 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1453 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1464 void SV_Physics (void)
1466 int i, newnum_edicts;
1468 qbyte runmove[MAX_EDICTS];
1470 // let the progs know that a new frame has started
1471 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1472 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1473 prog->globals.server->time = sv.time;
1474 prog->globals.server->frametime = sv.frametime;
1475 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1478 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1479 if ((runmove[i] = !ent->priv.server->free))
1480 newnum_edicts = i + 1;
1481 prog->num_edicts = max(svs.maxclients + 1, newnum_edicts);
1484 // treat each object in turn
1487 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1489 if (ent->priv.server->free)
1492 if (prog->globals.server->force_retouch)
1493 SV_LinkEdict (ent, true); // force retouch even for stationary
1495 if (i >= 1 && i <= svs.maxclients)
1497 host_client = svs.clients + i - 1;
1498 // don't do physics on disconnected clients, FrikBot relies on this
1499 if (!host_client->spawned)
1501 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1505 if (host_client->movesequence)
1506 continue; // return if running asynchronously
1508 else if (sv_freezenonclients.integer)
1511 SV_Physics_Entity(ent, runmove[i]);
1514 if (prog->globals.server->force_retouch > 0)
1515 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1517 // LordHavoc: endframe support
1520 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1521 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1522 prog->globals.server->time = sv.time;
1523 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1526 if (!sv_freezenonclients.integer)
1527 sv.time += sv.frametime;
1531 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1534 float gravity, savesolid;
1536 prvm_edict_t tempent, *tent;
1541 // copy the vars over
1542 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1543 // set up the temp entity to point to the copied vars
1545 tent->fields.server = &vars;
1547 savesolid = tossent->fields.server->solid;
1548 tossent->fields.server->solid = SOLID_NOT;
1550 // this has to fetch the field from the original edict, since our copy is truncated
1551 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1552 if (val != NULL && val->_float != 0)
1553 gravity = val->_float;
1556 gravity *= sv_gravity.value * 0.05;
1558 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1560 SV_CheckVelocity (tent);
1561 tent->fields.server->velocity[2] -= gravity;
1562 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1563 VectorScale (tent->fields.server->velocity, 0.05, move);
1564 VectorAdd (tent->fields.server->origin, move, end);
1565 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1566 VectorCopy (trace.endpos, tent->fields.server->origin);
1568 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1571 tossent->fields.server->solid = savesolid;
1572 trace.fraction = 0; // not relevant