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, (prvm_edict_t *)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, (prvm_edict_t *)trace.ent);
526 void SV_PushMove (prvm_edict_t *pusher, float movetime)
529 float savesolid, movetime2, pushltime;
530 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
532 int numcheckentities;
533 static prvm_edict_t *checkentities[MAX_EDICTS];
534 model_t *pushermodel;
537 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])
539 pusher->fields.server->ltime += movetime;
543 switch ((int) pusher->fields.server->solid)
545 // LordHavoc: valid pusher types
549 case SOLID_CORPSE: // LordHavoc: this would be weird...
551 // LordHavoc: no collisions
554 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
555 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
556 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
557 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
558 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
559 pusher->fields.server->ltime += movetime;
560 SV_LinkEdict (pusher, false);
563 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
566 index = (int) pusher->fields.server->modelindex;
567 if (index < 1 || index >= MAX_MODELS)
569 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
572 pushermodel = sv.models[index];
574 movetime2 = movetime;
575 VectorScale(pusher->fields.server->velocity, movetime2, move1);
576 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
577 if (moveangle[0] || moveangle[2])
579 for (i = 0;i < 3;i++)
583 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
584 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
588 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
589 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
593 else if (moveangle[1])
595 for (i = 0;i < 3;i++)
599 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
600 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
604 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
605 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
611 for (i = 0;i < 3;i++)
615 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
616 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
620 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
621 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
626 VectorNegate (moveangle, a);
627 AngleVectorsFLU (a, forward, left, up);
629 VectorCopy (pusher->fields.server->origin, pushorig);
630 VectorCopy (pusher->fields.server->angles, pushang);
631 pushltime = pusher->fields.server->ltime;
633 // move the pusher to its final position
635 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
636 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
637 pusher->fields.server->ltime += movetime;
638 SV_LinkEdict (pusher, false);
640 savesolid = pusher->fields.server->solid;
642 // see if any solid entities are inside the final position
645 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
646 for (e = 0;e < numcheckentities;e++)
648 prvm_edict_t *check = checkentities[e];
649 if (check->fields.server->movetype == MOVETYPE_NONE
650 || check->fields.server->movetype == MOVETYPE_PUSH
651 || check->fields.server->movetype == MOVETYPE_FOLLOW
652 || check->fields.server->movetype == MOVETYPE_NOCLIP
653 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
656 // if the entity is standing on the pusher, it will definitely be moved
657 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
659 // if the entity is not inside the pusher's final position, leave it alone
660 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
662 // remove the onground flag for non-players
663 if (check->fields.server->movetype != MOVETYPE_WALK)
664 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
668 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
671 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
672 org2[0] = DotProduct (org, forward);
673 org2[1] = DotProduct (org, left);
674 org2[2] = DotProduct (org, up);
675 VectorSubtract (org2, org, move);
676 VectorAdd (move, move1, move);
679 VectorCopy (move1, move);
681 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
682 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
683 sv.moved_edicts[num_moved++] = check;
685 // try moving the contacted entity
686 pusher->fields.server->solid = SOLID_NOT;
687 trace = SV_PushEntity (check, move);
688 // FIXME: turn players specially
689 check->fields.server->angles[1] += trace.fraction * moveangle[1];
690 pusher->fields.server->solid = savesolid; // was SOLID_BSP
692 // if it is still inside the pusher, block
693 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
695 // try moving the contacted entity a tiny bit further to account for precision errors
697 pusher->fields.server->solid = SOLID_NOT;
698 VectorScale(move, 1.1, move2);
699 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
700 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
701 SV_PushEntity (check, move2);
702 pusher->fields.server->solid = savesolid;
703 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
705 // try moving the contacted entity a tiny bit less to account for precision errors
706 pusher->fields.server->solid = SOLID_NOT;
707 VectorScale(move, 0.9, move2);
708 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
709 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
710 SV_PushEntity (check, move2);
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, 0, SUPERCONTENTS_SOLID).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 prvm_edict_t *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");
753 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
754 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
755 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
764 void SV_Physics_Pusher (prvm_edict_t *ent)
766 float thinktime, oldltime, movetime;
768 oldltime = ent->fields.server->ltime;
770 thinktime = ent->fields.server->nextthink;
771 if (thinktime < ent->fields.server->ltime + sv.frametime)
773 movetime = thinktime - ent->fields.server->ltime;
778 movetime = sv.frametime;
781 // advances ent->fields.server->ltime if not blocked
782 SV_PushMove (ent, movetime);
784 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
786 ent->fields.server->nextthink = 0;
787 prog->globals.server->time = sv.time;
788 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
789 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
790 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
796 ===============================================================================
800 ===============================================================================
807 This is a big hack to try and fix the rare case of getting stuck in the world
811 void SV_CheckStuck (prvm_edict_t *ent)
816 if (!SV_TestEntityPosition(ent))
818 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
822 VectorCopy (ent->fields.server->origin, org);
823 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
824 if (!SV_TestEntityPosition(ent))
826 Con_DPrint("Unstuck.\n");
827 SV_LinkEdict (ent, true);
831 for (z=0 ; z< 18 ; z++)
832 for (i=-1 ; i <= 1 ; i++)
833 for (j=-1 ; j <= 1 ; j++)
835 ent->fields.server->origin[0] = org[0] + i;
836 ent->fields.server->origin[1] = org[1] + j;
837 ent->fields.server->origin[2] = org[2] + z;
838 if (!SV_TestEntityPosition(ent))
840 Con_DPrint("Unstuck.\n");
841 SV_LinkEdict (ent, true);
846 VectorCopy (org, ent->fields.server->origin);
847 Con_DPrint("player is stuck.\n");
856 qboolean SV_CheckWater (prvm_edict_t *ent)
861 point[0] = ent->fields.server->origin[0];
862 point[1] = ent->fields.server->origin[1];
863 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
865 ent->fields.server->waterlevel = 0;
866 ent->fields.server->watertype = CONTENTS_EMPTY;
867 cont = SV_PointSuperContents(point);
868 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
870 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
871 ent->fields.server->waterlevel = 1;
872 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
873 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
875 ent->fields.server->waterlevel = 2;
876 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
877 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
878 ent->fields.server->waterlevel = 3;
882 return ent->fields.server->waterlevel > 1;
891 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
894 vec3_t forward, into, side;
896 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
897 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
899 // cut the tangential velocity
900 i = DotProduct (stepnormal, ent->fields.server->velocity);
901 VectorScale (stepnormal, i, into);
902 VectorSubtract (ent->fields.server->velocity, into, side);
903 ent->fields.server->velocity[0] = side[0] * (1 + d);
904 ent->fields.server->velocity[1] = side[1] * (1 + d);
909 =====================
912 Player has come to a dead stop, possibly due to the problem with limited
913 float precision at some angle joins in the BSP hull.
915 Try fixing by pushing one pixel in each direction.
917 This is a hack, but in the interest of good gameplay...
918 ======================
920 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
925 VectorCopy (ent->fields.server->origin, oldorg);
928 for (i=0 ; i<8 ; i++)
930 // try pushing a little in an axial direction
933 case 0: dir[0] = 2; dir[1] = 0; break;
934 case 1: dir[0] = 0; dir[1] = 2; break;
935 case 2: dir[0] = -2; dir[1] = 0; break;
936 case 3: dir[0] = 0; dir[1] = -2; break;
937 case 4: dir[0] = 2; dir[1] = 2; break;
938 case 5: dir[0] = -2; dir[1] = 2; break;
939 case 6: dir[0] = 2; dir[1] = -2; break;
940 case 7: dir[0] = -2; dir[1] = -2; break;
943 SV_PushEntity (ent, dir);
945 // retry the original move
946 ent->fields.server->velocity[0] = oldvel[0];
947 ent->fields.server->velocity[1] = oldvel[1];
948 ent->fields.server->velocity[2] = 0;
949 clip = SV_FlyMove (ent, 0.1, NULL);
951 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
952 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
954 Con_DPrint("TryUnstick - success.\n");
958 // go back to the original pos and try again
959 VectorCopy (oldorg, ent->fields.server->origin);
963 VectorClear (ent->fields.server->velocity);
964 Con_DPrint("TryUnstick - failure.\n");
969 =====================
973 ======================
975 void SV_WalkMove (prvm_edict_t *ent)
977 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
978 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
981 SV_CheckVelocity(ent);
983 // do a regular slide move unless it looks like you ran into a step
984 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
985 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
987 VectorCopy (ent->fields.server->origin, start_origin);
988 VectorCopy (ent->fields.server->velocity, start_velocity);
990 clip = SV_FlyMove (ent, sv.frametime, NULL);
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_CheckVelocity(ent);
1103 //============================================================================
1109 Entities that are "stuck" to another entity
1112 void SV_Physics_Follow (prvm_edict_t *ent)
1114 vec3_t vf, vr, vu, angles, v;
1118 if (!SV_RunThink (ent))
1121 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1122 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1123 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])
1125 // quick case for no rotation
1126 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1130 angles[0] = -ent->fields.server->punchangle[0];
1131 angles[1] = ent->fields.server->punchangle[1];
1132 angles[2] = ent->fields.server->punchangle[2];
1133 AngleVectors (angles, vf, vr, vu);
1134 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];
1135 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];
1136 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];
1137 angles[0] = -e->fields.server->angles[0];
1138 angles[1] = e->fields.server->angles[1];
1139 angles[2] = e->fields.server->angles[2];
1140 AngleVectors (angles, vf, vr, vu);
1141 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1142 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1143 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1145 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1146 SV_LinkEdict (ent, true);
1150 ==============================================================================
1154 ==============================================================================
1159 SV_CheckWaterTransition
1163 void SV_CheckWaterTransition (prvm_edict_t *ent)
1166 cont = SV_PointQ1Contents(ent->fields.server->origin);
1167 if (!ent->fields.server->watertype)
1169 // just spawned here
1170 ent->fields.server->watertype = cont;
1171 ent->fields.server->waterlevel = 1;
1175 // check if the entity crossed into or out of water
1176 if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1177 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1179 if (cont <= CONTENTS_WATER)
1181 ent->fields.server->watertype = cont;
1182 ent->fields.server->waterlevel = 1;
1186 ent->fields.server->watertype = CONTENTS_EMPTY;
1187 ent->fields.server->waterlevel = 0;
1195 Toss, bounce, and fly movement. When onground, do nothing.
1198 void SV_Physics_Toss (prvm_edict_t *ent)
1203 // don't stick to ground if onground and moving upward
1204 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1205 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1207 // if onground, return without moving
1208 if ((int)ent->fields.server->flags & FL_ONGROUND)
1210 if (ent->fields.server->groundentity == 0 || sv_gameplayfix_noairborncorpse.integer)
1212 // if ent was supported by a brush model on previous frame,
1213 // and groundentity is now freed, set groundentity to 0 (floating)
1214 if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1216 // leave it suspended in the air
1217 ent->fields.server->groundentity = 0;
1221 ent->priv.server->suspendedinairflag = false;
1223 SV_CheckVelocity (ent);
1226 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1227 SV_AddGravity (ent);
1230 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1233 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1234 trace = SV_PushEntity (ent, move);
1235 if (ent->priv.server->free)
1238 if (trace.fraction < 1)
1240 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1242 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1243 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1245 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1248 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1249 // LordHavoc: fixed grenades not bouncing when fired down a slope
1250 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1252 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1253 if (trace.plane.normal[2] > 0.7 && fabs(d) < 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;
1265 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1267 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1268 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1269 VectorClear (ent->fields.server->velocity);
1270 VectorClear (ent->fields.server->avelocity);
1273 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1278 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1279 if (trace.plane.normal[2] > 0.7)
1281 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1282 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1283 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1284 ent->priv.server->suspendedinairflag = true;
1285 VectorClear (ent->fields.server->velocity);
1286 VectorClear (ent->fields.server->avelocity);
1289 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1293 // check for in water
1294 SV_CheckWaterTransition (ent);
1298 ===============================================================================
1302 ===============================================================================
1309 Monsters freefall when they don't have a ground entity, otherwise
1310 all movement is done with discrete steps.
1312 This is also used for objects that have become still on the ground, but
1313 will fall if the floor is pulled out from under them.
1316 void SV_Physics_Step (prvm_edict_t *ent)
1318 // don't stick to ground if onground and moving upward
1319 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1320 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1322 // freefall if not onground/fly/swim
1323 if (!((int)ent->fields.server->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1325 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1328 SV_CheckVelocity(ent);
1329 SV_FlyMove(ent, sv.frametime, NULL);
1330 SV_LinkEdict(ent, true);
1333 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1334 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1340 SV_CheckWaterTransition(ent);
1343 //============================================================================
1345 void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove)
1347 int i = ent - prog->edicts;
1348 if (i >= 1 && i <= svs.maxclients)
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 && sv_playerphysicsqc.integer)
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 Con_Printf ("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