2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
29 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38 solid_edge items only clip against bsp models.
42 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4"};
43 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100"};
44 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800"};
45 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000"};
46 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0"};
47 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18"};
48 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "1"};
49 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1"};
50 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0"};
51 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0"};
52 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1"};
54 #define MOVE_EPSILON 0.01
56 void SV_Physics_Toss (prvm_edict_t *ent);
58 void SV_Phys_Init (void)
60 Cvar_RegisterVariable(&sv_stepheight);
61 Cvar_RegisterVariable(&sv_jumpstep);
62 Cvar_RegisterVariable(&sv_wallfriction);
63 Cvar_RegisterVariable(&sv_newflymove);
64 Cvar_RegisterVariable(&sv_freezenonclients);
66 Cvar_RegisterVariable(&sv_playerphysicsqc);
74 void SV_CheckAllEnts (void)
79 // see if any solid entities are inside the final position
80 check = PRVM_NEXT_EDICT(prog->edicts);
81 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
83 if (check->priv.server->free)
85 if (check->fields.server->movetype == MOVETYPE_PUSH
86 || check->fields.server->movetype == MOVETYPE_NONE
87 || check->fields.server->movetype == MOVETYPE_FOLLOW
88 || check->fields.server->movetype == MOVETYPE_NOCLIP)
91 if (SV_TestEntityPosition (check))
92 Con_Print("entity in invalid position\n");
101 void SV_CheckVelocity (prvm_edict_t *ent)
109 for (i=0 ; i<3 ; i++)
111 if (IS_NAN(ent->fields.server->velocity[i]))
113 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
114 ent->fields.server->velocity[i] = 0;
116 if (IS_NAN(ent->fields.server->origin[i]))
118 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
119 ent->fields.server->origin[i] = 0;
123 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
124 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
125 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
127 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
128 ent->fields.server->velocity[0] *= wishspeed;
129 ent->fields.server->velocity[1] *= wishspeed;
130 ent->fields.server->velocity[2] *= wishspeed;
138 Runs thinking code if time. There is some play in the exact time the think
139 function will be called, because it is called before any movement is done
140 in a frame. Not used for pushmove objects, because they must be exact.
141 Returns false if the entity removed itself.
144 qboolean SV_RunThink (prvm_edict_t *ent)
148 thinktime = ent->fields.server->nextthink;
149 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
152 // don't let things stay in the past.
153 // it is possible to start that way by a trigger with a local time.
154 if (thinktime < sv.time)
157 ent->fields.server->nextthink = 0;
158 prog->globals.server->time = thinktime;
159 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
160 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
161 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
162 return !ent->priv.server->free;
169 Two entities have touched, so run their touch functions
172 void SV_Impact (prvm_edict_t *e1, prvm_edict_t *e2)
174 int old_self, old_other;
176 old_self = prog->globals.server->self;
177 old_other = prog->globals.server->other;
179 prog->globals.server->time = sv.time;
180 if (e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
182 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
183 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
184 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
187 if (e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
189 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
190 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
191 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
194 prog->globals.server->self = old_self;
195 prog->globals.server->other = old_other;
203 Slide off of the impacting object
204 returns the blocked flags (1 = floor, 2 = step / wall)
207 #define STOP_EPSILON 0.1
208 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
213 backoff = -DotProduct (in, normal) * overbounce;
214 VectorMA(in, backoff, normal, out);
216 for (i = 0;i < 3;i++)
217 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
226 The basic solid body movement clip that slides along multiple planes
227 Returns the clipflags if the velocity was modified (hit something solid)
231 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
234 // LordHavoc: increased from 5 to 32
235 #define MAX_CLIP_PLANES 32
236 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
238 int blocked, bumpcount;
239 int i, j, impact, numplanes;
241 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
244 VectorCopy(ent->fields.server->velocity, original_velocity);
245 VectorCopy(ent->fields.server->velocity, primal_velocity);
248 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
250 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
253 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
254 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
256 //if (trace.fraction < 0.002)
261 VectorCopy(ent->fields.server->origin, start);
262 start[2] += 3;//0.03125;
263 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
264 end[2] += 3;//0.03125;
265 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
266 if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
268 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
274 for (i = 0;i < numplanes;i++)
276 VectorCopy(ent->fields.server->origin, start);
277 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
278 VectorMA(start, 3, planes[i], start);
279 VectorMA(end, 3, planes[i], end);
280 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
281 if (trace.fraction < testtrace.fraction)
284 VectorCopy(start, ent->fields.server->origin);
289 // VectorAdd(ent->fields.server->origin, planes[j], start);
295 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
296 if (trace.fraction < 1)
297 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
302 if (trace.startsolid)
304 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
305 // entity is trapped in another solid
306 VectorClear(ent->fields.server->velocity);
311 // break if it moved the entire distance
312 if (trace.fraction == 1)
314 VectorCopy(trace.endpos, ent->fields.server->origin);
320 Con_Printf ("SV_FlyMove: !trace.ent");
321 trace.ent = prog->edicts;
324 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
328 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
332 if (trace.plane.normal[2])
334 if (trace.plane.normal[2] > 0.7)
338 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
339 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
346 // save the trace for player extrafriction
348 VectorCopy(trace.plane.normal, stepnormal);
351 if (trace.fraction >= 0.001)
353 // actually covered some distance
354 VectorCopy(trace.endpos, ent->fields.server->origin);
355 VectorCopy(ent->fields.server->velocity, original_velocity);
359 // run the impact function
362 SV_Impact(ent, (prvm_edict_t *)trace.ent);
364 // break if removed by the impact function
365 if (ent->priv.server->free)
369 time_left *= 1 - trace.fraction;
371 // clipped to another plane
372 if (numplanes >= MAX_CLIP_PLANES)
374 // this shouldn't really happen
375 VectorClear(ent->fields.server->velocity);
381 for (i = 0;i < numplanes;i++)
382 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
386 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
391 VectorCopy(trace.plane.normal, planes[numplanes]);
394 if (sv_newflymove.integer)
395 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
398 // modify original_velocity so it parallels all of the clip planes
399 for (i = 0;i < numplanes;i++)
401 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
402 for (j = 0;j < numplanes;j++)
407 if (DotProduct(new_velocity, planes[j]) < 0)
417 // go along this plane
418 VectorCopy(new_velocity, ent->fields.server->velocity);
422 // go along the crease
425 VectorClear(ent->fields.server->velocity);
429 CrossProduct(planes[0], planes[1], dir);
430 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
431 VectorNormalize(dir);
432 d = DotProduct(dir, ent->fields.server->velocity);
433 VectorScale(dir, d, ent->fields.server->velocity);
437 // if current velocity is against the original velocity,
438 // stop dead to avoid tiny occilations in sloping corners
439 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
441 VectorClear(ent->fields.server->velocity);
446 //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2]);
449 if ((blocked & 1) == 0 && bumpcount > 1)
451 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
452 // flag ONGROUND if there's ground under it
453 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
465 void SV_AddGravity (prvm_edict_t *ent)
470 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
471 if (val!=0 && val->_float)
472 ent_gravity = val->_float;
475 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
480 ===============================================================================
484 ===============================================================================
491 Does not change the entities velocity at all
494 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
500 VectorAdd (ent->fields.server->origin, push, end);
502 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
504 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
505 type = MOVE_NOMONSTERS; // only clip against bmodels
509 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
511 VectorCopy (trace.endpos, ent->fields.server->origin);
512 SV_LinkEdict (ent, true);
514 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
515 SV_Impact (ent, (prvm_edict_t *)trace.ent);
526 void SV_PushMove (prvm_edict_t *pusher, float movetime)
529 float savesolid, movetime2, pushltime;
530 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
532 int numcheckentities;
533 static prvm_edict_t *checkentities[MAX_EDICTS];
534 model_t *pushermodel;
537 if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
539 pusher->fields.server->ltime += movetime;
543 switch ((int) pusher->fields.server->solid)
545 // LordHavoc: valid pusher types
549 case SOLID_CORPSE: // LordHavoc: this would be weird...
551 // LordHavoc: no collisions
554 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
555 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
556 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
557 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
558 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
559 pusher->fields.server->ltime += movetime;
560 SV_LinkEdict (pusher, false);
563 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
566 index = (int) pusher->fields.server->modelindex;
567 if (index < 1 || index >= MAX_MODELS)
569 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
572 pushermodel = sv.models[index];
574 movetime2 = movetime;
575 VectorScale(pusher->fields.server->velocity, movetime2, move1);
576 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
577 if (moveangle[0] || moveangle[2])
579 for (i = 0;i < 3;i++)
583 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
584 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
588 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
589 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
593 else if (moveangle[1])
595 for (i = 0;i < 3;i++)
599 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
600 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
604 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
605 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
611 for (i = 0;i < 3;i++)
615 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
616 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
620 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
621 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
626 VectorNegate (moveangle, a);
627 AngleVectorsFLU (a, forward, left, up);
629 VectorCopy (pusher->fields.server->origin, pushorig);
630 VectorCopy (pusher->fields.server->angles, pushang);
631 pushltime = pusher->fields.server->ltime;
633 // move the pusher to its final position
635 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
636 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
637 pusher->fields.server->ltime += movetime;
638 SV_LinkEdict (pusher, false);
640 savesolid = pusher->fields.server->solid;
642 // see if any solid entities are inside the final position
645 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
646 for (e = 0;e < numcheckentities;e++)
648 prvm_edict_t *check = checkentities[e];
649 if (check->fields.server->movetype == MOVETYPE_NONE
650 || check->fields.server->movetype == MOVETYPE_PUSH
651 || check->fields.server->movetype == MOVETYPE_FOLLOW
652 || check->fields.server->movetype == MOVETYPE_NOCLIP
653 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
656 // if the entity is standing on the pusher, it will definitely be moved
657 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
659 // if the entity is not inside the pusher's final position, leave it alone
660 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
662 // remove the onground flag for non-players
663 if (check->fields.server->movetype != MOVETYPE_WALK)
664 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
668 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
671 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
672 org2[0] = DotProduct (org, forward);
673 org2[1] = DotProduct (org, left);
674 org2[2] = DotProduct (org, up);
675 VectorSubtract (org2, org, move);
676 VectorAdd (move, move1, move);
679 VectorCopy (move1, move);
681 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
682 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
683 sv.moved_edicts[num_moved++] = check;
685 // try moving the contacted entity
686 pusher->fields.server->solid = SOLID_NOT;
687 trace = SV_PushEntity (check, move);
688 // FIXME: turn players specially
689 check->fields.server->angles[1] += trace.fraction * moveangle[1];
690 pusher->fields.server->solid = savesolid; // was SOLID_BSP
692 // if it is still inside the pusher, block
693 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
695 // try moving the contacted entity a tiny bit further to account for precision errors
697 pusher->fields.server->solid = SOLID_NOT;
698 VectorScale(move, 1.1, move2);
699 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
700 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
701 SV_PushEntity (check, move2);
702 pusher->fields.server->solid = savesolid;
703 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
705 // try moving the contacted entity a tiny bit less to account for precision errors
706 pusher->fields.server->solid = SOLID_NOT;
707 VectorScale(move, 0.9, move2);
708 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
709 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
710 SV_PushEntity (check, move2);
711 pusher->fields.server->solid = savesolid;
712 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
714 // still inside pusher, so it's really blocked
717 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
719 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
722 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
723 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
727 VectorCopy (pushorig, pusher->fields.server->origin);
728 VectorCopy (pushang, pusher->fields.server->angles);
729 pusher->fields.server->ltime = pushltime;
730 SV_LinkEdict (pusher, false);
732 // move back any entities we already moved
733 for (i = 0;i < num_moved;i++)
735 prvm_edict_t *ed = sv.moved_edicts[i];
736 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
737 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
738 SV_LinkEdict (ed, false);
741 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
742 if (pusher->fields.server->blocked)
744 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
745 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
746 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
753 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
754 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
755 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
764 void SV_Physics_Pusher (prvm_edict_t *ent)
766 float thinktime, oldltime, movetime;
768 oldltime = ent->fields.server->ltime;
770 thinktime = ent->fields.server->nextthink;
771 if (thinktime < ent->fields.server->ltime + sv.frametime)
773 movetime = thinktime - ent->fields.server->ltime;
778 movetime = sv.frametime;
781 // advances ent->fields.server->ltime if not blocked
782 SV_PushMove (ent, movetime);
784 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
786 ent->fields.server->nextthink = 0;
787 prog->globals.server->time = sv.time;
788 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
789 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
790 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
796 ===============================================================================
800 ===============================================================================
807 This is a big hack to try and fix the rare case of getting stuck in the world
811 void SV_CheckStuck (prvm_edict_t *ent)
816 if (!SV_TestEntityPosition(ent))
818 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
822 VectorCopy (ent->fields.server->origin, org);
823 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
824 if (!SV_TestEntityPosition(ent))
826 Con_DPrint("Unstuck.\n");
827 SV_LinkEdict (ent, true);
831 for (z=0 ; z< 18 ; z++)
832 for (i=-1 ; i <= 1 ; i++)
833 for (j=-1 ; j <= 1 ; j++)
835 ent->fields.server->origin[0] = org[0] + i;
836 ent->fields.server->origin[1] = org[1] + j;
837 ent->fields.server->origin[2] = org[2] + z;
838 if (!SV_TestEntityPosition(ent))
840 Con_DPrint("Unstuck.\n");
841 SV_LinkEdict (ent, true);
846 VectorCopy (org, ent->fields.server->origin);
847 Con_DPrint("player is stuck.\n");
856 qboolean SV_CheckWater (prvm_edict_t *ent)
861 point[0] = ent->fields.server->origin[0];
862 point[1] = ent->fields.server->origin[1];
863 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
865 ent->fields.server->waterlevel = 0;
866 ent->fields.server->watertype = CONTENTS_EMPTY;
867 cont = SV_PointSuperContents(point);
868 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
870 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
871 ent->fields.server->waterlevel = 1;
872 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
873 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
875 ent->fields.server->waterlevel = 2;
876 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
877 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
878 ent->fields.server->waterlevel = 3;
882 return ent->fields.server->waterlevel > 1;
891 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
894 vec3_t forward, into, side;
896 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
897 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
899 // cut the tangential velocity
900 i = DotProduct (stepnormal, ent->fields.server->velocity);
901 VectorScale (stepnormal, i, into);
902 VectorSubtract (ent->fields.server->velocity, into, side);
903 ent->fields.server->velocity[0] = side[0] * (1 + d);
904 ent->fields.server->velocity[1] = side[1] * (1 + d);
909 =====================
912 Player has come to a dead stop, possibly due to the problem with limited
913 float precision at some angle joins in the BSP hull.
915 Try fixing by pushing one pixel in each direction.
917 This is a hack, but in the interest of good gameplay...
918 ======================
920 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
925 VectorCopy (ent->fields.server->origin, oldorg);
928 for (i=0 ; i<8 ; i++)
930 // try pushing a little in an axial direction
933 case 0: dir[0] = 2; dir[1] = 0; break;
934 case 1: dir[0] = 0; dir[1] = 2; break;
935 case 2: dir[0] = -2; dir[1] = 0; break;
936 case 3: dir[0] = 0; dir[1] = -2; break;
937 case 4: dir[0] = 2; dir[1] = 2; break;
938 case 5: dir[0] = -2; dir[1] = 2; break;
939 case 6: dir[0] = 2; dir[1] = -2; break;
940 case 7: dir[0] = -2; dir[1] = -2; break;
943 SV_PushEntity (ent, dir);
945 // retry the original move
946 ent->fields.server->velocity[0] = oldvel[0];
947 ent->fields.server->velocity[1] = oldvel[1];
948 ent->fields.server->velocity[2] = 0;
949 clip = SV_FlyMove (ent, 0.1, NULL);
951 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
952 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
954 Con_DPrint("TryUnstick - success.\n");
958 // go back to the original pos and try again
959 VectorCopy (oldorg, ent->fields.server->origin);
963 VectorClear (ent->fields.server->velocity);
964 Con_DPrint("TryUnstick - failure.\n");
969 =====================
973 ======================
975 void SV_WalkMove (prvm_edict_t *ent)
977 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
978 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
981 SV_CheckVelocity(ent);
983 // do a regular slide move unless it looks like you ran into a step
984 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
985 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
987 VectorCopy (ent->fields.server->origin, start_origin);
988 VectorCopy (ent->fields.server->velocity, start_velocity);
990 clip = SV_FlyMove (ent, sv.frametime, NULL);
992 SV_CheckVelocity(ent);
994 VectorCopy(ent->fields.server->origin, originalmove_origin);
995 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
996 originalmove_clip = clip;
997 originalmove_flags = (int)ent->fields.server->flags;
998 originalmove_groundentity = ent->fields.server->groundentity;
1000 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1003 if (sv_nostep.integer)
1006 // if move didn't block on a step, return
1009 // if move was not trying to move into the step, return
1010 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1013 if (ent->fields.server->movetype != MOVETYPE_FLY)
1015 // return if gibbed by a trigger
1016 if (ent->fields.server->movetype != MOVETYPE_WALK)
1019 // only step up while jumping if that is enabled
1020 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1021 if (!oldonground && ent->fields.server->waterlevel == 0)
1025 // try moving up and forward to go up a step
1026 // back to start pos
1027 VectorCopy (start_origin, ent->fields.server->origin);
1028 VectorCopy (start_velocity, ent->fields.server->velocity);
1031 VectorClear (upmove);
1032 upmove[2] = sv_stepheight.value;
1033 // FIXME: don't link?
1034 SV_PushEntity(ent, upmove);
1037 ent->fields.server->velocity[2] = 0;
1038 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1039 ent->fields.server->velocity[2] += start_velocity[2];
1041 SV_CheckVelocity(ent);
1043 // check for stuckness, possibly due to the limited precision of floats
1044 // in the clipping hulls
1046 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1047 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1049 //Con_Printf("wall\n");
1050 // stepping up didn't make any progress, revert to original move
1051 VectorCopy(originalmove_origin, ent->fields.server->origin);
1052 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1053 //clip = originalmove_clip;
1054 ent->fields.server->flags = originalmove_flags;
1055 ent->fields.server->groundentity = originalmove_groundentity;
1056 // now try to unstick if needed
1057 //clip = SV_TryUnstick (ent, oldvel);
1061 //Con_Printf("step - ");
1063 // extra friction based on view angle
1064 if (clip & 2 && sv_wallfriction.integer)
1065 SV_WallFriction (ent, stepnormal);
1067 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1068 else if (!(sv_gameplayfix_stepdown.integer && ent->fields.server->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->fields.server->flags & FL_ONGROUND)))
1072 VectorClear (downmove);
1073 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1074 // FIXME: don't link?
1075 downtrace = SV_PushEntity (ent, downmove);
1077 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1079 // LordHavoc: disabled this check so you can walk on monsters/players
1080 //if (ent->fields.server->solid == SOLID_BSP)
1082 //Con_Printf("onground\n");
1083 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1084 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1089 //Con_Printf("slope\n");
1090 // if the push down didn't end up on good ground, use the move without
1091 // the step up. This happens near wall / slope combinations, and can
1092 // cause the player to hop up higher on a slope too steep to climb
1093 VectorCopy(originalmove_origin, ent->fields.server->origin);
1094 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1095 //clip = originalmove_clip;
1096 ent->fields.server->flags = originalmove_flags;
1097 ent->fields.server->groundentity = originalmove_groundentity;
1100 SV_CheckVelocity(ent);
1103 //============================================================================
1109 Entities that are "stuck" to another entity
1112 void SV_Physics_Follow (prvm_edict_t *ent)
1114 vec3_t vf, vr, vu, angles, v;
1118 if (!SV_RunThink (ent))
1121 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1122 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1123 if (e->fields.server->angles[0] == ent->fields.server->punchangle[0] && e->fields.server->angles[1] == ent->fields.server->punchangle[1] && e->fields.server->angles[2] == ent->fields.server->punchangle[2])
1125 // quick case for no rotation
1126 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1130 angles[0] = -ent->fields.server->punchangle[0];
1131 angles[1] = ent->fields.server->punchangle[1];
1132 angles[2] = ent->fields.server->punchangle[2];
1133 AngleVectors (angles, vf, vr, vu);
1134 v[0] = ent->fields.server->view_ofs[0] * vf[0] + ent->fields.server->view_ofs[1] * vr[0] + ent->fields.server->view_ofs[2] * vu[0];
1135 v[1] = ent->fields.server->view_ofs[0] * vf[1] + ent->fields.server->view_ofs[1] * vr[1] + ent->fields.server->view_ofs[2] * vu[1];
1136 v[2] = ent->fields.server->view_ofs[0] * vf[2] + ent->fields.server->view_ofs[1] * vr[2] + ent->fields.server->view_ofs[2] * vu[2];
1137 angles[0] = -e->fields.server->angles[0];
1138 angles[1] = e->fields.server->angles[1];
1139 angles[2] = e->fields.server->angles[2];
1140 AngleVectors (angles, vf, vr, vu);
1141 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1142 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1143 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1145 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1146 SV_LinkEdict (ent, true);
1150 ==============================================================================
1154 ==============================================================================
1159 SV_CheckWaterTransition
1163 void SV_CheckWaterTransition (prvm_edict_t *ent)
1166 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1167 if (!ent->fields.server->watertype)
1169 // just spawned here
1170 ent->fields.server->watertype = cont;
1171 ent->fields.server->waterlevel = 1;
1175 // check if the entity crossed into or out of water
1176 if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1177 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1179 if (cont <= CONTENTS_WATER)
1181 ent->fields.server->watertype = cont;
1182 ent->fields.server->waterlevel = 1;
1186 ent->fields.server->watertype = CONTENTS_EMPTY;
1187 ent->fields.server->waterlevel = 0;
1195 Toss, bounce, and fly movement. When onground, do nothing.
1198 void SV_Physics_Toss (prvm_edict_t *ent)
1203 // if onground, return without moving
1204 if ((int)ent->fields.server->flags & FL_ONGROUND)
1206 // don't stick to ground if onground and moving upward
1207 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1208 ent->fields.server->flags -= FL_ONGROUND;
1211 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1212 if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1214 // if ent was supported by a brush model on previous frame,
1215 // and groundentity is now freed, set groundentity to 0 (floating)
1216 if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1218 // leave it suspended in the air
1219 ent->fields.server->groundentity = 0;
1224 ent->priv.server->suspendedinairflag = false;
1226 SV_CheckVelocity (ent);
1229 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1230 SV_AddGravity (ent);
1233 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1236 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1237 trace = SV_PushEntity (ent, move);
1238 if (ent->priv.server->free)
1241 if (trace.fraction < 1)
1243 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1245 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1246 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1248 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1251 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1252 // LordHavoc: fixed grenades not bouncing when fired down a slope
1253 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1255 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1256 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1258 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1259 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1260 VectorClear (ent->fields.server->velocity);
1261 VectorClear (ent->fields.server->avelocity);
1264 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1268 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1270 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1271 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1272 VectorClear (ent->fields.server->velocity);
1273 VectorClear (ent->fields.server->avelocity);
1276 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1281 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1282 if (trace.plane.normal[2] > 0.7)
1284 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1285 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1286 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1287 ent->priv.server->suspendedinairflag = true;
1288 VectorClear (ent->fields.server->velocity);
1289 VectorClear (ent->fields.server->avelocity);
1292 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1296 // check for in water
1297 SV_CheckWaterTransition (ent);
1301 ===============================================================================
1305 ===============================================================================
1312 Monsters freefall when they don't have a ground entity, otherwise
1313 all movement is done with discrete steps.
1315 This is also used for objects that have become still on the ground, but
1316 will fall if the floor is pulled out from under them.
1319 void SV_Physics_Step (prvm_edict_t *ent)
1321 int flags = (int)ent->fields.server->flags;
1322 // don't fall at all if fly/swim
1323 if (!(flags & (FL_FLY | FL_SWIM)))
1325 if (flags & FL_ONGROUND)
1327 // freefall if onground and moving upward
1328 // freefall if not standing on a world surface (it may be a lift)
1329 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1330 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1332 ent->fields.server->flags -= FL_ONGROUND;
1334 SV_CheckVelocity(ent);
1335 SV_FlyMove(ent, sv.frametime, NULL);
1336 SV_LinkEdict(ent, true);
1341 // freefall if not onground
1342 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1345 SV_CheckVelocity(ent);
1346 SV_FlyMove(ent, sv.frametime, NULL);
1347 SV_LinkEdict(ent, true);
1350 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1351 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1358 SV_CheckWaterTransition(ent);
1361 //============================================================================
1363 static void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove)
1365 switch ((int) ent->fields.server->movetype)
1368 case MOVETYPE_FAKEPUSH:
1369 SV_Physics_Pusher (ent);
1372 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1373 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1376 case MOVETYPE_FOLLOW:
1377 SV_Physics_Follow (ent);
1379 case MOVETYPE_NOCLIP:
1380 if (SV_RunThink(ent))
1383 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1384 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1386 SV_LinkEdict(ent, false);
1389 SV_Physics_Step (ent);
1392 if (SV_RunThink (ent))
1394 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1395 SV_AddGravity (ent);
1396 SV_CheckStuck (ent);
1398 SV_LinkEdict (ent, true);
1402 case MOVETYPE_BOUNCE:
1403 case MOVETYPE_BOUNCEMISSILE:
1404 case MOVETYPE_FLYMISSILE:
1407 if (SV_RunThink (ent) && runmove)
1408 SV_Physics_Toss (ent);
1411 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1416 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1418 // make sure the velocity is sane (not a NaN)
1419 SV_CheckVelocity(ent);
1420 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1421 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1423 prog->globals.server->time = sv.time;
1424 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1425 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1429 // make sure the velocity is sane (not a NaN)
1430 SV_CheckVelocity(ent);
1431 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1432 // player_run/player_stand1 does not horribly malfunction if the
1433 // velocity becomes a number that is both == 0 and != 0
1434 // (sounds to me like NaN but to be absolutely safe...)
1435 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1436 VectorClear(ent->fields.server->velocity);
1437 // call standard client pre-think
1438 prog->globals.server->time = sv.time;
1439 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1440 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1441 SV_CheckVelocity (ent);
1443 switch ((int) ent->fields.server->movetype)
1446 case MOVETYPE_FAKEPUSH:
1447 SV_Physics_Pusher (ent);
1450 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1451 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1454 case MOVETYPE_FOLLOW:
1455 SV_Physics_Follow (ent);
1457 case MOVETYPE_NOCLIP:
1458 if (SV_RunThink(ent))
1461 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1462 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1466 SV_Physics_Step (ent);
1469 if (SV_RunThink (ent))
1471 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1472 SV_AddGravity (ent);
1473 SV_CheckStuck (ent);
1478 case MOVETYPE_BOUNCE:
1479 case MOVETYPE_BOUNCEMISSILE:
1480 case MOVETYPE_FLYMISSILE:
1482 if (SV_RunThink (ent))
1483 SV_Physics_Toss (ent);
1486 if (SV_RunThink (ent))
1488 SV_CheckWater (ent);
1493 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1497 SV_CheckVelocity (ent);
1499 // call standard player post-think
1500 SV_LinkEdict (ent, true);
1502 SV_CheckVelocity (ent);
1504 prog->globals.server->time = sv.time;
1505 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1506 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1515 void SV_Physics (void)
1517 int i, newnum_edicts;
1519 unsigned char runmove[MAX_EDICTS];
1521 // let the progs know that a new frame has started
1522 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1523 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1524 prog->globals.server->time = sv.time;
1525 prog->globals.server->frametime = sv.frametime;
1526 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1528 // don't run a move on newly spawned projectiles as it messes up movement
1529 // interpolation and rocket trails
1531 for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1532 if ((runmove[i] = !ent->priv.server->free))
1533 newnum_edicts = i + 1;
1534 prog->num_edicts = max(svs.maxclients + 1, newnum_edicts);
1537 // treat each object in turn
1540 // if force_retouch, relink all the entities
1541 if (prog->globals.server->force_retouch > 0)
1542 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1543 if (!ent->priv.server->free)
1544 SV_LinkEdict (ent, true); // force retouch even for stationary
1546 // run physics on the client entities
1547 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1549 if (!ent->priv.server->free)
1551 // don't do physics on disconnected clients, FrikBot relies on this
1552 if (!host_client->spawned)
1553 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1554 // don't run physics here if running asynchronously
1555 else if (!host_client->movesequence)
1556 SV_Physics_ClientEntity(ent);
1560 // run physics on all the non-client entities
1561 if (!sv_freezenonclients.integer)
1562 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1563 if (!ent->priv.server->free)
1564 SV_Physics_Entity(ent, runmove[i]);
1566 if (prog->globals.server->force_retouch > 0)
1567 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1569 // LordHavoc: endframe support
1572 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1573 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1574 prog->globals.server->time = sv.time;
1575 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1578 if (!sv_freezenonclients.integer)
1579 sv.time += sv.frametime;
1583 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1586 float gravity, savesolid;
1588 prvm_edict_t tempent, *tent;
1593 // copy the vars over
1594 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1595 // set up the temp entity to point to the copied vars
1597 tent->fields.server = &vars;
1599 savesolid = tossent->fields.server->solid;
1600 tossent->fields.server->solid = SOLID_NOT;
1602 // this has to fetch the field from the original edict, since our copy is truncated
1603 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1604 if (val != NULL && val->_float != 0)
1605 gravity = val->_float;
1608 gravity *= sv_gravity.value * 0.05;
1610 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1612 SV_CheckVelocity (tent);
1613 tent->fields.server->velocity[2] -= gravity;
1614 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1615 VectorScale (tent->fields.server->velocity, 0.05, move);
1616 VectorAdd (tent->fields.server->origin, move, end);
1617 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1618 VectorCopy (trace.endpos, tent->fields.server->origin);
1620 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1623 tossent->fields.server->solid = savesolid;
1624 trace.fraction = 0; // not relevant