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);
527 void SV_PushMove (prvm_edict_t *pusher, float movetime)
530 prvm_edict_t *check, *ed;
531 float savesolid, movetime2, pushltime;
532 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
534 int numcheckentities;
535 static prvm_edict_t *checkentities[MAX_EDICTS];
536 model_t *pushermodel;
539 if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
541 pusher->fields.server->ltime += movetime;
545 switch ((int) pusher->fields.server->solid)
547 // LordHavoc: valid pusher types
551 case SOLID_CORPSE: // LordHavoc: this would be weird...
553 // LordHavoc: no collisions
556 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
557 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
558 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
559 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
560 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
561 pusher->fields.server->ltime += movetime;
562 SV_LinkEdict (pusher, false);
565 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
568 index = (int) pusher->fields.server->modelindex;
569 if (index < 1 || index >= MAX_MODELS)
571 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
574 pushermodel = sv.models[index];
576 movetime2 = movetime;
577 VectorScale(pusher->fields.server->velocity, movetime2, move1);
578 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
579 if (moveangle[0] || moveangle[2])
581 for (i = 0;i < 3;i++)
585 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
586 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
590 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
591 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
595 else if (moveangle[1])
597 for (i = 0;i < 3;i++)
601 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
602 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
606 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
607 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
613 for (i = 0;i < 3;i++)
617 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
618 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
622 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
623 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
628 VectorNegate (moveangle, a);
629 AngleVectorsFLU (a, forward, left, up);
631 VectorCopy (pusher->fields.server->origin, pushorig);
632 VectorCopy (pusher->fields.server->angles, pushang);
633 pushltime = pusher->fields.server->ltime;
635 // move the pusher to it's final position
637 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
638 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
639 pusher->fields.server->ltime += movetime;
640 SV_LinkEdict (pusher, false);
642 savesolid = pusher->fields.server->solid;
644 // see if any solid entities are inside the final position
647 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
648 for (e = 0;e < numcheckentities;e++)
650 check = checkentities[e];
651 if (check->fields.server->movetype == MOVETYPE_PUSH
652 || check->fields.server->movetype == MOVETYPE_NONE
653 || check->fields.server->movetype == MOVETYPE_FOLLOW
654 || check->fields.server->movetype == MOVETYPE_NOCLIP
655 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
658 // if the entity is standing on the pusher, it will definitely be moved
659 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
660 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
663 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
665 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
666 org2[0] = DotProduct (org, forward);
667 org2[1] = DotProduct (org, left);
668 org2[2] = DotProduct (org, up);
669 VectorSubtract (org2, org, move);
670 VectorAdd (move, move1, move);
673 VectorCopy (move1, move);
675 // remove the onground flag for non-players
676 if (check->fields.server->movetype != MOVETYPE_WALK)
677 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
679 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
680 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
681 sv.moved_edicts[num_moved++] = check;
683 // try moving the contacted entity
684 pusher->fields.server->solid = SOLID_NOT;
685 trace = SV_PushEntity (check, move);
686 // FIXME: turn players specially
687 check->fields.server->angles[1] += trace.fraction * moveangle[1];
688 pusher->fields.server->solid = savesolid; // was SOLID_BSP
690 // if it is still inside the pusher, block
691 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
693 // try moving the contacted entity a tiny bit further to account for precision errors
694 pusher->fields.server->solid = SOLID_NOT;
695 VectorScale(move, 0.1, move);
696 SV_PushEntity (check, move);
697 pusher->fields.server->solid = savesolid;
698 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin).startsolid)
700 // still inside pusher, so it's really blocked
703 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
705 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
708 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
709 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
713 VectorCopy (pushorig, pusher->fields.server->origin);
714 VectorCopy (pushang, pusher->fields.server->angles);
715 pusher->fields.server->ltime = pushltime;
716 SV_LinkEdict (pusher, false);
718 // move back any entities we already moved
719 for (i = 0;i < num_moved;i++)
721 ed = sv.moved_edicts[i];
722 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
723 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
724 SV_LinkEdict (ed, false);
727 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
728 if (pusher->fields.server->blocked)
730 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
731 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
732 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
738 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
739 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
740 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
749 void SV_Physics_Pusher (prvm_edict_t *ent)
751 float thinktime, oldltime, movetime;
753 oldltime = ent->fields.server->ltime;
755 thinktime = ent->fields.server->nextthink;
756 if (thinktime < ent->fields.server->ltime + sv.frametime)
758 movetime = thinktime - ent->fields.server->ltime;
763 movetime = sv.frametime;
766 // advances ent->fields.server->ltime if not blocked
767 SV_PushMove (ent, movetime);
769 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
771 ent->fields.server->nextthink = 0;
772 prog->globals.server->time = sv.time;
773 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
774 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
775 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
781 ===============================================================================
785 ===============================================================================
792 This is a big hack to try and fix the rare case of getting stuck in the world
796 void SV_CheckStuck (prvm_edict_t *ent)
801 if (!SV_TestEntityPosition(ent))
803 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
807 VectorCopy (ent->fields.server->origin, org);
808 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
809 if (!SV_TestEntityPosition(ent))
811 Con_DPrint("Unstuck.\n");
812 SV_LinkEdict (ent, true);
816 for (z=0 ; z< 18 ; z++)
817 for (i=-1 ; i <= 1 ; i++)
818 for (j=-1 ; j <= 1 ; j++)
820 ent->fields.server->origin[0] = org[0] + i;
821 ent->fields.server->origin[1] = org[1] + j;
822 ent->fields.server->origin[2] = org[2] + z;
823 if (!SV_TestEntityPosition(ent))
825 Con_DPrint("Unstuck.\n");
826 SV_LinkEdict (ent, true);
831 VectorCopy (org, ent->fields.server->origin);
832 Con_DPrint("player is stuck.\n");
841 qboolean SV_CheckWater (prvm_edict_t *ent)
846 point[0] = ent->fields.server->origin[0];
847 point[1] = ent->fields.server->origin[1];
848 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
850 ent->fields.server->waterlevel = 0;
851 ent->fields.server->watertype = CONTENTS_EMPTY;
852 cont = SV_PointSuperContents(point);
853 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
855 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
856 ent->fields.server->waterlevel = 1;
857 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
858 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
860 ent->fields.server->waterlevel = 2;
861 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
862 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
863 ent->fields.server->waterlevel = 3;
867 return ent->fields.server->waterlevel > 1;
876 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
879 vec3_t forward, into, side;
881 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
882 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
884 // cut the tangential velocity
885 i = DotProduct (stepnormal, ent->fields.server->velocity);
886 VectorScale (stepnormal, i, into);
887 VectorSubtract (ent->fields.server->velocity, into, side);
888 ent->fields.server->velocity[0] = side[0] * (1 + d);
889 ent->fields.server->velocity[1] = side[1] * (1 + d);
894 =====================
897 Player has come to a dead stop, possibly due to the problem with limited
898 float precision at some angle joins in the BSP hull.
900 Try fixing by pushing one pixel in each direction.
902 This is a hack, but in the interest of good gameplay...
903 ======================
905 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
910 VectorCopy (ent->fields.server->origin, oldorg);
913 for (i=0 ; i<8 ; i++)
915 // try pushing a little in an axial direction
918 case 0: dir[0] = 2; dir[1] = 0; break;
919 case 1: dir[0] = 0; dir[1] = 2; break;
920 case 2: dir[0] = -2; dir[1] = 0; break;
921 case 3: dir[0] = 0; dir[1] = -2; break;
922 case 4: dir[0] = 2; dir[1] = 2; break;
923 case 5: dir[0] = -2; dir[1] = 2; break;
924 case 6: dir[0] = 2; dir[1] = -2; break;
925 case 7: dir[0] = -2; dir[1] = -2; break;
928 SV_PushEntity (ent, dir);
930 // retry the original move
931 ent->fields.server->velocity[0] = oldvel[0];
932 ent->fields.server->velocity[1] = oldvel[1];
933 ent->fields.server->velocity[2] = 0;
934 clip = SV_FlyMove (ent, 0.1, NULL);
936 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
937 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
939 Con_DPrint("TryUnstick - success.\n");
943 // go back to the original pos and try again
944 VectorCopy (oldorg, ent->fields.server->origin);
948 VectorClear (ent->fields.server->velocity);
949 Con_DPrint("TryUnstick - failure.\n");
954 =====================
958 ======================
960 void SV_WalkMove (prvm_edict_t *ent)
962 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
963 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
966 SV_CheckVelocity(ent);
968 // do a regular slide move unless it looks like you ran into a step
969 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
970 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
972 VectorCopy (ent->fields.server->origin, start_origin);
973 VectorCopy (ent->fields.server->velocity, start_velocity);
975 clip = SV_FlyMove (ent, sv.frametime, NULL);
977 SV_CheckVelocity(ent);
979 VectorCopy(ent->fields.server->origin, originalmove_origin);
980 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
981 originalmove_clip = clip;
982 originalmove_flags = (int)ent->fields.server->flags;
983 originalmove_groundentity = ent->fields.server->groundentity;
985 if ((int)ent->fields.server->flags & FL_WATERJUMP)
988 if (sv_nostep.integer)
991 // if move didn't block on a step, return
994 // if move was not trying to move into the step, return
995 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
998 if (ent->fields.server->movetype != MOVETYPE_FLY)
1000 // return if gibbed by a trigger
1001 if (ent->fields.server->movetype != MOVETYPE_WALK)
1004 // only step up while jumping if that is enabled
1005 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1006 if (!oldonground && ent->fields.server->waterlevel == 0)
1010 // try moving up and forward to go up a step
1011 // back to start pos
1012 VectorCopy (start_origin, ent->fields.server->origin);
1013 VectorCopy (start_velocity, ent->fields.server->velocity);
1016 VectorClear (upmove);
1017 upmove[2] = sv_stepheight.value;
1018 // FIXME: don't link?
1019 SV_PushEntity(ent, upmove);
1022 ent->fields.server->velocity[2] = 0;
1023 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1024 ent->fields.server->velocity[2] += start_velocity[2];
1026 SV_CheckVelocity(ent);
1028 // check for stuckness, possibly due to the limited precision of floats
1029 // in the clipping hulls
1031 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1032 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1034 //Con_Printf("wall\n");
1035 // stepping up didn't make any progress, revert to original move
1036 VectorCopy(originalmove_origin, ent->fields.server->origin);
1037 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1038 //clip = originalmove_clip;
1039 ent->fields.server->flags = originalmove_flags;
1040 ent->fields.server->groundentity = originalmove_groundentity;
1041 // now try to unstick if needed
1042 //clip = SV_TryUnstick (ent, oldvel);
1046 //Con_Printf("step - ");
1048 // extra friction based on view angle
1049 if (clip & 2 && sv_wallfriction.integer)
1050 SV_WallFriction (ent, stepnormal);
1052 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1053 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)))
1057 VectorClear (downmove);
1058 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1059 // FIXME: don't link?
1060 downtrace = SV_PushEntity (ent, downmove);
1062 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1064 // LordHavoc: disabled this check so you can walk on monsters/players
1065 //if (ent->fields.server->solid == SOLID_BSP)
1067 //Con_Printf("onground\n");
1068 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1069 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1074 //Con_Printf("slope\n");
1075 // if the push down didn't end up on good ground, use the move without
1076 // the step up. This happens near wall / slope combinations, and can
1077 // cause the player to hop up higher on a slope too steep to climb
1078 VectorCopy(originalmove_origin, ent->fields.server->origin);
1079 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1080 //clip = originalmove_clip;
1081 ent->fields.server->flags = originalmove_flags;
1082 ent->fields.server->groundentity = originalmove_groundentity;
1085 SV_CheckVelocity(ent);
1088 //============================================================================
1094 Entities that are "stuck" to another entity
1097 void SV_Physics_Follow (prvm_edict_t *ent)
1099 vec3_t vf, vr, vu, angles, v;
1103 if (!SV_RunThink (ent))
1106 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1107 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1108 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])
1110 // quick case for no rotation
1111 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1115 angles[0] = -ent->fields.server->punchangle[0];
1116 angles[1] = ent->fields.server->punchangle[1];
1117 angles[2] = ent->fields.server->punchangle[2];
1118 AngleVectors (angles, vf, vr, vu);
1119 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];
1120 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];
1121 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];
1122 angles[0] = -e->fields.server->angles[0];
1123 angles[1] = e->fields.server->angles[1];
1124 angles[2] = e->fields.server->angles[2];
1125 AngleVectors (angles, vf, vr, vu);
1126 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1127 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1128 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1130 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1131 SV_LinkEdict (ent, true);
1135 ==============================================================================
1139 ==============================================================================
1144 SV_CheckWaterTransition
1148 void SV_CheckWaterTransition (prvm_edict_t *ent)
1151 cont = SV_PointQ1Contents(ent->fields.server->origin);
1152 if (!ent->fields.server->watertype)
1154 // just spawned here
1155 ent->fields.server->watertype = cont;
1156 ent->fields.server->waterlevel = 1;
1160 // check if the entity crossed into or out of water
1161 if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1162 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1164 if (cont <= CONTENTS_WATER)
1166 ent->fields.server->watertype = cont;
1167 ent->fields.server->waterlevel = 1;
1171 ent->fields.server->watertype = CONTENTS_EMPTY;
1172 ent->fields.server->waterlevel = 0;
1180 Toss, bounce, and fly movement. When onground, do nothing.
1183 void SV_Physics_Toss (prvm_edict_t *ent)
1188 // don't stick to ground if onground and moving upward
1189 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1190 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1192 // if onground, return without moving
1193 if ((int)ent->fields.server->flags & FL_ONGROUND)
1195 if (ent->fields.server->groundentity == 0 || sv_gameplayfix_noairborncorpse.integer)
1197 // if ent was supported by a brush model on previous frame,
1198 // and groundentity is now freed, set groundentity to 0 (floating)
1199 if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1201 // leave it suspended in the air
1202 ent->fields.server->groundentity = 0;
1206 ent->priv.server->suspendedinairflag = false;
1208 SV_CheckVelocity (ent);
1211 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1212 SV_AddGravity (ent);
1215 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1218 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1219 trace = SV_PushEntity (ent, move);
1220 if (ent->priv.server->free)
1223 if (trace.fraction < 1)
1225 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1227 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1228 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1230 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1233 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1234 // LordHavoc: fixed grenades not bouncing when fired down a slope
1235 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1237 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1238 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1240 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1241 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1242 VectorClear (ent->fields.server->velocity);
1243 VectorClear (ent->fields.server->avelocity);
1246 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1250 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1252 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1253 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1254 VectorClear (ent->fields.server->velocity);
1255 VectorClear (ent->fields.server->avelocity);
1258 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1263 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1264 if (trace.plane.normal[2] > 0.7)
1266 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1267 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1268 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1269 ent->priv.server->suspendedinairflag = true;
1270 VectorClear (ent->fields.server->velocity);
1271 VectorClear (ent->fields.server->avelocity);
1274 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1278 // check for in water
1279 SV_CheckWaterTransition (ent);
1283 ===============================================================================
1287 ===============================================================================
1294 Monsters freefall when they don't have a ground entity, otherwise
1295 all movement is done with discrete steps.
1297 This is also used for objects that have become still on the ground, but
1298 will fall if the floor is pulled out from under them.
1301 void SV_Physics_Step (prvm_edict_t *ent)
1303 // don't stick to ground if onground and moving upward
1304 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1305 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1307 // freefall if not onground/fly/swim
1308 if (!((int)ent->fields.server->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1310 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1313 SV_CheckVelocity(ent);
1314 SV_FlyMove(ent, sv.frametime, NULL);
1315 SV_LinkEdict(ent, true);
1318 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1319 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1325 SV_CheckWaterTransition(ent);
1328 //============================================================================
1330 void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove)
1332 int i = ent - prog->edicts;
1333 if (i >= 1 && i <= svs.maxclients)
1335 // make sure the velocity is sane (not a NaN)
1336 SV_CheckVelocity(ent);
1337 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1338 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1340 prog->globals.server->time = sv.time;
1341 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1342 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1346 // make sure the velocity is sane (not a NaN)
1347 SV_CheckVelocity(ent);
1348 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1349 // player_run/player_stand1 does not horribly malfunction if the
1350 // velocity becomes a number that is both == 0 and != 0
1351 // (sounds to me like NaN but to be absolutely safe...)
1352 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1353 VectorClear(ent->fields.server->velocity);
1354 // call standard client pre-think
1355 prog->globals.server->time = sv.time;
1356 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1357 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1358 SV_CheckVelocity (ent);
1361 // LordHavoc: merged client and normal entity physics
1362 switch ((int) ent->fields.server->movetype)
1365 case MOVETYPE_FAKEPUSH:
1366 SV_Physics_Pusher (ent);
1369 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1370 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1373 case MOVETYPE_FOLLOW:
1374 SV_Physics_Follow (ent);
1376 case MOVETYPE_NOCLIP:
1377 if (SV_RunThink(ent))
1380 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1381 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1383 // relink normal entities here, players always get relinked so don't relink twice
1384 if (!(i > 0 && i <= svs.maxclients))
1385 SV_LinkEdict(ent, false);
1388 SV_Physics_Step (ent);
1391 if (SV_RunThink (ent))
1393 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1394 SV_AddGravity (ent);
1395 SV_CheckStuck (ent);
1397 // relink normal entities here, players always get relinked so don't relink twice
1398 if (!(i > 0 && i <= svs.maxclients))
1399 SV_LinkEdict (ent, true);
1403 case MOVETYPE_BOUNCE:
1404 case MOVETYPE_BOUNCEMISSILE:
1405 case MOVETYPE_FLYMISSILE:
1407 if (SV_RunThink (ent) && runmove)
1408 SV_Physics_Toss (ent);
1411 if (SV_RunThink (ent) && runmove)
1413 if (i > 0 && i <= svs.maxclients)
1415 SV_CheckWater (ent);
1419 SV_Physics_Toss (ent);
1423 Con_Printf ("SV_Physics: bad movetype %i", (int)ent->fields.server->movetype);
1427 if (i >= 1 && i <= svs.maxclients)
1429 SV_CheckVelocity (ent);
1431 // call standard player post-think
1432 SV_LinkEdict (ent, true);
1434 SV_CheckVelocity (ent);
1436 prog->globals.server->time = sv.time;
1437 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1438 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1449 void SV_Physics (void)
1451 int i, newnum_edicts;
1453 qbyte runmove[MAX_EDICTS];
1455 // let the progs know that a new frame has started
1456 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1457 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1458 prog->globals.server->time = sv.time;
1459 prog->globals.server->frametime = sv.frametime;
1460 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1463 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1464 if ((runmove[i] = !ent->priv.server->free))
1465 newnum_edicts = i + 1;
1466 prog->num_edicts = max(svs.maxclients + 1, newnum_edicts);
1469 // treat each object in turn
1472 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1474 if (ent->priv.server->free)
1477 if (prog->globals.server->force_retouch)
1478 SV_LinkEdict (ent, true); // force retouch even for stationary
1480 if (i >= 1 && i <= svs.maxclients)
1482 host_client = svs.clients + i - 1;
1483 // don't do physics on disconnected clients, FrikBot relies on this
1484 if (!host_client->spawned)
1486 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1490 if (host_client->movesequence)
1491 continue; // return if running asynchronously
1493 else if (sv_freezenonclients.integer)
1496 SV_Physics_Entity(ent, runmove[i]);
1499 if (prog->globals.server->force_retouch > 0)
1500 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1502 // LordHavoc: endframe support
1505 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1506 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1507 prog->globals.server->time = sv.time;
1508 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1511 if (!sv_freezenonclients.integer)
1512 sv.time += sv.frametime;
1516 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1519 float gravity, savesolid;
1521 prvm_edict_t tempent, *tent;
1526 // copy the vars over
1527 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1528 // set up the temp entity to point to the copied vars
1530 tent->fields.server = &vars;
1532 savesolid = tossent->fields.server->solid;
1533 tossent->fields.server->solid = SOLID_NOT;
1535 // this has to fetch the field from the original edict, since our copy is truncated
1536 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1537 if (val != NULL && val->_float != 0)
1538 gravity = val->_float;
1541 gravity *= sv_gravity.value * 0.05;
1543 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1545 SV_CheckVelocity (tent);
1546 tent->fields.server->velocity[2] -= gravity;
1547 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1548 VectorScale (tent->fields.server->velocity, 0.05, move);
1549 VectorAdd (tent->fields.server->origin, move, end);
1550 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1551 VectorCopy (trace.endpos, tent->fields.server->origin);
1553 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1556 tossent->fields.server->solid = savesolid;
1557 trace.fraction = 0; // not relevant