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", "how fast you slow down"};
43 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
44 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
45 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
46 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
47 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
48 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "1", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
49 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
50 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
51 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
52 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
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_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
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_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
841 SV_LinkEdict (ent, true);
846 VectorCopy (org, ent->fields.server->origin);
847 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
850 void SV_UnstickEntity (prvm_edict_t *ent)
855 VectorCopy (ent->fields.server->origin, org);
857 for (z=0 ; z< 18 ; z += 6)
858 for (i=-1 ; i <= 1 ; i++)
859 for (j=-1 ; j <= 1 ; j++)
861 ent->fields.server->origin[0] = org[0] + i;
862 ent->fields.server->origin[1] = org[1] + j;
863 ent->fields.server->origin[2] = org[2] + z;
864 if (!SV_TestEntityPosition(ent))
866 Con_DPrintf("Unstuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
867 SV_LinkEdict (ent, true);
872 VectorCopy (org, ent->fields.server->origin);
873 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
882 qboolean SV_CheckWater (prvm_edict_t *ent)
887 point[0] = ent->fields.server->origin[0];
888 point[1] = ent->fields.server->origin[1];
889 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
891 ent->fields.server->waterlevel = 0;
892 ent->fields.server->watertype = CONTENTS_EMPTY;
893 cont = SV_PointSuperContents(point);
894 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
896 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
897 ent->fields.server->waterlevel = 1;
898 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
899 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
901 ent->fields.server->waterlevel = 2;
902 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
903 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
904 ent->fields.server->waterlevel = 3;
908 return ent->fields.server->waterlevel > 1;
917 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
920 vec3_t forward, into, side;
922 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
923 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
925 // cut the tangential velocity
926 i = DotProduct (stepnormal, ent->fields.server->velocity);
927 VectorScale (stepnormal, i, into);
928 VectorSubtract (ent->fields.server->velocity, into, side);
929 ent->fields.server->velocity[0] = side[0] * (1 + d);
930 ent->fields.server->velocity[1] = side[1] * (1 + d);
935 =====================
938 Player has come to a dead stop, possibly due to the problem with limited
939 float precision at some angle joins in the BSP hull.
941 Try fixing by pushing one pixel in each direction.
943 This is a hack, but in the interest of good gameplay...
944 ======================
946 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
951 VectorCopy (ent->fields.server->origin, oldorg);
954 for (i=0 ; i<8 ; i++)
956 // try pushing a little in an axial direction
959 case 0: dir[0] = 2; dir[1] = 0; break;
960 case 1: dir[0] = 0; dir[1] = 2; break;
961 case 2: dir[0] = -2; dir[1] = 0; break;
962 case 3: dir[0] = 0; dir[1] = -2; break;
963 case 4: dir[0] = 2; dir[1] = 2; break;
964 case 5: dir[0] = -2; dir[1] = 2; break;
965 case 6: dir[0] = 2; dir[1] = -2; break;
966 case 7: dir[0] = -2; dir[1] = -2; break;
969 SV_PushEntity (ent, dir);
971 // retry the original move
972 ent->fields.server->velocity[0] = oldvel[0];
973 ent->fields.server->velocity[1] = oldvel[1];
974 ent->fields.server->velocity[2] = 0;
975 clip = SV_FlyMove (ent, 0.1, NULL);
977 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
978 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
980 Con_DPrint("TryUnstick - success.\n");
984 // go back to the original pos and try again
985 VectorCopy (oldorg, ent->fields.server->origin);
989 VectorClear (ent->fields.server->velocity);
990 Con_DPrint("TryUnstick - failure.\n");
995 =====================
999 ======================
1001 void SV_WalkMove (prvm_edict_t *ent)
1003 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1004 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1007 SV_CheckVelocity(ent);
1009 // do a regular slide move unless it looks like you ran into a step
1010 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1011 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1013 VectorCopy (ent->fields.server->origin, start_origin);
1014 VectorCopy (ent->fields.server->velocity, start_velocity);
1016 clip = SV_FlyMove (ent, sv.frametime, NULL);
1018 SV_CheckVelocity(ent);
1020 VectorCopy(ent->fields.server->origin, originalmove_origin);
1021 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1022 originalmove_clip = clip;
1023 originalmove_flags = (int)ent->fields.server->flags;
1024 originalmove_groundentity = ent->fields.server->groundentity;
1026 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1029 if (sv_nostep.integer)
1032 // if move didn't block on a step, return
1035 // if move was not trying to move into the step, return
1036 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1039 if (ent->fields.server->movetype != MOVETYPE_FLY)
1041 // return if gibbed by a trigger
1042 if (ent->fields.server->movetype != MOVETYPE_WALK)
1045 // only step up while jumping if that is enabled
1046 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1047 if (!oldonground && ent->fields.server->waterlevel == 0)
1051 // try moving up and forward to go up a step
1052 // back to start pos
1053 VectorCopy (start_origin, ent->fields.server->origin);
1054 VectorCopy (start_velocity, ent->fields.server->velocity);
1057 VectorClear (upmove);
1058 upmove[2] = sv_stepheight.value;
1059 // FIXME: don't link?
1060 SV_PushEntity(ent, upmove);
1063 ent->fields.server->velocity[2] = 0;
1064 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1065 ent->fields.server->velocity[2] += start_velocity[2];
1067 SV_CheckVelocity(ent);
1069 // check for stuckness, possibly due to the limited precision of floats
1070 // in the clipping hulls
1072 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1073 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1075 //Con_Printf("wall\n");
1076 // stepping up didn't make any progress, revert to original move
1077 VectorCopy(originalmove_origin, ent->fields.server->origin);
1078 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1079 //clip = originalmove_clip;
1080 ent->fields.server->flags = originalmove_flags;
1081 ent->fields.server->groundentity = originalmove_groundentity;
1082 // now try to unstick if needed
1083 //clip = SV_TryUnstick (ent, oldvel);
1087 //Con_Printf("step - ");
1089 // extra friction based on view angle
1090 if (clip & 2 && sv_wallfriction.integer)
1091 SV_WallFriction (ent, stepnormal);
1093 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1094 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)))
1098 VectorClear (downmove);
1099 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1100 // FIXME: don't link?
1101 downtrace = SV_PushEntity (ent, downmove);
1103 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1105 // LordHavoc: disabled this check so you can walk on monsters/players
1106 //if (ent->fields.server->solid == SOLID_BSP)
1108 //Con_Printf("onground\n");
1109 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1110 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1115 //Con_Printf("slope\n");
1116 // if the push down didn't end up on good ground, use the move without
1117 // the step up. This happens near wall / slope combinations, and can
1118 // cause the player to hop up higher on a slope too steep to climb
1119 VectorCopy(originalmove_origin, ent->fields.server->origin);
1120 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1121 //clip = originalmove_clip;
1122 ent->fields.server->flags = originalmove_flags;
1123 ent->fields.server->groundentity = originalmove_groundentity;
1126 SV_CheckVelocity(ent);
1129 //============================================================================
1135 Entities that are "stuck" to another entity
1138 void SV_Physics_Follow (prvm_edict_t *ent)
1140 vec3_t vf, vr, vu, angles, v;
1144 if (!SV_RunThink (ent))
1147 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1148 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1149 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])
1151 // quick case for no rotation
1152 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1156 angles[0] = -ent->fields.server->punchangle[0];
1157 angles[1] = ent->fields.server->punchangle[1];
1158 angles[2] = ent->fields.server->punchangle[2];
1159 AngleVectors (angles, vf, vr, vu);
1160 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];
1161 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];
1162 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];
1163 angles[0] = -e->fields.server->angles[0];
1164 angles[1] = e->fields.server->angles[1];
1165 angles[2] = e->fields.server->angles[2];
1166 AngleVectors (angles, vf, vr, vu);
1167 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1168 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1169 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1171 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1172 SV_LinkEdict (ent, true);
1176 ==============================================================================
1180 ==============================================================================
1185 SV_CheckWaterTransition
1189 void SV_CheckWaterTransition (prvm_edict_t *ent)
1192 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1193 if (!ent->fields.server->watertype)
1195 // just spawned here
1196 ent->fields.server->watertype = cont;
1197 ent->fields.server->waterlevel = 1;
1201 // check if the entity crossed into or out of water
1202 if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1203 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1205 if (cont <= CONTENTS_WATER)
1207 ent->fields.server->watertype = cont;
1208 ent->fields.server->waterlevel = 1;
1212 ent->fields.server->watertype = CONTENTS_EMPTY;
1213 ent->fields.server->waterlevel = 0;
1221 Toss, bounce, and fly movement. When onground, do nothing.
1224 void SV_Physics_Toss (prvm_edict_t *ent)
1229 // if onground, return without moving
1230 if ((int)ent->fields.server->flags & FL_ONGROUND)
1232 // don't stick to ground if onground and moving upward
1233 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1234 ent->fields.server->flags -= FL_ONGROUND;
1237 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1238 if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1240 // if ent was supported by a brush model on previous frame,
1241 // and groundentity is now freed, set groundentity to 0 (floating)
1242 if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1244 // leave it suspended in the air
1245 ent->fields.server->groundentity = 0;
1250 ent->priv.server->suspendedinairflag = false;
1252 SV_CheckVelocity (ent);
1255 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1256 SV_AddGravity (ent);
1259 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1262 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1263 trace = SV_PushEntity (ent, move);
1264 if (ent->priv.server->free)
1266 if (trace.startsolid)
1268 // try to unstick the entity
1269 SV_UnstickEntity(ent);
1270 trace = SV_PushEntity (ent, move);
1271 if (ent->priv.server->free)
1275 if (trace.fraction < 1)
1277 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1279 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1280 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1282 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1285 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1286 // LordHavoc: fixed grenades not bouncing when fired down a slope
1287 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1289 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1290 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1292 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1293 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1294 VectorClear (ent->fields.server->velocity);
1295 VectorClear (ent->fields.server->avelocity);
1298 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1302 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1304 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1305 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1306 VectorClear (ent->fields.server->velocity);
1307 VectorClear (ent->fields.server->avelocity);
1310 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1315 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1316 if (trace.plane.normal[2] > 0.7)
1318 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1319 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1320 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1321 ent->priv.server->suspendedinairflag = true;
1322 VectorClear (ent->fields.server->velocity);
1323 VectorClear (ent->fields.server->avelocity);
1326 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1330 // check for in water
1331 SV_CheckWaterTransition (ent);
1335 ===============================================================================
1339 ===============================================================================
1346 Monsters freefall when they don't have a ground entity, otherwise
1347 all movement is done with discrete steps.
1349 This is also used for objects that have become still on the ground, but
1350 will fall if the floor is pulled out from under them.
1353 void SV_Physics_Step (prvm_edict_t *ent)
1355 int flags = (int)ent->fields.server->flags;
1356 // don't fall at all if fly/swim
1357 if (!(flags & (FL_FLY | FL_SWIM)))
1359 if (flags & FL_ONGROUND)
1361 // freefall if onground and moving upward
1362 // freefall if not standing on a world surface (it may be a lift)
1363 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1364 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1366 ent->fields.server->flags -= FL_ONGROUND;
1368 SV_CheckVelocity(ent);
1369 SV_FlyMove(ent, sv.frametime, NULL);
1370 SV_LinkEdict(ent, true);
1375 // freefall if not onground
1376 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1379 SV_CheckVelocity(ent);
1380 SV_FlyMove(ent, sv.frametime, NULL);
1381 SV_LinkEdict(ent, true);
1384 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1385 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1392 SV_CheckWaterTransition(ent);
1395 //============================================================================
1397 static void SV_Physics_Entity (prvm_edict_t *ent)
1399 // don't run a move on newly spawned projectiles as it messes up movement
1400 // interpolation and rocket trails
1401 qboolean runmove = ent->priv.server->move;
1402 ent->priv.server->move = true;
1403 switch ((int) ent->fields.server->movetype)
1406 case MOVETYPE_FAKEPUSH:
1407 SV_Physics_Pusher (ent);
1410 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1411 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1414 case MOVETYPE_FOLLOW:
1415 SV_Physics_Follow (ent);
1417 case MOVETYPE_NOCLIP:
1418 if (SV_RunThink(ent))
1421 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1422 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1424 SV_LinkEdict(ent, false);
1427 SV_Physics_Step (ent);
1430 if (SV_RunThink (ent))
1432 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1433 SV_AddGravity (ent);
1434 SV_CheckStuck (ent);
1436 SV_LinkEdict (ent, true);
1440 case MOVETYPE_BOUNCE:
1441 case MOVETYPE_BOUNCEMISSILE:
1442 case MOVETYPE_FLYMISSILE:
1445 if (SV_RunThink (ent) && runmove)
1446 SV_Physics_Toss (ent);
1449 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1454 void SV_ApplyClientMove (void);
1455 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1457 SV_ApplyClientMove();
1458 // make sure the velocity is sane (not a NaN)
1459 SV_CheckVelocity(ent);
1460 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1461 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1463 prog->globals.server->time = sv.time;
1464 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1465 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1469 // make sure the velocity is sane (not a NaN)
1470 SV_CheckVelocity(ent);
1471 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1472 // player_run/player_stand1 does not horribly malfunction if the
1473 // velocity becomes a number that is both == 0 and != 0
1474 // (sounds to me like NaN but to be absolutely safe...)
1475 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1476 VectorClear(ent->fields.server->velocity);
1477 // call standard client pre-think
1478 prog->globals.server->time = sv.time;
1479 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1480 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1481 SV_CheckVelocity (ent);
1483 switch ((int) ent->fields.server->movetype)
1486 case MOVETYPE_FAKEPUSH:
1487 SV_Physics_Pusher (ent);
1490 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1491 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1494 case MOVETYPE_FOLLOW:
1495 SV_Physics_Follow (ent);
1497 case MOVETYPE_NOCLIP:
1498 if (SV_RunThink(ent))
1501 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1502 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1506 SV_Physics_Step (ent);
1509 if (SV_RunThink (ent))
1511 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1512 SV_AddGravity (ent);
1513 SV_CheckStuck (ent);
1518 case MOVETYPE_BOUNCE:
1519 case MOVETYPE_BOUNCEMISSILE:
1520 case MOVETYPE_FLYMISSILE:
1522 if (SV_RunThink (ent))
1523 SV_Physics_Toss (ent);
1526 if (SV_RunThink (ent))
1528 SV_CheckWater (ent);
1533 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1537 SV_CheckVelocity (ent);
1539 // call standard player post-think
1540 SV_LinkEdict (ent, true);
1542 SV_CheckVelocity (ent);
1544 prog->globals.server->time = sv.time;
1545 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1546 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1555 void SV_Physics (void)
1560 // let the progs know that a new frame has started
1561 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1562 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1563 prog->globals.server->time = sv.time;
1564 prog->globals.server->frametime = sv.frametime;
1565 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1568 // treat each object in turn
1571 // if force_retouch, relink all the entities
1572 if (prog->globals.server->force_retouch > 0)
1573 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1574 if (!ent->priv.server->free)
1575 SV_LinkEdict (ent, true); // force retouch even for stationary
1577 // run physics on the client entities
1578 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1580 if (!ent->priv.server->free)
1582 // don't do physics on disconnected clients, FrikBot relies on this
1583 if (!host_client->spawned)
1584 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1585 // don't run physics here if running asynchronously
1586 else if (!host_client->movesequence)
1587 SV_Physics_ClientEntity(ent);
1591 // run physics on all the non-client entities
1592 if (!sv_freezenonclients.integer)
1593 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1594 if (!ent->priv.server->free)
1595 SV_Physics_Entity(ent);
1597 if (prog->globals.server->force_retouch > 0)
1598 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1600 // LordHavoc: endframe support
1603 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1604 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1605 prog->globals.server->time = sv.time;
1606 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1609 // decrement prog->num_edicts if the highest number entities died
1610 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1612 if (!sv_freezenonclients.integer)
1613 sv.time += sv.frametime;
1617 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1620 float gravity, savesolid;
1622 prvm_edict_t tempent, *tent;
1627 // copy the vars over
1628 memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1629 // set up the temp entity to point to the copied vars
1631 tent->fields.server = &vars;
1633 savesolid = tossent->fields.server->solid;
1634 tossent->fields.server->solid = SOLID_NOT;
1636 // this has to fetch the field from the original edict, since our copy is truncated
1637 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1638 if (val != NULL && val->_float != 0)
1639 gravity = val->_float;
1642 gravity *= sv_gravity.value * 0.05;
1644 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1646 SV_CheckVelocity (tent);
1647 tent->fields.server->velocity[2] -= gravity;
1648 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1649 VectorScale (tent->fields.server->velocity, 0.05, move);
1650 VectorAdd (tent->fields.server->origin, move, end);
1651 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1652 VectorCopy (trace.endpos, tent->fields.server->origin);
1654 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1657 tossent->fields.server->solid = savesolid;
1658 trace.fraction = 0; // not relevant