2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
29 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38 solid_edge items only clip against bsp models.
42 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4"};
43 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100"};
44 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800"};
45 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000"};
46 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0"};
47 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18"};
48 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "1"};
49 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1"};
50 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0"};
51 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0"};
53 #define MOVE_EPSILON 0.01
55 void SV_Physics_Toss (prvm_edict_t *ent);
57 void SV_Phys_Init (void)
59 Cvar_RegisterVariable(&sv_stepheight);
60 Cvar_RegisterVariable(&sv_jumpstep);
61 Cvar_RegisterVariable(&sv_wallfriction);
62 Cvar_RegisterVariable(&sv_newflymove);
63 Cvar_RegisterVariable(&sv_freezenonclients);
71 void SV_CheckAllEnts (void)
76 // see if any solid entities are inside the final position
77 check = PRVM_NEXT_EDICT(prog->edicts);
78 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
80 if (check->priv.server->free)
82 if (check->fields.server->movetype == MOVETYPE_PUSH
83 || check->fields.server->movetype == MOVETYPE_NONE
84 || check->fields.server->movetype == MOVETYPE_FOLLOW
85 || check->fields.server->movetype == MOVETYPE_NOCLIP)
88 if (SV_TestEntityPosition (check))
89 Con_Print("entity in invalid position\n");
98 void SV_CheckVelocity (prvm_edict_t *ent)
106 for (i=0 ; i<3 ; i++)
108 if (IS_NAN(ent->fields.server->velocity[i]))
110 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
111 ent->fields.server->velocity[i] = 0;
113 if (IS_NAN(ent->fields.server->origin[i]))
115 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
116 ent->fields.server->origin[i] = 0;
120 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
121 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
122 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
124 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
125 ent->fields.server->velocity[0] *= wishspeed;
126 ent->fields.server->velocity[1] *= wishspeed;
127 ent->fields.server->velocity[2] *= wishspeed;
135 Runs thinking code if time. There is some play in the exact time the think
136 function will be called, because it is called before any movement is done
137 in a frame. Not used for pushmove objects, because they must be exact.
138 Returns false if the entity removed itself.
141 qboolean SV_RunThink (prvm_edict_t *ent)
145 thinktime = ent->fields.server->nextthink;
146 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
149 // don't let things stay in the past.
150 // it is possible to start that way by a trigger with a local time.
151 if (thinktime < sv.time)
154 ent->fields.server->nextthink = 0;
155 prog->globals.server->time = thinktime;
156 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
157 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
158 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
159 return !ent->priv.server->free;
166 Two entities have touched, so run their touch functions
169 void SV_Impact (prvm_edict_t *e1, prvm_edict_t *e2)
171 int old_self, old_other;
173 old_self = prog->globals.server->self;
174 old_other = prog->globals.server->other;
176 prog->globals.server->time = sv.time;
177 if (e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
179 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
180 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
181 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
184 if (e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
186 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
187 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
188 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
191 prog->globals.server->self = old_self;
192 prog->globals.server->other = old_other;
200 Slide off of the impacting object
201 returns the blocked flags (1 = floor, 2 = step / wall)
204 #define STOP_EPSILON 0.1
205 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
210 backoff = -DotProduct (in, normal) * overbounce;
211 VectorMA(in, backoff, normal, out);
213 for (i = 0;i < 3;i++)
214 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
223 The basic solid body movement clip that slides along multiple planes
224 Returns the clipflags if the velocity was modified (hit something solid)
228 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
231 // LordHavoc: increased from 5 to 32
232 #define MAX_CLIP_PLANES 32
233 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
235 int blocked, bumpcount;
236 int i, j, impact, numplanes;
238 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
241 VectorCopy(ent->fields.server->velocity, original_velocity);
242 VectorCopy(ent->fields.server->velocity, primal_velocity);
245 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
247 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
250 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
251 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
253 //if (trace.fraction < 0.002)
258 VectorCopy(ent->fields.server->origin, start);
259 start[2] += 3;//0.03125;
260 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
261 end[2] += 3;//0.03125;
262 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
263 if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
265 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
271 for (i = 0;i < numplanes;i++)
273 VectorCopy(ent->fields.server->origin, start);
274 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
275 VectorMA(start, 3, planes[i], start);
276 VectorMA(end, 3, planes[i], end);
277 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
278 if (trace.fraction < testtrace.fraction)
281 VectorCopy(start, ent->fields.server->origin);
286 // VectorAdd(ent->fields.server->origin, planes[j], start);
292 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
293 if (trace.fraction < 1)
294 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
299 if (trace.startsolid)
301 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
302 // entity is trapped in another solid
303 VectorClear(ent->fields.server->velocity);
308 // break if it moved the entire distance
309 if (trace.fraction == 1)
311 VectorCopy(trace.endpos, ent->fields.server->origin);
317 Con_Printf ("SV_FlyMove: !trace.ent");
318 trace.ent = prog->edicts;
321 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
325 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
329 if (trace.plane.normal[2])
331 if (trace.plane.normal[2] > 0.7)
335 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
336 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
343 // save the trace for player extrafriction
345 VectorCopy(trace.plane.normal, stepnormal);
348 if (trace.fraction >= 0.001)
350 // actually covered some distance
351 VectorCopy(trace.endpos, ent->fields.server->origin);
352 VectorCopy(ent->fields.server->velocity, original_velocity);
356 // run the impact function
359 SV_Impact(ent, trace.ent);
361 // break if removed by the impact function
362 if (ent->priv.server->free)
366 time_left *= 1 - trace.fraction;
368 // clipped to another plane
369 if (numplanes >= MAX_CLIP_PLANES)
371 // this shouldn't really happen
372 VectorClear(ent->fields.server->velocity);
378 for (i = 0;i < numplanes;i++)
379 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
383 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
388 VectorCopy(trace.plane.normal, planes[numplanes]);
391 if (sv_newflymove.integer)
392 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
395 // modify original_velocity so it parallels all of the clip planes
396 for (i = 0;i < numplanes;i++)
398 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
399 for (j = 0;j < numplanes;j++)
404 if (DotProduct(new_velocity, planes[j]) < 0)
414 // go along this plane
415 VectorCopy(new_velocity, ent->fields.server->velocity);
419 // go along the crease
422 VectorClear(ent->fields.server->velocity);
426 CrossProduct(planes[0], planes[1], dir);
427 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
428 VectorNormalize(dir);
429 d = DotProduct(dir, ent->fields.server->velocity);
430 VectorScale(dir, d, ent->fields.server->velocity);
434 // if current velocity is against the original velocity,
435 // stop dead to avoid tiny occilations in sloping corners
436 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
438 VectorClear(ent->fields.server->velocity);
443 //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2]);
446 if ((blocked & 1) == 0 && bumpcount > 1)
448 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
449 // flag ONGROUND if there's ground under it
450 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
462 void SV_AddGravity (prvm_edict_t *ent)
467 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
468 if (val!=0 && val->_float)
469 ent_gravity = val->_float;
472 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
477 ===============================================================================
481 ===============================================================================
488 Does not change the entities velocity at all
491 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
497 VectorAdd (ent->fields.server->origin, push, end);
499 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
501 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
502 type = MOVE_NOMONSTERS; // only clip against bmodels
506 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
508 VectorCopy (trace.endpos, ent->fields.server->origin);
509 SV_LinkEdict (ent, true);
511 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
512 SV_Impact (ent, trace.ent);
523 trace_t SV_ClipMoveToEntity (prvm_edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
524 void SV_PushMove (prvm_edict_t *pusher, float movetime)
527 prvm_edict_t *check, *ed;
528 float savesolid, movetime2, pushltime;
529 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
531 int numcheckentities;
532 static prvm_edict_t *checkentities[MAX_EDICTS];
533 model_t *pushermodel;
536 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])
538 pusher->fields.server->ltime += movetime;
542 switch ((int) pusher->fields.server->solid)
544 // LordHavoc: valid pusher types
548 case SOLID_CORPSE: // LordHavoc: this would be weird...
550 // LordHavoc: no collisions
553 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
554 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
555 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
556 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
557 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
558 pusher->fields.server->ltime += movetime;
559 SV_LinkEdict (pusher, false);
562 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
565 index = (int) pusher->fields.server->modelindex;
566 if (index < 1 || index >= MAX_MODELS)
568 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
571 pushermodel = sv.models[index];
573 movetime2 = movetime;
574 VectorScale(pusher->fields.server->velocity, movetime2, move1);
575 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
576 if (moveangle[0] || moveangle[2])
578 for (i = 0;i < 3;i++)
582 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
583 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
587 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
588 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
592 else if (moveangle[1])
594 for (i = 0;i < 3;i++)
598 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
599 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
603 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
604 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
610 for (i = 0;i < 3;i++)
614 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
615 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
619 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
620 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
625 VectorNegate (moveangle, a);
626 AngleVectorsFLU (a, forward, left, up);
628 VectorCopy (pusher->fields.server->origin, pushorig);
629 VectorCopy (pusher->fields.server->angles, pushang);
630 pushltime = pusher->fields.server->ltime;
632 // move the pusher to it's final position
634 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
635 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
636 pusher->fields.server->ltime += movetime;
637 SV_LinkEdict (pusher, false);
639 savesolid = pusher->fields.server->solid;
641 // see if any solid entities are inside the final position
644 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
645 for (e = 0;e < numcheckentities;e++)
647 check = checkentities[e];
648 if (check->fields.server->movetype == MOVETYPE_PUSH
649 || check->fields.server->movetype == MOVETYPE_NONE
650 || check->fields.server->movetype == MOVETYPE_FOLLOW
651 || check->fields.server->movetype == MOVETYPE_NOCLIP
652 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
655 // if the entity is standing on the pusher, it will definitely be moved
656 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
657 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
660 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
662 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
663 org2[0] = DotProduct (org, forward);
664 org2[1] = DotProduct (org, left);
665 org2[2] = DotProduct (org, up);
666 VectorSubtract (org2, org, move);
667 VectorAdd (move, move1, move);
670 VectorCopy (move1, move);
672 // remove the onground flag for non-players
673 if (check->fields.server->movetype != MOVETYPE_WALK)
674 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
676 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
677 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
678 sv.moved_edicts[num_moved++] = check;
680 // try moving the contacted entity
681 pusher->fields.server->solid = SOLID_NOT;
682 trace = SV_PushEntity (check, move);
683 // FIXME: turn players specially
684 check->fields.server->angles[1] += trace.fraction * moveangle[1];
685 pusher->fields.server->solid = savesolid; // was SOLID_BSP
687 // if it is still inside the pusher, block
688 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
690 // try moving the contacted entity a tiny bit further to account for precision errors
691 pusher->fields.server->solid = SOLID_NOT;
692 VectorScale(move, 0.1, move);
693 SV_PushEntity (check, move);
694 pusher->fields.server->solid = savesolid;
695 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
697 // still inside pusher, so it's really blocked
700 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
702 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
705 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
706 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
710 VectorCopy (pushorig, pusher->fields.server->origin);
711 VectorCopy (pushang, pusher->fields.server->angles);
712 pusher->fields.server->ltime = pushltime;
713 SV_LinkEdict (pusher, false);
715 // move back any entities we already moved
716 for (i = 0;i < num_moved;i++)
718 ed = sv.moved_edicts[i];
719 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
720 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
721 SV_LinkEdict (ed, false);
724 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
725 if (pusher->fields.server->blocked)
727 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
728 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
729 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
735 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
736 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
737 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
746 void SV_Physics_Pusher (prvm_edict_t *ent)
748 float thinktime, oldltime, movetime;
750 oldltime = ent->fields.server->ltime;
752 thinktime = ent->fields.server->nextthink;
753 if (thinktime < ent->fields.server->ltime + sv.frametime)
755 movetime = thinktime - ent->fields.server->ltime;
760 movetime = sv.frametime;
763 // advances ent->fields.server->ltime if not blocked
764 SV_PushMove (ent, movetime);
766 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
768 ent->fields.server->nextthink = 0;
769 prog->globals.server->time = sv.time;
770 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
771 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
772 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
778 ===============================================================================
782 ===============================================================================
789 This is a big hack to try and fix the rare case of getting stuck in the world
793 void SV_CheckStuck (prvm_edict_t *ent)
798 if (!SV_TestEntityPosition(ent))
800 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
804 VectorCopy (ent->fields.server->origin, org);
805 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
806 if (!SV_TestEntityPosition(ent))
808 Con_DPrint("Unstuck.\n");
809 SV_LinkEdict (ent, true);
813 for (z=0 ; z< 18 ; z++)
814 for (i=-1 ; i <= 1 ; i++)
815 for (j=-1 ; j <= 1 ; j++)
817 ent->fields.server->origin[0] = org[0] + i;
818 ent->fields.server->origin[1] = org[1] + j;
819 ent->fields.server->origin[2] = org[2] + z;
820 if (!SV_TestEntityPosition(ent))
822 Con_DPrint("Unstuck.\n");
823 SV_LinkEdict (ent, true);
828 VectorCopy (org, ent->fields.server->origin);
829 Con_DPrint("player is stuck.\n");
838 qboolean SV_CheckWater (prvm_edict_t *ent)
843 point[0] = ent->fields.server->origin[0];
844 point[1] = ent->fields.server->origin[1];
845 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
847 ent->fields.server->waterlevel = 0;
848 ent->fields.server->watertype = CONTENTS_EMPTY;
849 cont = SV_PointSuperContents(point);
850 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
852 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
853 ent->fields.server->waterlevel = 1;
854 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
855 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
857 ent->fields.server->waterlevel = 2;
858 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
859 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
860 ent->fields.server->waterlevel = 3;
864 return ent->fields.server->waterlevel > 1;
873 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
876 vec3_t forward, into, side;
878 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
879 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
881 // cut the tangential velocity
882 i = DotProduct (stepnormal, ent->fields.server->velocity);
883 VectorScale (stepnormal, i, into);
884 VectorSubtract (ent->fields.server->velocity, into, side);
885 ent->fields.server->velocity[0] = side[0] * (1 + d);
886 ent->fields.server->velocity[1] = side[1] * (1 + d);
891 =====================
894 Player has come to a dead stop, possibly due to the problem with limited
895 float precision at some angle joins in the BSP hull.
897 Try fixing by pushing one pixel in each direction.
899 This is a hack, but in the interest of good gameplay...
900 ======================
902 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
907 VectorCopy (ent->fields.server->origin, oldorg);
910 for (i=0 ; i<8 ; i++)
912 // try pushing a little in an axial direction
915 case 0: dir[0] = 2; dir[1] = 0; break;
916 case 1: dir[0] = 0; dir[1] = 2; break;
917 case 2: dir[0] = -2; dir[1] = 0; break;
918 case 3: dir[0] = 0; dir[1] = -2; break;
919 case 4: dir[0] = 2; dir[1] = 2; break;
920 case 5: dir[0] = -2; dir[1] = 2; break;
921 case 6: dir[0] = 2; dir[1] = -2; break;
922 case 7: dir[0] = -2; dir[1] = -2; break;
925 SV_PushEntity (ent, dir);
927 // retry the original move
928 ent->fields.server->velocity[0] = oldvel[0];
929 ent->fields.server->velocity[1] = oldvel[1];
930 ent->fields.server->velocity[2] = 0;
931 clip = SV_FlyMove (ent, 0.1, NULL);
933 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
934 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
936 Con_DPrint("TryUnstick - success.\n");
940 // go back to the original pos and try again
941 VectorCopy (oldorg, ent->fields.server->origin);
945 VectorClear (ent->fields.server->velocity);
946 Con_DPrint("TryUnstick - failure.\n");
951 =====================
955 ======================
957 void SV_WalkMove (prvm_edict_t *ent)
959 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
960 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
963 SV_CheckVelocity(ent);
965 // do a regular slide move unless it looks like you ran into a step
966 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
967 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
969 VectorCopy (ent->fields.server->origin, start_origin);
970 VectorCopy (ent->fields.server->velocity, start_velocity);
972 clip = SV_FlyMove (ent, sv.frametime, NULL);
974 SV_CheckVelocity(ent);
976 VectorCopy(ent->fields.server->origin, originalmove_origin);
977 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
978 originalmove_clip = clip;
979 originalmove_flags = (int)ent->fields.server->flags;
980 originalmove_groundentity = ent->fields.server->groundentity;
982 if ((int)ent->fields.server->flags & FL_WATERJUMP)
985 if (sv_nostep.integer)
988 // if move didn't block on a step, return
991 // if move was not trying to move into the step, return
992 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
995 if (ent->fields.server->movetype != MOVETYPE_FLY)
997 // return if gibbed by a trigger
998 if (ent->fields.server->movetype != MOVETYPE_WALK)
1001 // only step up while jumping if that is enabled
1002 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1003 if (!oldonground && ent->fields.server->waterlevel == 0)
1007 // try moving up and forward to go up a step
1008 // back to start pos
1009 VectorCopy (start_origin, ent->fields.server->origin);
1010 VectorCopy (start_velocity, ent->fields.server->velocity);
1013 VectorClear (upmove);
1014 upmove[2] = sv_stepheight.value;
1015 // FIXME: don't link?
1016 SV_PushEntity(ent, upmove);
1019 ent->fields.server->velocity[2] = 0;
1020 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1021 ent->fields.server->velocity[2] += start_velocity[2];
1023 SV_CheckVelocity(ent);
1025 // check for stuckness, possibly due to the limited precision of floats
1026 // in the clipping hulls
1028 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1029 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1031 //Con_Printf("wall\n");
1032 // stepping up didn't make any progress, revert to original move
1033 VectorCopy(originalmove_origin, ent->fields.server->origin);
1034 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1035 //clip = originalmove_clip;
1036 ent->fields.server->flags = originalmove_flags;
1037 ent->fields.server->groundentity = originalmove_groundentity;
1038 // now try to unstick if needed
1039 //clip = SV_TryUnstick (ent, oldvel);
1043 //Con_Printf("step - ");
1045 // extra friction based on view angle
1046 if (clip & 2 && sv_wallfriction.integer)
1047 SV_WallFriction (ent, stepnormal);
1049 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1050 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)))
1054 VectorClear (downmove);
1055 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1056 // FIXME: don't link?
1057 downtrace = SV_PushEntity (ent, downmove);
1059 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1061 // LordHavoc: disabled this check so you can walk on monsters/players
1062 //if (ent->fields.server->solid == SOLID_BSP)
1064 //Con_Printf("onground\n");
1065 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1066 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1071 //Con_Printf("slope\n");
1072 // if the push down didn't end up on good ground, use the move without
1073 // the step up. This happens near wall / slope combinations, and can
1074 // cause the player to hop up higher on a slope too steep to climb
1075 VectorCopy(originalmove_origin, ent->fields.server->origin);
1076 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1077 //clip = originalmove_clip;
1078 ent->fields.server->flags = originalmove_flags;
1079 ent->fields.server->groundentity = originalmove_groundentity;
1082 SV_CheckVelocity(ent);
1085 //============================================================================
1091 Entities that are "stuck" to another entity
1094 void SV_Physics_Follow (prvm_edict_t *ent)
1096 vec3_t vf, vr, vu, angles, v;
1100 if (!SV_RunThink (ent))
1103 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1104 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1105 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])
1107 // quick case for no rotation
1108 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1112 angles[0] = -ent->fields.server->punchangle[0];
1113 angles[1] = ent->fields.server->punchangle[1];
1114 angles[2] = ent->fields.server->punchangle[2];
1115 AngleVectors (angles, vf, vr, vu);
1116 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];
1117 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];
1118 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];
1119 angles[0] = -e->fields.server->angles[0];
1120 angles[1] = e->fields.server->angles[1];
1121 angles[2] = e->fields.server->angles[2];
1122 AngleVectors (angles, vf, vr, vu);
1123 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1124 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1125 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1127 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1128 SV_LinkEdict (ent, true);
1132 ==============================================================================
1136 ==============================================================================
1141 SV_CheckWaterTransition
1145 void SV_CheckWaterTransition (prvm_edict_t *ent)
1148 cont = SV_PointQ1Contents(ent->fields.server->origin);
1149 if (!ent->fields.server->watertype)
1151 // just spawned here
1152 ent->fields.server->watertype = cont;
1153 ent->fields.server->waterlevel = 1;
1157 // check if the entity crossed into or out of water
1158 if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1159 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1161 if (cont <= CONTENTS_WATER)
1163 ent->fields.server->watertype = cont;
1164 ent->fields.server->waterlevel = 1;
1168 ent->fields.server->watertype = CONTENTS_EMPTY;
1169 ent->fields.server->waterlevel = 0;
1177 Toss, bounce, and fly movement. When onground, do nothing.
1180 void SV_Physics_Toss (prvm_edict_t *ent)
1185 // don't stick to ground if onground and moving upward
1186 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1187 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1189 // if onground, return without moving
1190 if ((int)ent->fields.server->flags & FL_ONGROUND)
1192 if (ent->fields.server->groundentity == 0 || sv_gameplayfix_noairborncorpse.integer)
1194 // if ent was supported by a brush model on previous frame,
1195 // and groundentity is now freed, set groundentity to 0 (floating)
1196 if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1198 // leave it suspended in the air
1199 ent->fields.server->groundentity = 0;
1203 ent->priv.server->suspendedinairflag = false;
1205 SV_CheckVelocity (ent);
1208 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1209 SV_AddGravity (ent);
1212 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1215 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1216 trace = SV_PushEntity (ent, move);
1217 if (ent->priv.server->free)
1220 if (trace.fraction < 1)
1222 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1224 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1225 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1227 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1230 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1231 // LordHavoc: fixed grenades not bouncing when fired down a slope
1232 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1234 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1235 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1237 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1238 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1239 VectorClear (ent->fields.server->velocity);
1240 VectorClear (ent->fields.server->avelocity);
1243 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1247 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1249 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1250 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1251 VectorClear (ent->fields.server->velocity);
1252 VectorClear (ent->fields.server->avelocity);
1255 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1260 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1261 if (trace.plane.normal[2] > 0.7)
1263 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1264 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1265 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1266 ent->priv.server->suspendedinairflag = true;
1267 VectorClear (ent->fields.server->velocity);
1268 VectorClear (ent->fields.server->avelocity);
1271 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1275 // check for in water
1276 SV_CheckWaterTransition (ent);
1280 ===============================================================================
1284 ===============================================================================
1291 Monsters freefall when they don't have a ground entity, otherwise
1292 all movement is done with discrete steps.
1294 This is also used for objects that have become still on the ground, but
1295 will fall if the floor is pulled out from under them.
1298 void SV_Physics_Step (prvm_edict_t *ent)
1300 // don't stick to ground if onground and moving upward
1301 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1302 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1304 // freefall if not onground/fly/swim
1305 if (!((int)ent->fields.server->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1307 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1310 SV_CheckVelocity(ent);
1311 SV_FlyMove(ent, sv.frametime, NULL);
1312 SV_LinkEdict(ent, true);
1315 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1316 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1322 SV_CheckWaterTransition(ent);
1325 //============================================================================
1327 void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove)
1329 int i = ent - prog->edicts;
1330 if (i >= 1 && i <= svs.maxclients)
1332 // make sure the velocity is sane (not a NaN)
1333 SV_CheckVelocity(ent);
1334 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1335 if (SV_PlayerPhysicsQC)
1337 prog->globals.server->time = sv.time;
1338 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1339 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1343 // make sure the velocity is sane (not a NaN)
1344 SV_CheckVelocity(ent);
1345 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1346 // player_run/player_stand1 does not horribly malfunction if the
1347 // velocity becomes a number that is both == 0 and != 0
1348 // (sounds to me like NaN but to be absolutely safe...)
1349 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1350 VectorClear(ent->fields.server->velocity);
1351 // call standard client pre-think
1352 prog->globals.server->time = sv.time;
1353 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1354 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1355 SV_CheckVelocity (ent);
1358 // LordHavoc: merged client and normal entity physics
1359 switch ((int) ent->fields.server->movetype)
1362 case MOVETYPE_FAKEPUSH:
1363 SV_Physics_Pusher (ent);
1366 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1367 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1370 case MOVETYPE_FOLLOW:
1371 SV_Physics_Follow (ent);
1373 case MOVETYPE_NOCLIP:
1374 if (SV_RunThink(ent))
1377 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1378 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1380 // relink normal entities here, players always get relinked so don't relink twice
1381 if (!(i > 0 && i <= svs.maxclients))
1382 SV_LinkEdict(ent, false);
1385 SV_Physics_Step (ent);
1388 if (SV_RunThink (ent))
1390 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1391 SV_AddGravity (ent);
1392 SV_CheckStuck (ent);
1394 // relink normal entities here, players always get relinked so don't relink twice
1395 if (!(i > 0 && i <= svs.maxclients))
1396 SV_LinkEdict (ent, true);
1400 case MOVETYPE_BOUNCE:
1401 case MOVETYPE_BOUNCEMISSILE:
1402 case MOVETYPE_FLYMISSILE:
1404 if (SV_RunThink (ent) && runmove)
1405 SV_Physics_Toss (ent);
1408 if (SV_RunThink (ent) && runmove)
1410 if (i > 0 && i <= svs.maxclients)
1412 SV_CheckWater (ent);
1416 SV_Physics_Toss (ent);
1420 Con_Printf ("SV_Physics: bad movetype %i", (int)ent->fields.server->movetype);
1424 if (i >= 1 && i <= svs.maxclients)
1426 SV_CheckVelocity (ent);
1428 // call standard player post-think
1429 SV_LinkEdict (ent, true);
1431 SV_CheckVelocity (ent);
1433 prog->globals.server->time = sv.time;
1434 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1435 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1446 void SV_Physics (void)
1448 int i, newnum_edicts;
1450 qbyte runmove[MAX_EDICTS];
1452 // let the progs know that a new frame has started
1453 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1454 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1455 prog->globals.server->time = sv.time;
1456 prog->globals.server->frametime = sv.frametime;
1457 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1460 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1461 if ((runmove[i] = !ent->priv.server->free))
1462 newnum_edicts = i + 1;
1463 prog->num_edicts = max(svs.maxclients + 1, newnum_edicts);
1466 // treat each object in turn
1469 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1471 if (ent->priv.server->free)
1474 if (prog->globals.server->force_retouch)
1475 SV_LinkEdict (ent, true); // force retouch even for stationary
1477 if (i >= 1 && i <= svs.maxclients)
1479 host_client = svs.clients + i - 1;
1480 // don't do physics on disconnected clients, FrikBot relies on this
1481 if (!host_client->spawned)
1483 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1487 if (host_client->movesequence)
1488 continue; // return if running asynchronously
1490 else if (sv_freezenonclients.integer)
1493 SV_Physics_Entity(ent, runmove[i]);
1496 if (prog->globals.server->force_retouch > 0)
1497 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1499 // LordHavoc: endframe support
1502 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1503 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1504 prog->globals.server->time = sv.time;
1505 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1508 if (!sv_freezenonclients.integer)
1509 sv.time += sv.frametime;
1513 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1516 float gravity, savesolid;
1518 prvm_edict_t tempent, *tent;
1523 // copy the vars over
1524 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1525 // set up the temp entity to point to the copied vars
1527 tent->fields.server = &vars;
1529 savesolid = tossent->fields.server->solid;
1530 tossent->fields.server->solid = SOLID_NOT;
1532 // this has to fetch the field from the original edict, since our copy is truncated
1533 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1534 if (val != NULL && val->_float != 0)
1535 gravity = val->_float;
1538 gravity *= sv_gravity.value * 0.05;
1540 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1542 SV_CheckVelocity (tent);
1543 tent->fields.server->velocity[2] -= gravity;
1544 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1545 VectorScale (tent->fields.server->velocity, 0.05, move);
1546 VectorAdd (tent->fields.server->origin, move, end);
1547 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1548 VectorCopy (trace.endpos, tent->fields.server->origin);
1550 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1553 tossent->fields.server->solid = savesolid;
1554 trace.fraction = 0; // not relevant