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", "1"};
51 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0"};
53 #define MOVE_EPSILON 0.01
55 void SV_Physics_Toss (edict_t *ent);
57 void SV_Phys_Init (void)
59 Cvar_RegisterVariable(&sv_stepheight);
60 Cvar_RegisterVariable(&sv_jumpstep);
61 Cvar_RegisterVariable(&sv_wallfriction);
62 Cvar_RegisterVariable(&sv_newflymove);
63 Cvar_RegisterVariable(&sv_freezenonclients);
71 void SV_CheckAllEnts (void)
76 // see if any solid entities are inside the final position
77 check = NEXT_EDICT(sv.edicts);
78 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
82 if (check->v->movetype == MOVETYPE_PUSH
83 || check->v->movetype == MOVETYPE_NONE
84 || check->v->movetype == MOVETYPE_FOLLOW
85 || check->v->movetype == MOVETYPE_NOCLIP)
88 if (SV_TestEntityPosition (check))
89 Con_Printf ("entity in invalid position\n");
98 void SV_CheckVelocity (edict_t *ent)
106 for (i=0 ; i<3 ; i++)
108 if (IS_NAN(ent->v->velocity[i]))
110 Con_Printf ("Got a NaN velocity on %s\n", PR_GetString(ent->v->classname));
111 ent->v->velocity[i] = 0;
113 if (IS_NAN(ent->v->origin[i]))
115 Con_Printf ("Got a NaN origin on %s\n", PR_GetString(ent->v->classname));
116 ent->v->origin[i] = 0;
120 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
121 wishspeed = DotProduct(ent->v->velocity, ent->v->velocity);
122 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
124 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
125 ent->v->velocity[0] *= wishspeed;
126 ent->v->velocity[1] *= wishspeed;
127 ent->v->velocity[2] *= wishspeed;
135 Runs thinking code if time. There is some play in the exact time the think
136 function will be called, because it is called before any movement is done
137 in a frame. Not used for pushmove objects, because they must be exact.
138 Returns false if the entity removed itself.
141 qboolean SV_RunThink (edict_t *ent)
145 thinktime = ent->v->nextthink;
146 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
149 // don't let things stay in the past.
150 // it is possible to start that way by a trigger with a local time.
151 if (thinktime < sv.time)
154 ent->v->nextthink = 0;
155 pr_global_struct->time = thinktime;
156 pr_global_struct->self = EDICT_TO_PROG(ent);
157 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
158 PR_ExecuteProgram (ent->v->think, "NULL think function");
159 return !ent->e->free;
166 Two entities have touched, so run their touch functions
169 void SV_Impact (edict_t *e1, edict_t *e2)
171 int old_self, old_other;
173 old_self = pr_global_struct->self;
174 old_other = pr_global_struct->other;
176 pr_global_struct->time = sv.time;
177 if (e1->v->touch && e1->v->solid != SOLID_NOT)
179 pr_global_struct->self = EDICT_TO_PROG(e1);
180 pr_global_struct->other = EDICT_TO_PROG(e2);
181 PR_ExecuteProgram (e1->v->touch, "");
184 if (e2->v->touch && e2->v->solid != SOLID_NOT)
186 pr_global_struct->self = EDICT_TO_PROG(e2);
187 pr_global_struct->other = EDICT_TO_PROG(e1);
188 PR_ExecuteProgram (e2->v->touch, "");
191 pr_global_struct->self = old_self;
192 pr_global_struct->other = old_other;
200 Slide off of the impacting object
201 returns the blocked flags (1 = floor, 2 = step / wall)
204 #define STOP_EPSILON 0.1
205 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
210 backoff = -DotProduct (in, normal) * overbounce;
211 VectorMA(in, backoff, normal, out);
213 for (i = 0;i < 3;i++)
214 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
223 The basic solid body movement clip that slides along multiple planes
224 Returns the clipflags if the velocity was modified (hit something solid)
228 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
231 // LordHavoc: increased from 5 to 20
232 #define MAX_CLIP_PLANES 20
233 int SV_FlyMove (edict_t *ent, float time, float *stepnormal)
235 int blocked, bumpcount;
236 edict_t *hackongroundentity;
237 hackongroundentity = NULL;
238 if (sv_newflymove.integer)
241 vec3_t end, primal_velocity;
245 VectorCopy (ent->v->velocity, primal_velocity);
247 for (bumpcount = 0;bumpcount < 8;bumpcount++)
249 //Con_Printf("entity %i bump %i: blocked %i velocity %f %f %f\n", ent - sv.edicts, bumpcount, blocked, ent->v->velocity[0], ent->v->velocity[1], ent->v->velocity[2]);
250 if (!ent->v->velocity[0] && !ent->v->velocity[1] && !ent->v->velocity[2])
253 VectorMA(ent->v->origin, time, ent->v->velocity, end);
254 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
255 //Con_Printf("trace %f %f %f : %f : %f %f %f\n", trace.endpos[0], trace.endpos[1], trace.endpos[2], trace.fraction, trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
258 if (trace.startsolid)
260 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
261 // entity is trapped in another solid
262 VectorClear(ent->v->velocity);
267 if (trace.fraction >= 0.001)
269 // actually covered some distance
270 VectorCopy (trace.endpos, ent->v->origin);
273 // break if it moved the entire distance
274 if (trace.fraction == 1)
278 Host_Error ("SV_FlyMove: !trace.ent");
280 if ((int) ent->v->flags & FL_ONGROUND)
282 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
286 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
293 if (trace.plane.normal[2])
295 if (trace.plane.normal[2] > 0.7)
299 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
300 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
302 else if (trace.fraction < 0.001)
303 hackongroundentity = trace.ent;
309 // save the trace for player extrafriction
311 VectorCopy(trace.plane.normal, stepnormal);
314 // run the impact function
317 SV_Impact (ent, trace.ent);
319 // break if removed by the impact function
324 time *= 1 - trace.fraction;
326 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1);
328 // if original velocity is against the original velocity,
329 // stop dead to avoid tiny occilations in sloping corners
330 if (DotProduct (ent->v->velocity, primal_velocity) < 0)
332 VectorClear(ent->v->velocity);
339 int i, j, impact, numplanes;
341 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
345 VectorCopy (ent->v->velocity, original_velocity);
346 VectorCopy (ent->v->velocity, primal_velocity);
351 for (bumpcount = 0;bumpcount < 8;bumpcount++)
353 //Con_Printf("entity %i bump %i: blocked %i velocity %f %f %f\n", ent - sv.edicts, bumpcount, blocked, ent->v->velocity[0], ent->v->velocity[1], ent->v->velocity[2]);
354 if (!ent->v->velocity[0] && !ent->v->velocity[1] && !ent->v->velocity[2])
357 for (i=0 ; i<3 ; i++)
358 end[i] = ent->v->origin[i] + time_left * ent->v->velocity[i];
360 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
363 if (trace.startsolid)
365 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
366 // entity is trapped in another solid
367 VectorClear(ent->v->velocity);
372 if (trace.fraction > 0)
374 // actually covered some distance
375 VectorCopy (trace.endpos, ent->v->origin);
376 VectorCopy (ent->v->velocity, original_velocity);
380 // break if it moved the entire distance
381 if (trace.fraction == 1)
385 Host_Error ("SV_FlyMove: !trace.ent");
387 if ((int) ent->v->flags & FL_ONGROUND)
389 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
393 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
400 if (trace.plane.normal[2])
402 if (trace.plane.normal[2] > 0.7)
406 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
407 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
409 else if (trace.fraction < 0.001)
410 hackongroundentity = trace.ent;
416 // save the trace for player extrafriction
418 VectorCopy(trace.plane.normal, stepnormal);
421 // run the impact function
424 SV_Impact (ent, trace.ent);
426 // break if removed by the impact function
432 time_left -= time_left * trace.fraction;
434 // clipped to another plane
435 if (numplanes >= MAX_CLIP_PLANES)
437 // this shouldn't really happen
438 VectorClear(ent->v->velocity);
443 VectorCopy (trace.plane.normal, planes[numplanes]);
446 // modify original_velocity so it parallels all of the clip planes
447 for (i=0 ; i<numplanes ; i++)
449 ClipVelocity (original_velocity, planes[i], new_velocity, 1);
450 for (j=0 ; j<numplanes ; j++)
454 if (DotProduct (new_velocity, planes[j]) < 0)
463 // go along this plane
464 VectorCopy (new_velocity, ent->v->velocity);
468 // go along the crease
471 VectorClear(ent->v->velocity);
475 CrossProduct (planes[0], planes[1], dir);
476 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
477 VectorNormalize(dir);
478 d = DotProduct (dir, ent->v->velocity);
479 VectorScale (dir, d, ent->v->velocity);
482 // if original velocity is against the original velocity,
483 // stop dead to avoid tiny occilations in sloping corners
484 if (DotProduct (ent->v->velocity, primal_velocity) <= 0)
486 VectorClear(ent->v->velocity);
492 //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - sv.edicts, blocked, ent->v->velocity[0], ent->v->velocity[1], ent->v->velocity[2]);
495 // FIXME: this doesn't work well at all, find another solution
496 // if player is ontop of a non-onground floor and made no progress,
497 // set onground anyway (this tends to happen if standing in a wedge)
498 if (bumpcount == 8 && hackongroundentity)
501 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
502 ent->v->groundentity = EDICT_TO_PROG(hackongroundentity);
507 if ((blocked & 1) == 0 && bumpcount > 1)
509 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
510 // flag ONGROUND if there's ground under it
511 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
524 void SV_AddGravity (edict_t *ent)
529 val = GETEDICTFIELDVALUE(ent, eval_gravity);
530 if (val!=0 && val->_float)
531 ent_gravity = val->_float;
534 ent->v->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
539 ===============================================================================
543 ===============================================================================
550 Does not change the entities velocity at all
553 trace_t SV_PushEntity (edict_t *ent, vec3_t push, vec3_t pushangles)
558 VectorAdd (ent->v->origin, push, end);
560 if (ent->v->movetype == MOVETYPE_FLYMISSILE)
561 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_MISSILE, ent);
562 else if (ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_NOT)
563 // only clip against bmodels
564 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NOMONSTERS, ent);
566 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
568 VectorCopy (trace.endpos, ent->v->origin);
569 // FIXME: turn players specially
570 ent->v->angles[1] += trace.fraction * pushangles[1];
571 SV_LinkEdict (ent, true);
573 if (trace.ent && (!((int)ent->v->flags & FL_ONGROUND) || ent->v->groundentity != EDICT_TO_PROG(trace.ent)))
574 SV_Impact (ent, trace.ent);
585 trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
586 void SV_PushMove (edict_t *pusher, float movetime)
590 float savesolid, movetime2, pushltime;
591 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
593 model_t *pushermodel;
595 if (!pusher->v->velocity[0] && !pusher->v->velocity[1] && !pusher->v->velocity[2] && !pusher->v->avelocity[0] && !pusher->v->avelocity[1] && !pusher->v->avelocity[2])
597 pusher->v->ltime += movetime;
601 switch ((int) pusher->v->solid)
603 // LordHavoc: valid pusher types
607 case SOLID_CORPSE: // LordHavoc: this would be weird...
609 // LordHavoc: no collisions
612 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
613 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
614 pusher->v->angles[0] -= 360.0 * floor(pusher->v->angles[0] * (1.0 / 360.0));
615 pusher->v->angles[1] -= 360.0 * floor(pusher->v->angles[1] * (1.0 / 360.0));
616 pusher->v->angles[2] -= 360.0 * floor(pusher->v->angles[2] * (1.0 / 360.0));
617 pusher->v->ltime += movetime;
618 SV_LinkEdict (pusher, false);
621 Host_Error("SV_PushMove: unrecognized solid type %f\n", pusher->v->solid);
623 index = (int) pusher->v->modelindex;
624 if (index < 1 || index >= MAX_MODELS)
625 Host_Error("SV_PushMove: invalid modelindex %f\n", pusher->v->modelindex);
626 pushermodel = sv.models[index];
628 movetime2 = movetime;
629 VectorScale(pusher->v->velocity, movetime2, move1);
630 VectorScale(pusher->v->avelocity, movetime2, moveangle);
631 if (moveangle[0] || moveangle[2])
633 for (i = 0;i < 3;i++)
637 mins[i] = pushermodel->rotatedmins[i] + pusher->v->origin[i] - 1;
638 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
642 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v->origin[i] - 1;
643 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v->origin[i] + 1;
647 else if (moveangle[1])
649 for (i = 0;i < 3;i++)
653 mins[i] = pushermodel->yawmins[i] + pusher->v->origin[i] - 1;
654 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
658 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v->origin[i] - 1;
659 maxs[i] = pushermodel->yawmaxs[i] + pusher->v->origin[i] + 1;
665 for (i = 0;i < 3;i++)
669 mins[i] = pushermodel->normalmins[i] + pusher->v->origin[i] - 1;
670 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
674 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v->origin[i] - 1;
675 maxs[i] = pushermodel->normalmaxs[i] + pusher->v->origin[i] + 1;
680 VectorNegate (moveangle, a);
681 AngleVectorsFLU (a, forward, left, up);
683 VectorCopy (pusher->v->origin, pushorig);
684 VectorCopy (pusher->v->angles, pushang);
685 pushltime = pusher->v->ltime;
687 // move the pusher to it's final position
689 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
690 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
691 pusher->v->ltime += movetime;
692 SV_LinkEdict (pusher, false);
694 savesolid = pusher->v->solid;
696 // see if any solid entities are inside the final position
698 check = NEXT_EDICT(sv.edicts);
699 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
703 if (check->v->movetype == MOVETYPE_PUSH
704 || check->v->movetype == MOVETYPE_NONE
705 || check->v->movetype == MOVETYPE_FOLLOW
706 || check->v->movetype == MOVETYPE_NOCLIP)
709 // if the entity is standing on the pusher, it will definitely be moved
710 if (!(((int)check->v->flags & FL_ONGROUND) && PROG_TO_EDICT(check->v->groundentity) == pusher))
712 if (check->v->absmin[0] >= maxs[0]
713 || check->v->absmax[0] <= mins[0]
714 || check->v->absmin[1] >= maxs[1]
715 || check->v->absmax[1] <= mins[1]
716 || check->v->absmin[2] >= maxs[2]
717 || check->v->absmax[2] <= mins[2])
720 if (!SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
724 if (forward[0] != 1) // quick way to check if any rotation is used
726 VectorSubtract (check->v->origin, pusher->v->origin, org);
727 org2[0] = DotProduct (org, forward);
728 org2[1] = DotProduct (org, left);
729 org2[2] = DotProduct (org, up);
730 VectorSubtract (org2, org, move);
731 VectorAdd (move, move1, move);
734 VectorCopy (move1, move);
736 // remove the onground flag for non-players
737 if (check->v->movetype != MOVETYPE_WALK)
738 check->v->flags = (int)check->v->flags & ~FL_ONGROUND;
740 VectorCopy (check->v->origin, check->e->moved_from);
741 VectorCopy (check->v->angles, check->e->moved_fromangles);
742 sv.moved_edicts[num_moved++] = check;
744 // try moving the contacted entity
745 pusher->v->solid = SOLID_NOT;
746 SV_PushEntity (check, move, moveangle);
747 pusher->v->solid = savesolid; // was SOLID_BSP
749 // if it is still inside the pusher, block
750 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
752 // try moving the contacted entity a tiny bit further to account for precision errors
753 pusher->v->solid = SOLID_NOT;
754 VectorScale(move, 0.1, move);
755 SV_PushEntity (check, move, vec3_origin);
756 pusher->v->solid = savesolid;
757 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
759 // still inside pusher, so it's really blocked
762 if (check->v->mins[0] == check->v->maxs[0])
764 if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
767 check->v->mins[0] = check->v->mins[1] = 0;
768 VectorCopy (check->v->mins, check->v->maxs);
772 VectorCopy (pushorig, pusher->v->origin);
773 VectorCopy (pushang, pusher->v->angles);
774 pusher->v->ltime = pushltime;
775 SV_LinkEdict (pusher, false);
777 // move back any entities we already moved
778 for (i = 0;i < num_moved;i++)
780 ed = sv.moved_edicts[i];
781 VectorCopy (ed->e->moved_from, ed->v->origin);
782 VectorCopy (ed->e->moved_fromangles, ed->v->angles);
783 SV_LinkEdict (ed, false);
786 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
787 if (pusher->v->blocked)
789 pr_global_struct->self = EDICT_TO_PROG(pusher);
790 pr_global_struct->other = EDICT_TO_PROG(check);
791 PR_ExecuteProgram (pusher->v->blocked, "");
797 pusher->v->angles[0] -= 360.0 * floor(pusher->v->angles[0] * (1.0 / 360.0));
798 pusher->v->angles[1] -= 360.0 * floor(pusher->v->angles[1] * (1.0 / 360.0));
799 pusher->v->angles[2] -= 360.0 * floor(pusher->v->angles[2] * (1.0 / 360.0));
808 void SV_Physics_Pusher (edict_t *ent)
810 float thinktime, oldltime, movetime;
812 oldltime = ent->v->ltime;
814 thinktime = ent->v->nextthink;
815 if (thinktime < ent->v->ltime + sv.frametime)
817 movetime = thinktime - ent->v->ltime;
822 movetime = sv.frametime;
825 // advances ent->v->ltime if not blocked
826 SV_PushMove (ent, movetime);
828 if (thinktime > oldltime && thinktime <= ent->v->ltime)
830 ent->v->nextthink = 0;
831 pr_global_struct->time = sv.time;
832 pr_global_struct->self = EDICT_TO_PROG(ent);
833 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
834 PR_ExecuteProgram (ent->v->think, "NULL think function");
840 ===============================================================================
844 ===============================================================================
851 This is a big hack to try and fix the rare case of getting stuck in the world
855 void SV_CheckStuck (edict_t *ent)
860 if (!SV_TestEntityPosition(ent))
862 VectorCopy (ent->v->origin, ent->v->oldorigin);
866 VectorCopy (ent->v->origin, org);
867 VectorCopy (ent->v->oldorigin, ent->v->origin);
868 if (!SV_TestEntityPosition(ent))
870 Con_DPrintf ("Unstuck.\n");
871 SV_LinkEdict (ent, true);
875 for (z=0 ; z< 18 ; z++)
876 for (i=-1 ; i <= 1 ; i++)
877 for (j=-1 ; j <= 1 ; j++)
879 ent->v->origin[0] = org[0] + i;
880 ent->v->origin[1] = org[1] + j;
881 ent->v->origin[2] = org[2] + z;
882 if (!SV_TestEntityPosition(ent))
884 Con_DPrintf ("Unstuck.\n");
885 SV_LinkEdict (ent, true);
890 VectorCopy (org, ent->v->origin);
891 Con_DPrintf ("player is stuck.\n");
900 qboolean SV_CheckWater (edict_t *ent)
905 point[0] = ent->v->origin[0];
906 point[1] = ent->v->origin[1];
907 point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
909 ent->v->waterlevel = 0;
910 ent->v->watertype = CONTENTS_EMPTY;
911 cont = SV_PointQ1Contents(point);
912 if (cont <= CONTENTS_WATER)
914 ent->v->watertype = cont;
915 ent->v->waterlevel = 1;
916 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
917 cont = SV_PointQ1Contents(point);
918 if (cont <= CONTENTS_WATER)
920 ent->v->waterlevel = 2;
921 point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
922 cont = SV_PointQ1Contents(point);
923 if (cont <= CONTENTS_WATER)
924 ent->v->waterlevel = 3;
928 return ent->v->waterlevel > 1;
937 void SV_WallFriction (edict_t *ent, float *stepnormal)
940 vec3_t forward, into, side;
942 AngleVectors (ent->v->v_angle, forward, NULL, NULL);
943 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
945 // cut the tangential velocity
946 i = DotProduct (stepnormal, ent->v->velocity);
947 VectorScale (stepnormal, i, into);
948 VectorSubtract (ent->v->velocity, into, side);
949 ent->v->velocity[0] = side[0] * (1 + d);
950 ent->v->velocity[1] = side[1] * (1 + d);
955 =====================
958 Player has come to a dead stop, possibly due to the problem with limited
959 float precision at some angle joins in the BSP hull.
961 Try fixing by pushing one pixel in each direction.
963 This is a hack, but in the interest of good gameplay...
964 ======================
966 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
971 VectorCopy (ent->v->origin, oldorg);
974 for (i=0 ; i<8 ; i++)
976 // try pushing a little in an axial direction
979 case 0: dir[0] = 2; dir[1] = 0; break;
980 case 1: dir[0] = 0; dir[1] = 2; break;
981 case 2: dir[0] = -2; dir[1] = 0; break;
982 case 3: dir[0] = 0; dir[1] = -2; break;
983 case 4: dir[0] = 2; dir[1] = 2; break;
984 case 5: dir[0] = -2; dir[1] = 2; break;
985 case 6: dir[0] = 2; dir[1] = -2; break;
986 case 7: dir[0] = -2; dir[1] = -2; break;
989 SV_PushEntity (ent, dir, vec3_origin);
991 // retry the original move
992 ent->v->velocity[0] = oldvel[0];
993 ent->v->velocity[1] = oldvel[1];
994 ent->v->velocity[2] = 0;
995 clip = SV_FlyMove (ent, 0.1, NULL);
997 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
998 || fabs(oldorg[0] - ent->v->origin[0]) > 4)
1000 Con_DPrintf("TryUnstick - success.\n");
1004 // go back to the original pos and try again
1005 VectorCopy (oldorg, ent->v->origin);
1009 VectorClear (ent->v->velocity);
1010 Con_DPrintf("TryUnstick - failure.\n");
1015 =====================
1018 Only used by players
1019 ======================
1021 void SV_WalkMove (edict_t *ent)
1023 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1024 vec3_t upmove, downmove, oldorg, oldvel, nosteporg, nostepvel, stepnormal, originalmove_origin, originalmove_velocity;
1027 SV_CheckVelocity(ent);
1029 // do a regular slide move unless it looks like you ran into a step
1030 oldonground = (int)ent->v->flags & FL_ONGROUND;
1031 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1033 VectorCopy (ent->v->origin, oldorg);
1034 VectorCopy (ent->v->velocity, oldvel);
1036 clip = SV_FlyMove (ent, sv.frametime, NULL);
1037 VectorCopy(ent->v->origin, originalmove_origin);
1038 VectorCopy(ent->v->velocity, originalmove_velocity);
1039 originalmove_clip = clip;
1040 originalmove_flags = (int)ent->v->flags;
1041 originalmove_groundentity = ent->v->groundentity;
1043 SV_CheckVelocity(ent);
1045 // if move didn't block on a step, return
1049 // if move was not trying to move into the step, return
1050 if (fabs(oldvel[0]) < 0.03125 && fabs(oldvel[1]) < 0.03125)
1053 if (ent->v->movetype != MOVETYPE_FLY)
1055 if (!oldonground && ent->v->waterlevel == 0 && !sv_jumpstep.integer)
1056 // don't stair up while jumping
1059 if (ent->v->movetype != MOVETYPE_WALK)
1060 // gibbed by a trigger
1064 SV_CheckVelocity(ent);
1066 if (sv_nostep.integer || (int)ent->v->flags & FL_WATERJUMP )
1069 VectorCopy (ent->v->origin, nosteporg);
1070 VectorCopy (ent->v->velocity, nostepvel);
1072 // try moving up and forward to go up a step
1073 // back to start pos
1074 VectorCopy (oldorg, ent->v->origin);
1076 VectorClear (upmove);
1077 VectorClear (downmove);
1078 upmove[2] = sv_stepheight.value;
1079 downmove[2] = -sv_stepheight.value + oldvel[2]*sv.frametime;
1082 // FIXME: don't link?
1083 SV_PushEntity(ent, upmove, vec3_origin);
1086 ent->v->velocity[0] = oldvel[0];
1087 ent->v->velocity[1] = oldvel[1];
1088 ent->v->velocity[2] = 0;
1089 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1090 ent->v->velocity[2] += oldvel[2];
1092 // check for stuckness, possibly due to the limited precision of floats
1093 // in the clipping hulls
1095 && fabs(oldorg[1] - ent->v->origin[1]) < 0.03125
1096 && fabs(oldorg[0] - ent->v->origin[0]) < 0.03125)
1098 // stepping up didn't make any progress, revert to original move
1099 VectorCopy(originalmove_origin, ent->v->origin);
1100 VectorCopy(originalmove_velocity, ent->v->velocity);
1101 clip = originalmove_clip;
1102 ent->v->flags = originalmove_flags;
1103 ent->v->groundentity = originalmove_groundentity;
1104 // now try to unstick if needed
1105 //clip = SV_TryUnstick (ent, oldvel);
1109 // extra friction based on view angle
1110 if (clip & 2 && sv_wallfriction.integer)
1111 SV_WallFriction (ent, stepnormal);
1114 // FIXME: don't link?
1115 downtrace = SV_PushEntity (ent, downmove, vec3_origin);
1117 if (downtrace.plane.normal[2] > 0.7)
1119 // LordHavoc: disabled this so you can walk on monsters/players
1120 //if (ent->v->solid == SOLID_BSP)
1122 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1123 ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
1128 // if the push down didn't end up on good ground, use the move without
1129 // the step up. This happens near wall / slope combinations, and can
1130 // cause the player to hop up higher on a slope too steep to climb
1131 VectorCopy (nosteporg, ent->v->origin);
1132 VectorCopy (nostepvel, ent->v->velocity);
1135 SV_CheckVelocity(ent);
1138 //============================================================================
1144 Entities that are "stuck" to another entity
1147 void SV_Physics_Follow (edict_t *ent)
1149 vec3_t vf, vr, vu, angles, v;
1153 if (!SV_RunThink (ent))
1156 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1157 e = PROG_TO_EDICT(ent->v->aiment);
1158 if (e->v->angles[0] == ent->v->punchangle[0] && e->v->angles[1] == ent->v->punchangle[1] && e->v->angles[2] == ent->v->punchangle[2])
1160 // quick case for no rotation
1161 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1165 angles[0] = -ent->v->punchangle[0];
1166 angles[1] = ent->v->punchangle[1];
1167 angles[2] = ent->v->punchangle[2];
1168 AngleVectors (angles, vf, vr, vu);
1169 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1170 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1171 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1172 angles[0] = -e->v->angles[0];
1173 angles[1] = e->v->angles[1];
1174 angles[2] = e->v->angles[2];
1175 AngleVectors (angles, vf, vr, vu);
1176 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1177 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1178 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1180 VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1181 SV_LinkEdict (ent, true);
1185 ==============================================================================
1189 ==============================================================================
1194 SV_CheckWaterTransition
1198 void SV_CheckWaterTransition (edict_t *ent)
1201 cont = SV_PointQ1Contents(ent->v->origin);
1202 if (!ent->v->watertype)
1204 // just spawned here
1205 ent->v->watertype = cont;
1206 ent->v->waterlevel = 1;
1210 // check if the entity crossed into or out of water
1211 if ((ent->v->watertype == CONTENTS_WATER || ent->v->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))
1212 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1214 if (cont <= CONTENTS_WATER)
1216 ent->v->watertype = cont;
1217 ent->v->waterlevel = 1;
1221 ent->v->watertype = CONTENTS_EMPTY;
1222 ent->v->waterlevel = 0;
1230 Toss, bounce, and fly movement. When onground, do nothing.
1233 void SV_Physics_Toss (edict_t *ent)
1237 edict_t *groundentity;
1240 if (!SV_RunThink (ent))
1243 // if onground, return without moving
1244 if ((int)ent->v->flags & FL_ONGROUND)
1246 if (ent->v->groundentity == 0)
1248 // if ent was supported by a brush model on previous frame,
1249 // and groundentity is now freed, set groundentity to 0 (floating)
1250 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1251 if (groundentity->v->solid == SOLID_BSP)
1253 ent->e->suspendedinairflag = true;
1256 else if (ent->e->suspendedinairflag && groundentity->e->free)
1258 // leave it suspended in the air
1259 ent->v->groundentity = 0;
1260 ent->e->suspendedinairflag = false;
1264 ent->e->suspendedinairflag = false;
1266 SV_CheckVelocity (ent);
1269 if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1270 SV_AddGravity (ent);
1273 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1276 VectorScale (ent->v->velocity, sv.frametime, move);
1277 trace = SV_PushEntity (ent, move, vec3_origin);
1281 if (trace.fraction < 1)
1283 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1285 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1286 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1288 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1290 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1291 // LordHavoc: fixed grenades not bouncing when fired down a slope
1292 if (trace.plane.normal[2] > 0.7 && DotProduct(trace.plane.normal, ent->v->velocity) < 60)
1294 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1295 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1296 VectorClear (ent->v->velocity);
1297 VectorClear (ent->v->avelocity);
1300 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1304 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1305 if (trace.plane.normal[2] > 0.7)
1307 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1308 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1309 VectorClear (ent->v->velocity);
1310 VectorClear (ent->v->avelocity);
1313 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1317 // check for in water
1318 SV_CheckWaterTransition (ent);
1322 ===============================================================================
1326 ===============================================================================
1333 Monsters freefall when they don't have a ground entity, otherwise
1334 all movement is done with discrete steps.
1336 This is also used for objects that have become still on the ground, but
1337 will fall if the floor is pulled out from under them.
1340 void SV_Physics_Step (edict_t *ent)
1342 // freefall if not onground/fly/swim
1343 if (!((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1345 int hitsound = ent->v->velocity[2] < sv_gravity.value * -0.1;
1348 SV_CheckVelocity(ent);
1349 SV_FlyMove(ent, sv.frametime, NULL);
1350 SV_LinkEdict(ent, true);
1353 if (hitsound && (int)ent->v->flags & FL_ONGROUND)
1354 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1360 SV_CheckWaterTransition(ent);
1363 //============================================================================
1371 void SV_Physics (void)
1376 // let the progs know that a new frame has started
1377 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1378 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1379 pr_global_struct->time = sv.time;
1380 PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1383 // treat each object in turn
1386 for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1391 if (pr_global_struct->force_retouch)
1392 SV_LinkEdict (ent, true); // force retouch even for stationary
1394 if (i <= svs.maxclients)
1398 if (!svs.clients[i-1].spawned)
1401 // call standard client pre-think
1402 SV_CheckVelocity (ent);
1403 pr_global_struct->time = sv.time;
1404 pr_global_struct->self = EDICT_TO_PROG(ent);
1405 PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1406 SV_CheckVelocity (ent);
1409 else if (sv_freezenonclients.integer)
1412 // LordHavoc: merged client and normal entity physics
1413 switch ((int) ent->v->movetype)
1416 SV_Physics_Pusher (ent);
1419 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1420 if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime)
1423 case MOVETYPE_FOLLOW:
1424 SV_Physics_Follow (ent);
1426 case MOVETYPE_NOCLIP:
1427 if (SV_RunThink(ent))
1430 VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1431 VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1433 // relink normal entities here, players always get relinked so don't relink twice
1434 if (!(i > 0 && i <= svs.maxclients))
1435 SV_LinkEdict(ent, false);
1438 SV_Physics_Step (ent);
1441 if (SV_RunThink (ent))
1443 if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1444 SV_AddGravity (ent);
1445 SV_CheckStuck (ent);
1447 // relink normal entities here, players always get relinked so don't relink twice
1448 if (!(i > 0 && i <= svs.maxclients))
1449 SV_LinkEdict (ent, true);
1453 case MOVETYPE_BOUNCE:
1454 case MOVETYPE_BOUNCEMISSILE:
1455 case MOVETYPE_FLYMISSILE:
1456 SV_Physics_Toss (ent);
1459 if (i > 0 && i <= svs.maxclients)
1461 if (SV_RunThink (ent))
1463 SV_CheckWater (ent);
1468 SV_Physics_Toss (ent);
1471 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1475 if (i <= svs.maxclients && i > 0 && !ent->e->free)
1477 SV_CheckVelocity (ent);
1479 // call standard player post-think
1480 SV_LinkEdict (ent, true);
1482 SV_CheckVelocity (ent);
1484 pr_global_struct->time = sv.time;
1485 pr_global_struct->self = EDICT_TO_PROG(ent);
1486 PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1490 if (pr_global_struct->force_retouch)
1491 pr_global_struct->force_retouch--;
1493 // LordHavoc: endframe support
1496 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1497 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1498 pr_global_struct->time = sv.time;
1499 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "");
1502 if (!sv_freezenonclients.integer)
1503 sv.time += sv.frametime;
1507 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1510 float gravity, savesolid;
1512 edict_t tempent, *tent;
1517 // copy the vars over
1518 memcpy(&vars, tossent->v, sizeof(entvars_t));
1519 // set up the temp entity to point to the copied vars
1523 savesolid = tossent->v->solid;
1524 tossent->v->solid = SOLID_NOT;
1526 // this has to fetch the field from the original edict, since our copy is truncated
1527 val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1528 if (val != NULL && val->_float != 0)
1529 gravity = val->_float;
1532 gravity *= sv_gravity.value * 0.05;
1534 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1536 SV_CheckVelocity (tent);
1537 tent->v->velocity[2] -= gravity;
1538 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1539 VectorScale (tent->v->velocity, 0.05, move);
1540 VectorAdd (tent->v->origin, move, end);
1541 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1542 VectorCopy (trace.endpos, tent->v->origin);
1544 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1547 tossent->v->solid = savesolid;
1548 trace.fraction = 0; // not relevant