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 float savesolid, movetime2, pushltime;
531 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
533 int numcheckentities;
534 static prvm_edict_t *checkentities[MAX_EDICTS];
535 model_t *pushermodel;
538 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])
540 pusher->fields.server->ltime += movetime;
544 switch ((int) pusher->fields.server->solid)
546 // LordHavoc: valid pusher types
550 case SOLID_CORPSE: // LordHavoc: this would be weird...
552 // LordHavoc: no collisions
555 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
556 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
557 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
558 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
559 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
560 pusher->fields.server->ltime += movetime;
561 SV_LinkEdict (pusher, false);
564 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
567 index = (int) pusher->fields.server->modelindex;
568 if (index < 1 || index >= MAX_MODELS)
570 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
573 pushermodel = sv.models[index];
575 movetime2 = movetime;
576 VectorScale(pusher->fields.server->velocity, movetime2, move1);
577 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
578 if (moveangle[0] || moveangle[2])
580 for (i = 0;i < 3;i++)
584 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
585 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
589 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
590 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
594 else if (moveangle[1])
596 for (i = 0;i < 3;i++)
600 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
601 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
605 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
606 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
612 for (i = 0;i < 3;i++)
616 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
617 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
621 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
622 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
627 VectorNegate (moveangle, a);
628 AngleVectorsFLU (a, forward, left, up);
630 VectorCopy (pusher->fields.server->origin, pushorig);
631 VectorCopy (pusher->fields.server->angles, pushang);
632 pushltime = pusher->fields.server->ltime;
634 // move the pusher to its final position
636 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
637 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
638 pusher->fields.server->ltime += movetime;
639 SV_LinkEdict (pusher, false);
641 savesolid = pusher->fields.server->solid;
643 // see if any solid entities are inside the final position
646 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
647 for (e = 0;e < numcheckentities;e++)
649 prvm_edict_t *check = checkentities[e];
650 if (check->fields.server->movetype == MOVETYPE_NONE
651 || check->fields.server->movetype == MOVETYPE_PUSH
652 || check->fields.server->movetype == MOVETYPE_FOLLOW
653 || check->fields.server->movetype == MOVETYPE_NOCLIP
654 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
657 // if the entity is standing on the pusher, it will definitely be moved
658 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
660 // if the entity is not inside the pusher's final position, leave it alone
661 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
663 // remove the onground flag for non-players
664 if (check->fields.server->movetype != MOVETYPE_WALK)
665 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
669 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
672 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
673 org2[0] = DotProduct (org, forward);
674 org2[1] = DotProduct (org, left);
675 org2[2] = DotProduct (org, up);
676 VectorSubtract (org2, org, move);
677 VectorAdd (move, move1, move);
680 VectorCopy (move1, move);
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
698 pusher->fields.server->solid = SOLID_NOT;
699 VectorScale(move, 1.1, move2);
700 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
701 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
702 SV_PushEntity (check, move2);
703 pusher->fields.server->solid = savesolid;
704 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
706 // try moving the contacted entity a tiny bit less to account for precision errors
707 pusher->fields.server->solid = SOLID_NOT;
708 VectorScale(move, 0.9, move2);
709 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
710 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
711 SV_PushEntity (check, move2);
712 pusher->fields.server->solid = savesolid;
713 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
715 // still inside pusher, so it's really blocked
718 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
720 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
723 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
724 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
728 VectorCopy (pushorig, pusher->fields.server->origin);
729 VectorCopy (pushang, pusher->fields.server->angles);
730 pusher->fields.server->ltime = pushltime;
731 SV_LinkEdict (pusher, false);
733 // move back any entities we already moved
734 for (i = 0;i < num_moved;i++)
736 prvm_edict_t *ed = sv.moved_edicts[i];
737 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
738 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
739 SV_LinkEdict (ed, false);
742 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
743 if (pusher->fields.server->blocked)
745 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
746 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
747 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
754 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
755 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
756 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
765 void SV_Physics_Pusher (prvm_edict_t *ent)
767 float thinktime, oldltime, movetime;
769 oldltime = ent->fields.server->ltime;
771 thinktime = ent->fields.server->nextthink;
772 if (thinktime < ent->fields.server->ltime + sv.frametime)
774 movetime = thinktime - ent->fields.server->ltime;
779 movetime = sv.frametime;
782 // advances ent->fields.server->ltime if not blocked
783 SV_PushMove (ent, movetime);
785 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
787 ent->fields.server->nextthink = 0;
788 prog->globals.server->time = sv.time;
789 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
790 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
791 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
797 ===============================================================================
801 ===============================================================================
808 This is a big hack to try and fix the rare case of getting stuck in the world
812 void SV_CheckStuck (prvm_edict_t *ent)
817 if (!SV_TestEntityPosition(ent))
819 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
823 VectorCopy (ent->fields.server->origin, org);
824 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
825 if (!SV_TestEntityPosition(ent))
827 Con_DPrint("Unstuck.\n");
828 SV_LinkEdict (ent, true);
832 for (z=0 ; z< 18 ; z++)
833 for (i=-1 ; i <= 1 ; i++)
834 for (j=-1 ; j <= 1 ; j++)
836 ent->fields.server->origin[0] = org[0] + i;
837 ent->fields.server->origin[1] = org[1] + j;
838 ent->fields.server->origin[2] = org[2] + z;
839 if (!SV_TestEntityPosition(ent))
841 Con_DPrint("Unstuck.\n");
842 SV_LinkEdict (ent, true);
847 VectorCopy (org, ent->fields.server->origin);
848 Con_DPrint("player is stuck.\n");
857 qboolean SV_CheckWater (prvm_edict_t *ent)
862 point[0] = ent->fields.server->origin[0];
863 point[1] = ent->fields.server->origin[1];
864 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
866 ent->fields.server->waterlevel = 0;
867 ent->fields.server->watertype = CONTENTS_EMPTY;
868 cont = SV_PointSuperContents(point);
869 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
871 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
872 ent->fields.server->waterlevel = 1;
873 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
874 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
876 ent->fields.server->waterlevel = 2;
877 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
878 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
879 ent->fields.server->waterlevel = 3;
883 return ent->fields.server->waterlevel > 1;
892 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
895 vec3_t forward, into, side;
897 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
898 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
900 // cut the tangential velocity
901 i = DotProduct (stepnormal, ent->fields.server->velocity);
902 VectorScale (stepnormal, i, into);
903 VectorSubtract (ent->fields.server->velocity, into, side);
904 ent->fields.server->velocity[0] = side[0] * (1 + d);
905 ent->fields.server->velocity[1] = side[1] * (1 + d);
910 =====================
913 Player has come to a dead stop, possibly due to the problem with limited
914 float precision at some angle joins in the BSP hull.
916 Try fixing by pushing one pixel in each direction.
918 This is a hack, but in the interest of good gameplay...
919 ======================
921 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
926 VectorCopy (ent->fields.server->origin, oldorg);
929 for (i=0 ; i<8 ; i++)
931 // try pushing a little in an axial direction
934 case 0: dir[0] = 2; dir[1] = 0; break;
935 case 1: dir[0] = 0; dir[1] = 2; break;
936 case 2: dir[0] = -2; dir[1] = 0; break;
937 case 3: dir[0] = 0; dir[1] = -2; break;
938 case 4: dir[0] = 2; dir[1] = 2; break;
939 case 5: dir[0] = -2; dir[1] = 2; break;
940 case 6: dir[0] = 2; dir[1] = -2; break;
941 case 7: dir[0] = -2; dir[1] = -2; break;
944 SV_PushEntity (ent, dir);
946 // retry the original move
947 ent->fields.server->velocity[0] = oldvel[0];
948 ent->fields.server->velocity[1] = oldvel[1];
949 ent->fields.server->velocity[2] = 0;
950 clip = SV_FlyMove (ent, 0.1, NULL);
952 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
953 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
955 Con_DPrint("TryUnstick - success.\n");
959 // go back to the original pos and try again
960 VectorCopy (oldorg, ent->fields.server->origin);
964 VectorClear (ent->fields.server->velocity);
965 Con_DPrint("TryUnstick - failure.\n");
970 =====================
974 ======================
976 void SV_WalkMove (prvm_edict_t *ent)
978 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
979 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
982 SV_CheckVelocity(ent);
984 // do a regular slide move unless it looks like you ran into a step
985 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
986 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
988 VectorCopy (ent->fields.server->origin, start_origin);
989 VectorCopy (ent->fields.server->velocity, start_velocity);
991 clip = SV_FlyMove (ent, sv.frametime, NULL);
993 SV_CheckVelocity(ent);
995 VectorCopy(ent->fields.server->origin, originalmove_origin);
996 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
997 originalmove_clip = clip;
998 originalmove_flags = (int)ent->fields.server->flags;
999 originalmove_groundentity = ent->fields.server->groundentity;
1001 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1004 if (sv_nostep.integer)
1007 // if move didn't block on a step, return
1010 // if move was not trying to move into the step, return
1011 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1014 if (ent->fields.server->movetype != MOVETYPE_FLY)
1016 // return if gibbed by a trigger
1017 if (ent->fields.server->movetype != MOVETYPE_WALK)
1020 // only step up while jumping if that is enabled
1021 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1022 if (!oldonground && ent->fields.server->waterlevel == 0)
1026 // try moving up and forward to go up a step
1027 // back to start pos
1028 VectorCopy (start_origin, ent->fields.server->origin);
1029 VectorCopy (start_velocity, ent->fields.server->velocity);
1032 VectorClear (upmove);
1033 upmove[2] = sv_stepheight.value;
1034 // FIXME: don't link?
1035 SV_PushEntity(ent, upmove);
1038 ent->fields.server->velocity[2] = 0;
1039 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1040 ent->fields.server->velocity[2] += start_velocity[2];
1042 SV_CheckVelocity(ent);
1044 // check for stuckness, possibly due to the limited precision of floats
1045 // in the clipping hulls
1047 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1048 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1050 //Con_Printf("wall\n");
1051 // stepping up didn't make any progress, revert to original move
1052 VectorCopy(originalmove_origin, ent->fields.server->origin);
1053 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1054 //clip = originalmove_clip;
1055 ent->fields.server->flags = originalmove_flags;
1056 ent->fields.server->groundentity = originalmove_groundentity;
1057 // now try to unstick if needed
1058 //clip = SV_TryUnstick (ent, oldvel);
1062 //Con_Printf("step - ");
1064 // extra friction based on view angle
1065 if (clip & 2 && sv_wallfriction.integer)
1066 SV_WallFriction (ent, stepnormal);
1068 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1069 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)))
1073 VectorClear (downmove);
1074 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1075 // FIXME: don't link?
1076 downtrace = SV_PushEntity (ent, downmove);
1078 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1080 // LordHavoc: disabled this check so you can walk on monsters/players
1081 //if (ent->fields.server->solid == SOLID_BSP)
1083 //Con_Printf("onground\n");
1084 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1085 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1090 //Con_Printf("slope\n");
1091 // if the push down didn't end up on good ground, use the move without
1092 // the step up. This happens near wall / slope combinations, and can
1093 // cause the player to hop up higher on a slope too steep to climb
1094 VectorCopy(originalmove_origin, ent->fields.server->origin);
1095 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1096 //clip = originalmove_clip;
1097 ent->fields.server->flags = originalmove_flags;
1098 ent->fields.server->groundentity = originalmove_groundentity;
1101 SV_CheckVelocity(ent);
1104 //============================================================================
1110 Entities that are "stuck" to another entity
1113 void SV_Physics_Follow (prvm_edict_t *ent)
1115 vec3_t vf, vr, vu, angles, v;
1119 if (!SV_RunThink (ent))
1122 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1123 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1124 if (e->fields.server->angles[0] == ent->fields.server->punchangle[0] && e->fields.server->angles[1] == ent->fields.server->punchangle[1] && e->fields.server->angles[2] == ent->fields.server->punchangle[2])
1126 // quick case for no rotation
1127 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1131 angles[0] = -ent->fields.server->punchangle[0];
1132 angles[1] = ent->fields.server->punchangle[1];
1133 angles[2] = ent->fields.server->punchangle[2];
1134 AngleVectors (angles, vf, vr, vu);
1135 v[0] = ent->fields.server->view_ofs[0] * vf[0] + ent->fields.server->view_ofs[1] * vr[0] + ent->fields.server->view_ofs[2] * vu[0];
1136 v[1] = ent->fields.server->view_ofs[0] * vf[1] + ent->fields.server->view_ofs[1] * vr[1] + ent->fields.server->view_ofs[2] * vu[1];
1137 v[2] = ent->fields.server->view_ofs[0] * vf[2] + ent->fields.server->view_ofs[1] * vr[2] + ent->fields.server->view_ofs[2] * vu[2];
1138 angles[0] = -e->fields.server->angles[0];
1139 angles[1] = e->fields.server->angles[1];
1140 angles[2] = e->fields.server->angles[2];
1141 AngleVectors (angles, vf, vr, vu);
1142 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1143 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1144 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1146 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1147 SV_LinkEdict (ent, true);
1151 ==============================================================================
1155 ==============================================================================
1160 SV_CheckWaterTransition
1164 void SV_CheckWaterTransition (prvm_edict_t *ent)
1167 cont = SV_PointQ1Contents(ent->fields.server->origin);
1168 if (!ent->fields.server->watertype)
1170 // just spawned here
1171 ent->fields.server->watertype = cont;
1172 ent->fields.server->waterlevel = 1;
1176 // check if the entity crossed into or out of water
1177 if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1178 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1180 if (cont <= CONTENTS_WATER)
1182 ent->fields.server->watertype = cont;
1183 ent->fields.server->waterlevel = 1;
1187 ent->fields.server->watertype = CONTENTS_EMPTY;
1188 ent->fields.server->waterlevel = 0;
1196 Toss, bounce, and fly movement. When onground, do nothing.
1199 void SV_Physics_Toss (prvm_edict_t *ent)
1204 // don't stick to ground if onground and moving upward
1205 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1206 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1208 // if onground, return without moving
1209 if ((int)ent->fields.server->flags & FL_ONGROUND)
1211 if (ent->fields.server->groundentity == 0 || sv_gameplayfix_noairborncorpse.integer)
1213 // if ent was supported by a brush model on previous frame,
1214 // and groundentity is now freed, set groundentity to 0 (floating)
1215 if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1217 // leave it suspended in the air
1218 ent->fields.server->groundentity = 0;
1222 ent->priv.server->suspendedinairflag = false;
1224 SV_CheckVelocity (ent);
1227 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1228 SV_AddGravity (ent);
1231 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1234 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1235 trace = SV_PushEntity (ent, move);
1236 if (ent->priv.server->free)
1239 if (trace.fraction < 1)
1241 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1243 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1244 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1246 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1249 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1250 // LordHavoc: fixed grenades not bouncing when fired down a slope
1251 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1253 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1254 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1256 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1257 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1258 VectorClear (ent->fields.server->velocity);
1259 VectorClear (ent->fields.server->avelocity);
1262 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1266 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1268 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1269 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1270 VectorClear (ent->fields.server->velocity);
1271 VectorClear (ent->fields.server->avelocity);
1274 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1279 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1280 if (trace.plane.normal[2] > 0.7)
1282 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1283 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1284 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1285 ent->priv.server->suspendedinairflag = true;
1286 VectorClear (ent->fields.server->velocity);
1287 VectorClear (ent->fields.server->avelocity);
1290 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1294 // check for in water
1295 SV_CheckWaterTransition (ent);
1299 ===============================================================================
1303 ===============================================================================
1310 Monsters freefall when they don't have a ground entity, otherwise
1311 all movement is done with discrete steps.
1313 This is also used for objects that have become still on the ground, but
1314 will fall if the floor is pulled out from under them.
1317 void SV_Physics_Step (prvm_edict_t *ent)
1319 // don't stick to ground if onground and moving upward
1320 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1321 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1323 // freefall if not onground/fly/swim
1324 if (!((int)ent->fields.server->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1326 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1329 SV_CheckVelocity(ent);
1330 SV_FlyMove(ent, sv.frametime, NULL);
1331 SV_LinkEdict(ent, true);
1334 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1335 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1341 SV_CheckWaterTransition(ent);
1344 //============================================================================
1346 void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove)
1348 int i = ent - prog->edicts;
1349 if (i >= 1 && i <= svs.maxclients)
1351 // make sure the velocity is sane (not a NaN)
1352 SV_CheckVelocity(ent);
1353 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1354 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1356 prog->globals.server->time = sv.time;
1357 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1358 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1362 // make sure the velocity is sane (not a NaN)
1363 SV_CheckVelocity(ent);
1364 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1365 // player_run/player_stand1 does not horribly malfunction if the
1366 // velocity becomes a number that is both == 0 and != 0
1367 // (sounds to me like NaN but to be absolutely safe...)
1368 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1369 VectorClear(ent->fields.server->velocity);
1370 // call standard client pre-think
1371 prog->globals.server->time = sv.time;
1372 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1373 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1374 SV_CheckVelocity (ent);
1377 // LordHavoc: merged client and normal entity physics
1378 switch ((int) ent->fields.server->movetype)
1381 case MOVETYPE_FAKEPUSH:
1382 SV_Physics_Pusher (ent);
1385 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1386 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1389 case MOVETYPE_FOLLOW:
1390 SV_Physics_Follow (ent);
1392 case MOVETYPE_NOCLIP:
1393 if (SV_RunThink(ent))
1396 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1397 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1399 // relink normal entities here, players always get relinked so don't relink twice
1400 if (!(i > 0 && i <= svs.maxclients))
1401 SV_LinkEdict(ent, false);
1404 SV_Physics_Step (ent);
1407 if (SV_RunThink (ent))
1409 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1410 SV_AddGravity (ent);
1411 SV_CheckStuck (ent);
1413 // relink normal entities here, players always get relinked so don't relink twice
1414 if (!(i > 0 && i <= svs.maxclients))
1415 SV_LinkEdict (ent, true);
1419 case MOVETYPE_BOUNCE:
1420 case MOVETYPE_BOUNCEMISSILE:
1421 case MOVETYPE_FLYMISSILE:
1423 if (SV_RunThink (ent) && runmove)
1424 SV_Physics_Toss (ent);
1427 if (SV_RunThink (ent) && runmove)
1429 if (i > 0 && i <= svs.maxclients)
1431 SV_CheckWater (ent);
1435 SV_Physics_Toss (ent);
1439 Con_Printf ("SV_Physics: bad movetype %i", (int)ent->fields.server->movetype);
1443 if (i >= 1 && i <= svs.maxclients)
1445 SV_CheckVelocity (ent);
1447 // call standard player post-think
1448 SV_LinkEdict (ent, true);
1450 SV_CheckVelocity (ent);
1452 prog->globals.server->time = sv.time;
1453 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1454 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1465 void SV_Physics (void)
1467 int i, newnum_edicts;
1469 qbyte runmove[MAX_EDICTS];
1471 // let the progs know that a new frame has started
1472 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1473 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1474 prog->globals.server->time = sv.time;
1475 prog->globals.server->frametime = sv.frametime;
1476 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1479 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1480 if ((runmove[i] = !ent->priv.server->free))
1481 newnum_edicts = i + 1;
1482 prog->num_edicts = max(svs.maxclients + 1, newnum_edicts);
1485 // treat each object in turn
1488 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1490 if (ent->priv.server->free)
1493 if (prog->globals.server->force_retouch)
1494 SV_LinkEdict (ent, true); // force retouch even for stationary
1496 if (i >= 1 && i <= svs.maxclients)
1498 host_client = svs.clients + i - 1;
1499 // don't do physics on disconnected clients, FrikBot relies on this
1500 if (!host_client->spawned)
1502 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1506 if (host_client->movesequence)
1507 continue; // return if running asynchronously
1509 else if (sv_freezenonclients.integer)
1512 SV_Physics_Entity(ent, runmove[i]);
1515 if (prog->globals.server->force_retouch > 0)
1516 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1518 // LordHavoc: endframe support
1521 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1522 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1523 prog->globals.server->time = sv.time;
1524 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1527 if (!sv_freezenonclients.integer)
1528 sv.time += sv.frametime;
1532 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1535 float gravity, savesolid;
1537 prvm_edict_t tempent, *tent;
1542 // copy the vars over
1543 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1544 // set up the temp entity to point to the copied vars
1546 tent->fields.server = &vars;
1548 savesolid = tossent->fields.server->solid;
1549 tossent->fields.server->solid = SOLID_NOT;
1551 // this has to fetch the field from the original edict, since our copy is truncated
1552 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1553 if (val != NULL && val->_float != 0)
1554 gravity = val->_float;
1557 gravity *= sv_gravity.value * 0.05;
1559 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1561 SV_CheckVelocity (tent);
1562 tent->fields.server->velocity[2] -= gravity;
1563 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1564 VectorScale (tent->fields.server->velocity, 0.05, move);
1565 VectorAdd (tent->fields.server->origin, move, end);
1566 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1567 VectorCopy (trace.endpos, tent->fields.server->origin);
1569 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1572 tossent->fields.server->solid = savesolid;
1573 trace.fraction = 0; // not relevant