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"};
52 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1"};
54 #define MOVE_EPSILON 0.01
56 void SV_Physics_Toss (prvm_edict_t *ent);
58 void SV_Phys_Init (void)
60 Cvar_RegisterVariable(&sv_stepheight);
61 Cvar_RegisterVariable(&sv_jumpstep);
62 Cvar_RegisterVariable(&sv_wallfriction);
63 Cvar_RegisterVariable(&sv_newflymove);
64 Cvar_RegisterVariable(&sv_freezenonclients);
66 Cvar_RegisterVariable(&sv_playerphysicsqc);
74 void SV_CheckAllEnts (void)
79 // see if any solid entities are inside the final position
80 check = PRVM_NEXT_EDICT(prog->edicts);
81 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
83 if (check->priv.server->free)
85 if (check->fields.server->movetype == MOVETYPE_PUSH
86 || check->fields.server->movetype == MOVETYPE_NONE
87 || check->fields.server->movetype == MOVETYPE_FOLLOW
88 || check->fields.server->movetype == MOVETYPE_NOCLIP)
91 if (SV_TestEntityPosition (check))
92 Con_Print("entity in invalid position\n");
101 void SV_CheckVelocity (prvm_edict_t *ent)
109 for (i=0 ; i<3 ; i++)
111 if (IS_NAN(ent->fields.server->velocity[i]))
113 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
114 ent->fields.server->velocity[i] = 0;
116 if (IS_NAN(ent->fields.server->origin[i]))
118 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
119 ent->fields.server->origin[i] = 0;
123 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
124 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
125 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
127 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
128 ent->fields.server->velocity[0] *= wishspeed;
129 ent->fields.server->velocity[1] *= wishspeed;
130 ent->fields.server->velocity[2] *= wishspeed;
138 Runs thinking code if time. There is some play in the exact time the think
139 function will be called, because it is called before any movement is done
140 in a frame. Not used for pushmove objects, because they must be exact.
141 Returns false if the entity removed itself.
144 qboolean SV_RunThink (prvm_edict_t *ent)
148 thinktime = ent->fields.server->nextthink;
149 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
152 // don't let things stay in the past.
153 // it is possible to start that way by a trigger with a local time.
154 if (thinktime < sv.time)
157 ent->fields.server->nextthink = 0;
158 prog->globals.server->time = thinktime;
159 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
160 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
161 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
162 return !ent->priv.server->free;
169 Two entities have touched, so run their touch functions
172 void SV_Impact (prvm_edict_t *e1, prvm_edict_t *e2)
174 int old_self, old_other;
176 old_self = prog->globals.server->self;
177 old_other = prog->globals.server->other;
179 prog->globals.server->time = sv.time;
180 if (e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
182 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
183 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
184 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
187 if (e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
189 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
190 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
191 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
194 prog->globals.server->self = old_self;
195 prog->globals.server->other = old_other;
203 Slide off of the impacting object
204 returns the blocked flags (1 = floor, 2 = step / wall)
207 #define STOP_EPSILON 0.1
208 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
213 backoff = -DotProduct (in, normal) * overbounce;
214 VectorMA(in, backoff, normal, out);
216 for (i = 0;i < 3;i++)
217 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
226 The basic solid body movement clip that slides along multiple planes
227 Returns the clipflags if the velocity was modified (hit something solid)
231 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
234 // LordHavoc: increased from 5 to 32
235 #define MAX_CLIP_PLANES 32
236 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
238 int blocked, bumpcount;
239 int i, j, impact, numplanes;
241 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
244 VectorCopy(ent->fields.server->velocity, original_velocity);
245 VectorCopy(ent->fields.server->velocity, primal_velocity);
248 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
250 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
253 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
254 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
256 //if (trace.fraction < 0.002)
261 VectorCopy(ent->fields.server->origin, start);
262 start[2] += 3;//0.03125;
263 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
264 end[2] += 3;//0.03125;
265 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
266 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)))
268 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
274 for (i = 0;i < numplanes;i++)
276 VectorCopy(ent->fields.server->origin, start);
277 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
278 VectorMA(start, 3, planes[i], start);
279 VectorMA(end, 3, planes[i], end);
280 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
281 if (trace.fraction < testtrace.fraction)
284 VectorCopy(start, ent->fields.server->origin);
289 // VectorAdd(ent->fields.server->origin, planes[j], start);
295 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);
296 if (trace.fraction < 1)
297 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
302 if (trace.startsolid)
304 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
305 // entity is trapped in another solid
306 VectorClear(ent->fields.server->velocity);
311 // break if it moved the entire distance
312 if (trace.fraction == 1)
314 VectorCopy(trace.endpos, ent->fields.server->origin);
320 Con_Printf ("SV_FlyMove: !trace.ent");
321 trace.ent = prog->edicts;
324 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
328 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
332 if (trace.plane.normal[2])
334 if (trace.plane.normal[2] > 0.7)
338 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
339 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
346 // save the trace for player extrafriction
348 VectorCopy(trace.plane.normal, stepnormal);
351 if (trace.fraction >= 0.001)
353 // actually covered some distance
354 VectorCopy(trace.endpos, ent->fields.server->origin);
355 VectorCopy(ent->fields.server->velocity, original_velocity);
359 // run the impact function
362 SV_Impact(ent, trace.ent);
364 // break if removed by the impact function
365 if (ent->priv.server->free)
369 time_left *= 1 - trace.fraction;
371 // clipped to another plane
372 if (numplanes >= MAX_CLIP_PLANES)
374 // this shouldn't really happen
375 VectorClear(ent->fields.server->velocity);
381 for (i = 0;i < numplanes;i++)
382 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
386 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
391 VectorCopy(trace.plane.normal, planes[numplanes]);
394 if (sv_newflymove.integer)
395 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
398 // modify original_velocity so it parallels all of the clip planes
399 for (i = 0;i < numplanes;i++)
401 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
402 for (j = 0;j < numplanes;j++)
407 if (DotProduct(new_velocity, planes[j]) < 0)
417 // go along this plane
418 VectorCopy(new_velocity, ent->fields.server->velocity);
422 // go along the crease
425 VectorClear(ent->fields.server->velocity);
429 CrossProduct(planes[0], planes[1], dir);
430 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
431 VectorNormalize(dir);
432 d = DotProduct(dir, ent->fields.server->velocity);
433 VectorScale(dir, d, ent->fields.server->velocity);
437 // if current velocity is against the original velocity,
438 // stop dead to avoid tiny occilations in sloping corners
439 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
441 VectorClear(ent->fields.server->velocity);
446 //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]);
449 if ((blocked & 1) == 0 && bumpcount > 1)
451 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
452 // flag ONGROUND if there's ground under it
453 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
465 void SV_AddGravity (prvm_edict_t *ent)
470 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
471 if (val!=0 && val->_float)
472 ent_gravity = val->_float;
475 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
480 ===============================================================================
484 ===============================================================================
491 Does not change the entities velocity at all
494 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
500 VectorAdd (ent->fields.server->origin, push, end);
502 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
504 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
505 type = MOVE_NOMONSTERS; // only clip against bmodels
509 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
511 VectorCopy (trace.endpos, ent->fields.server->origin);
512 SV_LinkEdict (ent, true);
514 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
515 SV_Impact (ent, trace.ent);
526 trace_t SV_ClipMoveToEntity (prvm_edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int movetype, int hitsupercontents);
527 void SV_PushMove (prvm_edict_t *pusher, float movetime)
530 prvm_edict_t *check, *ed;
531 float savesolid, movetime2, pushltime;
532 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
534 int numcheckentities;
535 static prvm_edict_t *checkentities[MAX_EDICTS];
536 model_t *pushermodel;
539 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])
541 pusher->fields.server->ltime += movetime;
545 switch ((int) pusher->fields.server->solid)
547 // LordHavoc: valid pusher types
551 case SOLID_CORPSE: // LordHavoc: this would be weird...
553 // LordHavoc: no collisions
556 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
557 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
558 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
559 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
560 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
561 pusher->fields.server->ltime += movetime;
562 SV_LinkEdict (pusher, false);
565 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
568 index = (int) pusher->fields.server->modelindex;
569 if (index < 1 || index >= MAX_MODELS)
571 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
574 pushermodel = sv.models[index];
576 movetime2 = movetime;
577 VectorScale(pusher->fields.server->velocity, movetime2, move1);
578 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
579 if (moveangle[0] || moveangle[2])
581 for (i = 0;i < 3;i++)
585 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
586 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
590 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
591 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
595 else if (moveangle[1])
597 for (i = 0;i < 3;i++)
601 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
602 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
606 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
607 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
613 for (i = 0;i < 3;i++)
617 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
618 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
622 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
623 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
628 VectorNegate (moveangle, a);
629 AngleVectorsFLU (a, forward, left, up);
631 VectorCopy (pusher->fields.server->origin, pushorig);
632 VectorCopy (pusher->fields.server->angles, pushang);
633 pushltime = pusher->fields.server->ltime;
635 // move the pusher to its final position
637 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
638 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
639 pusher->fields.server->ltime += movetime;
640 SV_LinkEdict (pusher, false);
642 savesolid = pusher->fields.server->solid;
644 // see if any solid entities are inside the final position
647 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
648 for (e = 0;e < numcheckentities;e++)
650 check = checkentities[e];
651 if (check->fields.server->movetype == MOVETYPE_NONE
652 || check->fields.server->movetype == MOVETYPE_PUSH
653 || check->fields.server->movetype == MOVETYPE_FOLLOW
654 || check->fields.server->movetype == MOVETYPE_NOCLIP
655 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
658 // if the entity is standing on the pusher, it will definitely be moved
659 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
661 // if the entity is not inside the pusher's final position, leave it alone
662 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
666 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
668 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
669 org2[0] = DotProduct (org, forward);
670 org2[1] = DotProduct (org, left);
671 org2[2] = DotProduct (org, up);
672 VectorSubtract (org2, org, move);
673 VectorAdd (move, move1, move);
676 VectorCopy (move1, move);
678 // remove the onground flag for non-players
679 if (check->fields.server->movetype != MOVETYPE_WALK)
680 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
682 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
683 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
684 sv.moved_edicts[num_moved++] = check;
686 // try moving the contacted entity
687 pusher->fields.server->solid = SOLID_NOT;
688 trace = SV_PushEntity (check, move);
689 // FIXME: turn players specially
690 check->fields.server->angles[1] += trace.fraction * moveangle[1];
691 pusher->fields.server->solid = savesolid; // was SOLID_BSP
693 // if it is still inside the pusher, block
694 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
696 // try moving the contacted entity a tiny bit further to account for precision errors
697 pusher->fields.server->solid = SOLID_NOT;
698 VectorScale(move, 0.1, move);
699 SV_PushEntity (check, move);
700 pusher->fields.server->solid = savesolid;
701 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
703 // still inside pusher, so it's really blocked
706 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
708 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
711 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
712 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
716 VectorCopy (pushorig, pusher->fields.server->origin);
717 VectorCopy (pushang, pusher->fields.server->angles);
718 pusher->fields.server->ltime = pushltime;
719 SV_LinkEdict (pusher, false);
721 // move back any entities we already moved
722 for (i = 0;i < num_moved;i++)
724 ed = sv.moved_edicts[i];
725 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
726 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
727 SV_LinkEdict (ed, false);
730 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
731 if (pusher->fields.server->blocked)
733 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
734 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
735 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
741 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
742 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
743 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
752 void SV_Physics_Pusher (prvm_edict_t *ent)
754 float thinktime, oldltime, movetime;
756 oldltime = ent->fields.server->ltime;
758 thinktime = ent->fields.server->nextthink;
759 if (thinktime < ent->fields.server->ltime + sv.frametime)
761 movetime = thinktime - ent->fields.server->ltime;
766 movetime = sv.frametime;
769 // advances ent->fields.server->ltime if not blocked
770 SV_PushMove (ent, movetime);
772 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
774 ent->fields.server->nextthink = 0;
775 prog->globals.server->time = sv.time;
776 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
777 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
778 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
784 ===============================================================================
788 ===============================================================================
795 This is a big hack to try and fix the rare case of getting stuck in the world
799 void SV_CheckStuck (prvm_edict_t *ent)
804 if (!SV_TestEntityPosition(ent))
806 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
810 VectorCopy (ent->fields.server->origin, org);
811 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
812 if (!SV_TestEntityPosition(ent))
814 Con_DPrint("Unstuck.\n");
815 SV_LinkEdict (ent, true);
819 for (z=0 ; z< 18 ; z++)
820 for (i=-1 ; i <= 1 ; i++)
821 for (j=-1 ; j <= 1 ; j++)
823 ent->fields.server->origin[0] = org[0] + i;
824 ent->fields.server->origin[1] = org[1] + j;
825 ent->fields.server->origin[2] = org[2] + z;
826 if (!SV_TestEntityPosition(ent))
828 Con_DPrint("Unstuck.\n");
829 SV_LinkEdict (ent, true);
834 VectorCopy (org, ent->fields.server->origin);
835 Con_DPrint("player is stuck.\n");
844 qboolean SV_CheckWater (prvm_edict_t *ent)
849 point[0] = ent->fields.server->origin[0];
850 point[1] = ent->fields.server->origin[1];
851 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
853 ent->fields.server->waterlevel = 0;
854 ent->fields.server->watertype = CONTENTS_EMPTY;
855 cont = SV_PointSuperContents(point);
856 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
858 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
859 ent->fields.server->waterlevel = 1;
860 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
861 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
863 ent->fields.server->waterlevel = 2;
864 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
865 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
866 ent->fields.server->waterlevel = 3;
870 return ent->fields.server->waterlevel > 1;
879 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
882 vec3_t forward, into, side;
884 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
885 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
887 // cut the tangential velocity
888 i = DotProduct (stepnormal, ent->fields.server->velocity);
889 VectorScale (stepnormal, i, into);
890 VectorSubtract (ent->fields.server->velocity, into, side);
891 ent->fields.server->velocity[0] = side[0] * (1 + d);
892 ent->fields.server->velocity[1] = side[1] * (1 + d);
897 =====================
900 Player has come to a dead stop, possibly due to the problem with limited
901 float precision at some angle joins in the BSP hull.
903 Try fixing by pushing one pixel in each direction.
905 This is a hack, but in the interest of good gameplay...
906 ======================
908 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
913 VectorCopy (ent->fields.server->origin, oldorg);
916 for (i=0 ; i<8 ; i++)
918 // try pushing a little in an axial direction
921 case 0: dir[0] = 2; dir[1] = 0; break;
922 case 1: dir[0] = 0; dir[1] = 2; break;
923 case 2: dir[0] = -2; dir[1] = 0; break;
924 case 3: dir[0] = 0; dir[1] = -2; break;
925 case 4: dir[0] = 2; dir[1] = 2; break;
926 case 5: dir[0] = -2; dir[1] = 2; break;
927 case 6: dir[0] = 2; dir[1] = -2; break;
928 case 7: dir[0] = -2; dir[1] = -2; break;
931 SV_PushEntity (ent, dir);
933 // retry the original move
934 ent->fields.server->velocity[0] = oldvel[0];
935 ent->fields.server->velocity[1] = oldvel[1];
936 ent->fields.server->velocity[2] = 0;
937 clip = SV_FlyMove (ent, 0.1, NULL);
939 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
940 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
942 Con_DPrint("TryUnstick - success.\n");
946 // go back to the original pos and try again
947 VectorCopy (oldorg, ent->fields.server->origin);
951 VectorClear (ent->fields.server->velocity);
952 Con_DPrint("TryUnstick - failure.\n");
957 =====================
961 ======================
963 void SV_WalkMove (prvm_edict_t *ent)
965 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
966 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
969 SV_CheckVelocity(ent);
971 // do a regular slide move unless it looks like you ran into a step
972 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
973 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
975 VectorCopy (ent->fields.server->origin, start_origin);
976 VectorCopy (ent->fields.server->velocity, start_velocity);
978 clip = SV_FlyMove (ent, sv.frametime, NULL);
980 SV_CheckVelocity(ent);
982 VectorCopy(ent->fields.server->origin, originalmove_origin);
983 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
984 originalmove_clip = clip;
985 originalmove_flags = (int)ent->fields.server->flags;
986 originalmove_groundentity = ent->fields.server->groundentity;
988 if ((int)ent->fields.server->flags & FL_WATERJUMP)
991 if (sv_nostep.integer)
994 // if move didn't block on a step, return
997 // if move was not trying to move into the step, return
998 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1001 if (ent->fields.server->movetype != MOVETYPE_FLY)
1003 // return if gibbed by a trigger
1004 if (ent->fields.server->movetype != MOVETYPE_WALK)
1007 // only step up while jumping if that is enabled
1008 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1009 if (!oldonground && ent->fields.server->waterlevel == 0)
1013 // try moving up and forward to go up a step
1014 // back to start pos
1015 VectorCopy (start_origin, ent->fields.server->origin);
1016 VectorCopy (start_velocity, ent->fields.server->velocity);
1019 VectorClear (upmove);
1020 upmove[2] = sv_stepheight.value;
1021 // FIXME: don't link?
1022 SV_PushEntity(ent, upmove);
1025 ent->fields.server->velocity[2] = 0;
1026 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1027 ent->fields.server->velocity[2] += start_velocity[2];
1029 SV_CheckVelocity(ent);
1031 // check for stuckness, possibly due to the limited precision of floats
1032 // in the clipping hulls
1034 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1035 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1037 //Con_Printf("wall\n");
1038 // stepping up didn't make any progress, revert to original move
1039 VectorCopy(originalmove_origin, ent->fields.server->origin);
1040 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1041 //clip = originalmove_clip;
1042 ent->fields.server->flags = originalmove_flags;
1043 ent->fields.server->groundentity = originalmove_groundentity;
1044 // now try to unstick if needed
1045 //clip = SV_TryUnstick (ent, oldvel);
1049 //Con_Printf("step - ");
1051 // extra friction based on view angle
1052 if (clip & 2 && sv_wallfriction.integer)
1053 SV_WallFriction (ent, stepnormal);
1055 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1056 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)))
1060 VectorClear (downmove);
1061 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1062 // FIXME: don't link?
1063 downtrace = SV_PushEntity (ent, downmove);
1065 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1067 // LordHavoc: disabled this check so you can walk on monsters/players
1068 //if (ent->fields.server->solid == SOLID_BSP)
1070 //Con_Printf("onground\n");
1071 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1072 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1077 //Con_Printf("slope\n");
1078 // if the push down didn't end up on good ground, use the move without
1079 // the step up. This happens near wall / slope combinations, and can
1080 // cause the player to hop up higher on a slope too steep to climb
1081 VectorCopy(originalmove_origin, ent->fields.server->origin);
1082 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1083 //clip = originalmove_clip;
1084 ent->fields.server->flags = originalmove_flags;
1085 ent->fields.server->groundentity = originalmove_groundentity;
1088 SV_CheckVelocity(ent);
1091 //============================================================================
1097 Entities that are "stuck" to another entity
1100 void SV_Physics_Follow (prvm_edict_t *ent)
1102 vec3_t vf, vr, vu, angles, v;
1106 if (!SV_RunThink (ent))
1109 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1110 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1111 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])
1113 // quick case for no rotation
1114 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1118 angles[0] = -ent->fields.server->punchangle[0];
1119 angles[1] = ent->fields.server->punchangle[1];
1120 angles[2] = ent->fields.server->punchangle[2];
1121 AngleVectors (angles, vf, vr, vu);
1122 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];
1123 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];
1124 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];
1125 angles[0] = -e->fields.server->angles[0];
1126 angles[1] = e->fields.server->angles[1];
1127 angles[2] = e->fields.server->angles[2];
1128 AngleVectors (angles, vf, vr, vu);
1129 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1130 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1131 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1133 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1134 SV_LinkEdict (ent, true);
1138 ==============================================================================
1142 ==============================================================================
1147 SV_CheckWaterTransition
1151 void SV_CheckWaterTransition (prvm_edict_t *ent)
1154 cont = SV_PointQ1Contents(ent->fields.server->origin);
1155 if (!ent->fields.server->watertype)
1157 // just spawned here
1158 ent->fields.server->watertype = cont;
1159 ent->fields.server->waterlevel = 1;
1163 // check if the entity crossed into or out of water
1164 if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1165 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1167 if (cont <= CONTENTS_WATER)
1169 ent->fields.server->watertype = cont;
1170 ent->fields.server->waterlevel = 1;
1174 ent->fields.server->watertype = CONTENTS_EMPTY;
1175 ent->fields.server->waterlevel = 0;
1183 Toss, bounce, and fly movement. When onground, do nothing.
1186 void SV_Physics_Toss (prvm_edict_t *ent)
1191 // don't stick to ground if onground and moving upward
1192 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1193 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1195 // if onground, return without moving
1196 if ((int)ent->fields.server->flags & FL_ONGROUND)
1198 if (ent->fields.server->groundentity == 0 || sv_gameplayfix_noairborncorpse.integer)
1200 // if ent was supported by a brush model on previous frame,
1201 // and groundentity is now freed, set groundentity to 0 (floating)
1202 if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1204 // leave it suspended in the air
1205 ent->fields.server->groundentity = 0;
1209 ent->priv.server->suspendedinairflag = false;
1211 SV_CheckVelocity (ent);
1214 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1215 SV_AddGravity (ent);
1218 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1221 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1222 trace = SV_PushEntity (ent, move);
1223 if (ent->priv.server->free)
1226 if (trace.fraction < 1)
1228 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1230 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1231 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1233 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1236 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1237 // LordHavoc: fixed grenades not bouncing when fired down a slope
1238 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1240 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1241 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1243 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1244 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1245 VectorClear (ent->fields.server->velocity);
1246 VectorClear (ent->fields.server->avelocity);
1249 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1253 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1255 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1256 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1257 VectorClear (ent->fields.server->velocity);
1258 VectorClear (ent->fields.server->avelocity);
1261 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1266 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1267 if (trace.plane.normal[2] > 0.7)
1269 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1270 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1271 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1272 ent->priv.server->suspendedinairflag = true;
1273 VectorClear (ent->fields.server->velocity);
1274 VectorClear (ent->fields.server->avelocity);
1277 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1281 // check for in water
1282 SV_CheckWaterTransition (ent);
1286 ===============================================================================
1290 ===============================================================================
1297 Monsters freefall when they don't have a ground entity, otherwise
1298 all movement is done with discrete steps.
1300 This is also used for objects that have become still on the ground, but
1301 will fall if the floor is pulled out from under them.
1304 void SV_Physics_Step (prvm_edict_t *ent)
1306 // don't stick to ground if onground and moving upward
1307 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1308 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1310 // freefall if not onground/fly/swim
1311 if (!((int)ent->fields.server->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1313 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1316 SV_CheckVelocity(ent);
1317 SV_FlyMove(ent, sv.frametime, NULL);
1318 SV_LinkEdict(ent, true);
1321 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1322 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1328 SV_CheckWaterTransition(ent);
1331 //============================================================================
1333 void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove)
1335 int i = ent - prog->edicts;
1336 if (i >= 1 && i <= svs.maxclients)
1338 // make sure the velocity is sane (not a NaN)
1339 SV_CheckVelocity(ent);
1340 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1341 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1343 prog->globals.server->time = sv.time;
1344 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1345 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1349 // make sure the velocity is sane (not a NaN)
1350 SV_CheckVelocity(ent);
1351 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1352 // player_run/player_stand1 does not horribly malfunction if the
1353 // velocity becomes a number that is both == 0 and != 0
1354 // (sounds to me like NaN but to be absolutely safe...)
1355 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1356 VectorClear(ent->fields.server->velocity);
1357 // call standard client pre-think
1358 prog->globals.server->time = sv.time;
1359 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1360 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1361 SV_CheckVelocity (ent);
1364 // LordHavoc: merged client and normal entity physics
1365 switch ((int) ent->fields.server->movetype)
1368 case MOVETYPE_FAKEPUSH:
1369 SV_Physics_Pusher (ent);
1372 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1373 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1376 case MOVETYPE_FOLLOW:
1377 SV_Physics_Follow (ent);
1379 case MOVETYPE_NOCLIP:
1380 if (SV_RunThink(ent))
1383 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1384 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1386 // relink normal entities here, players always get relinked so don't relink twice
1387 if (!(i > 0 && i <= svs.maxclients))
1388 SV_LinkEdict(ent, false);
1391 SV_Physics_Step (ent);
1394 if (SV_RunThink (ent))
1396 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1397 SV_AddGravity (ent);
1398 SV_CheckStuck (ent);
1400 // relink normal entities here, players always get relinked so don't relink twice
1401 if (!(i > 0 && i <= svs.maxclients))
1402 SV_LinkEdict (ent, true);
1406 case MOVETYPE_BOUNCE:
1407 case MOVETYPE_BOUNCEMISSILE:
1408 case MOVETYPE_FLYMISSILE:
1410 if (SV_RunThink (ent) && runmove)
1411 SV_Physics_Toss (ent);
1414 if (SV_RunThink (ent) && runmove)
1416 if (i > 0 && i <= svs.maxclients)
1418 SV_CheckWater (ent);
1422 SV_Physics_Toss (ent);
1426 Con_Printf ("SV_Physics: bad movetype %i", (int)ent->fields.server->movetype);
1430 if (i >= 1 && i <= svs.maxclients)
1432 SV_CheckVelocity (ent);
1434 // call standard player post-think
1435 SV_LinkEdict (ent, true);
1437 SV_CheckVelocity (ent);
1439 prog->globals.server->time = sv.time;
1440 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1441 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1452 void SV_Physics (void)
1454 int i, newnum_edicts;
1456 qbyte runmove[MAX_EDICTS];
1458 // let the progs know that a new frame has started
1459 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1460 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1461 prog->globals.server->time = sv.time;
1462 prog->globals.server->frametime = sv.frametime;
1463 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1466 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1467 if ((runmove[i] = !ent->priv.server->free))
1468 newnum_edicts = i + 1;
1469 prog->num_edicts = max(svs.maxclients + 1, newnum_edicts);
1472 // treat each object in turn
1475 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1477 if (ent->priv.server->free)
1480 if (prog->globals.server->force_retouch)
1481 SV_LinkEdict (ent, true); // force retouch even for stationary
1483 if (i >= 1 && i <= svs.maxclients)
1485 host_client = svs.clients + i - 1;
1486 // don't do physics on disconnected clients, FrikBot relies on this
1487 if (!host_client->spawned)
1489 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1493 if (host_client->movesequence)
1494 continue; // return if running asynchronously
1496 else if (sv_freezenonclients.integer)
1499 SV_Physics_Entity(ent, runmove[i]);
1502 if (prog->globals.server->force_retouch > 0)
1503 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1505 // LordHavoc: endframe support
1508 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1509 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1510 prog->globals.server->time = sv.time;
1511 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1514 if (!sv_freezenonclients.integer)
1515 sv.time += sv.frametime;
1519 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1522 float gravity, savesolid;
1524 prvm_edict_t tempent, *tent;
1529 // copy the vars over
1530 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1531 // set up the temp entity to point to the copied vars
1533 tent->fields.server = &vars;
1535 savesolid = tossent->fields.server->solid;
1536 tossent->fields.server->solid = SOLID_NOT;
1538 // this has to fetch the field from the original edict, since our copy is truncated
1539 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1540 if (val != NULL && val->_float != 0)
1541 gravity = val->_float;
1544 gravity *= sv_gravity.value * 0.05;
1546 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1548 SV_CheckVelocity (tent);
1549 tent->fields.server->velocity[2] -= gravity;
1550 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1551 VectorScale (tent->fields.server->velocity, 0.05, move);
1552 VectorAdd (tent->fields.server->origin, move, end);
1553 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1554 VectorCopy (trace.endpos, tent->fields.server->origin);
1556 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1559 tossent->fields.server->solid = savesolid;
1560 trace.fraction = 0; // not relevant