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"};
52 #define MOVE_EPSILON 0.01
54 void SV_Physics_Toss (edict_t *ent);
56 void SV_Phys_Init (void)
58 Cvar_RegisterVariable(&sv_stepheight);
59 Cvar_RegisterVariable(&sv_jumpstep);
60 Cvar_RegisterVariable(&sv_wallfriction);
61 Cvar_RegisterVariable(&sv_newflymove);
69 void SV_CheckAllEnts (void)
74 // see if any solid entities are inside the final position
75 check = NEXT_EDICT(sv.edicts);
76 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
80 if (check->v->movetype == MOVETYPE_PUSH
81 || check->v->movetype == MOVETYPE_NONE
82 || check->v->movetype == MOVETYPE_FOLLOW
83 || check->v->movetype == MOVETYPE_NOCLIP)
86 if (SV_TestEntityPosition (check))
87 Con_Printf ("entity in invalid position\n");
96 void SV_CheckVelocity (edict_t *ent)
104 for (i=0 ; i<3 ; i++)
106 if (IS_NAN(ent->v->velocity[i]))
108 Con_Printf ("Got a NaN velocity on %s\n", PR_GetString(ent->v->classname));
109 ent->v->velocity[i] = 0;
111 if (IS_NAN(ent->v->origin[i]))
113 Con_Printf ("Got a NaN origin on %s\n", PR_GetString(ent->v->classname));
114 ent->v->origin[i] = 0;
118 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
119 wishspeed = DotProduct(ent->v->velocity, ent->v->velocity);
120 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
122 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
123 ent->v->velocity[0] *= wishspeed;
124 ent->v->velocity[1] *= wishspeed;
125 ent->v->velocity[2] *= wishspeed;
133 Runs thinking code if time. There is some play in the exact time the think
134 function will be called, because it is called before any movement is done
135 in a frame. Not used for pushmove objects, because they must be exact.
136 Returns false if the entity removed itself.
139 qboolean SV_RunThink (edict_t *ent)
143 thinktime = ent->v->nextthink;
144 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
147 // don't let things stay in the past.
148 // it is possible to start that way by a trigger with a local time.
149 if (thinktime < sv.time)
152 ent->v->nextthink = 0;
153 pr_global_struct->time = thinktime;
154 pr_global_struct->self = EDICT_TO_PROG(ent);
155 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
156 PR_ExecuteProgram (ent->v->think, "NULL think function");
157 return !ent->e->free;
164 Two entities have touched, so run their touch functions
167 void SV_Impact (edict_t *e1, edict_t *e2)
169 int old_self, old_other;
171 old_self = pr_global_struct->self;
172 old_other = pr_global_struct->other;
174 pr_global_struct->time = sv.time;
175 if (e1->v->touch && e1->v->solid != SOLID_NOT)
177 pr_global_struct->self = EDICT_TO_PROG(e1);
178 pr_global_struct->other = EDICT_TO_PROG(e2);
179 PR_ExecuteProgram (e1->v->touch, "");
182 if (e2->v->touch && e2->v->solid != SOLID_NOT)
184 pr_global_struct->self = EDICT_TO_PROG(e2);
185 pr_global_struct->other = EDICT_TO_PROG(e1);
186 PR_ExecuteProgram (e2->v->touch, "");
189 pr_global_struct->self = old_self;
190 pr_global_struct->other = old_other;
198 Slide off of the impacting object
199 returns the blocked flags (1 = floor, 2 = step / wall)
202 #define STOP_EPSILON 0.1
203 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
208 backoff = -DotProduct (in, normal) * overbounce;
209 VectorMA(in, backoff, normal, out);
211 for (i = 0;i < 3;i++)
212 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
221 The basic solid body movement clip that slides along multiple planes
222 Returns the clipflags if the velocity was modified (hit something solid)
226 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
229 // LordHavoc: increased from 5 to 20
230 #define MAX_CLIP_PLANES 20
231 int SV_FlyMove (edict_t *ent, float time, float *stepnormal)
233 if (sv_newflymove.integer)
235 int blocked, impact, bumpcount;
236 vec3_t end, primal_velocity;
240 VectorCopy (ent->v->velocity, primal_velocity);
242 for (bumpcount = 0;bumpcount < 4;bumpcount++)
244 if (!ent->v->velocity[0] && !ent->v->velocity[1] && !ent->v->velocity[2])
247 VectorMA(ent->v->origin, time, ent->v->velocity, end);
248 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
249 //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]);
252 if (trace.startsolid)
254 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
255 // entity is trapped in another solid
256 VectorClear(ent->v->velocity);
261 if (trace.fraction > 0)
263 // actually covered some distance
264 VectorCopy (trace.endpos, ent->v->origin);
267 // break if it moved the entire distance
268 if (trace.fraction == 1)
272 Host_Error ("SV_FlyMove: !trace.ent");
274 if ((int) ent->v->flags & FL_ONGROUND)
276 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
280 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
287 if (trace.plane.normal[2] > 0.7)
291 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
292 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
294 if (!trace.plane.normal[2])
298 // save the trace for player extrafriction
300 VectorCopy(trace.plane.normal, stepnormal);
303 // run the impact function
306 SV_Impact (ent, trace.ent);
308 // break if removed by the impact function
313 time *= 1 - trace.fraction;
315 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1);
322 int i, j, blocked, impact, numplanes, bumpcount, numbumps;
324 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
330 VectorCopy (ent->v->velocity, original_velocity);
331 VectorCopy (ent->v->velocity, primal_velocity);
336 for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
338 if (!ent->v->velocity[0] && !ent->v->velocity[1] && !ent->v->velocity[2])
341 for (i=0 ; i<3 ; i++)
342 end[i] = ent->v->origin[i] + time_left * ent->v->velocity[i];
344 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
347 if (trace.startsolid)
349 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
350 // entity is trapped in another solid
351 VectorClear(ent->v->velocity);
356 if (trace.fraction > 0)
358 // actually covered some distance
359 VectorCopy (trace.endpos, ent->v->origin);
360 VectorCopy (ent->v->velocity, original_velocity);
364 // break if it moved the entire distance
365 if (trace.fraction == 1)
369 Host_Error ("SV_FlyMove: !trace.ent");
371 if ((int) ent->v->flags & FL_ONGROUND)
373 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
377 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
384 if (trace.plane.normal[2] > 0.7)
388 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
389 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
391 if (!trace.plane.normal[2])
395 // save the trace for player extrafriction
397 VectorCopy(trace.plane.normal, stepnormal);
400 // run the impact function
403 SV_Impact (ent, trace.ent);
405 // break if removed by the impact function
411 time_left -= time_left * trace.fraction;
413 // clipped to another plane
414 if (numplanes >= MAX_CLIP_PLANES)
416 // this shouldn't really happen
417 VectorClear(ent->v->velocity);
421 VectorCopy (trace.plane.normal, planes[numplanes]);
424 // modify original_velocity so it parallels all of the clip planes
425 for (i=0 ; i<numplanes ; i++)
427 ClipVelocity (original_velocity, planes[i], new_velocity, 1);
428 for (j=0 ; j<numplanes ; j++)
432 if (DotProduct (new_velocity, planes[j]) < 0)
441 // go along this plane
442 VectorCopy (new_velocity, ent->v->velocity);
446 // go along the crease
449 VectorClear(ent->v->velocity);
452 CrossProduct (planes[0], planes[1], dir);
453 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
454 VectorNormalize(dir);
455 d = DotProduct (dir, ent->v->velocity);
456 VectorScale (dir, d, ent->v->velocity);
459 // if original velocity is against the original velocity,
460 // stop dead to avoid tiny occilations in sloping corners
461 if (DotProduct (ent->v->velocity, primal_velocity) <= 0)
463 VectorClear(ent->v->velocity);
479 void SV_AddGravity (edict_t *ent)
484 val = GETEDICTFIELDVALUE(ent, eval_gravity);
485 if (val!=0 && val->_float)
486 ent_gravity = val->_float;
489 ent->v->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
494 ===============================================================================
498 ===============================================================================
505 Does not change the entities velocity at all
508 trace_t SV_PushEntity (edict_t *ent, vec3_t push, vec3_t pushangles)
513 VectorAdd (ent->v->origin, push, end);
515 if (ent->v->movetype == MOVETYPE_FLYMISSILE)
516 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_MISSILE, ent);
517 else if (ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_NOT)
518 // only clip against bmodels
519 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NOMONSTERS, ent);
521 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
523 VectorCopy (trace.endpos, ent->v->origin);
524 // FIXME: turn players specially
525 ent->v->angles[1] += trace.fraction * pushangles[1];
526 SV_LinkEdict (ent, true);
528 if (trace.fraction < 1 && trace.ent && (!((int)ent->v->flags & FL_ONGROUND) || ent->v->groundentity != EDICT_TO_PROG(trace.ent)))
529 SV_Impact (ent, trace.ent);
540 trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
541 void SV_PushMove (edict_t *pusher, float movetime)
545 float savesolid, movetime2, pushltime;
546 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
548 model_t *pushermodel;
551 switch ((int) pusher->v->solid)
553 // LordHavoc: valid pusher types
557 case SOLID_CORPSE: // LordHavoc: this would be weird...
559 // LordHavoc: no collisions
562 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
563 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
564 pusher->v->ltime += movetime;
565 SV_LinkEdict (pusher, false);
568 Host_Error("SV_PushMove: unrecognized solid type %f\n", pusher->v->solid);
570 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])
572 pusher->v->ltime += movetime;
575 index = (int) pusher->v->modelindex;
576 if (index < 1 || index >= MAX_MODELS)
577 Host_Error("SV_PushMove: invalid modelindex %f\n", pusher->v->modelindex);
578 pushermodel = sv.models[index];
580 movetime2 = movetime;
581 VectorScale(pusher->v->velocity, movetime2, move1);
582 VectorScale(pusher->v->avelocity, movetime2, moveangle);
583 if (moveangle[0] || moveangle[2])
585 for (i = 0;i < 3;i++)
589 mins[i] = pushermodel->rotatedmins[i] + pusher->v->origin[i] - 1;
590 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
594 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v->origin[i] - 1;
595 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v->origin[i] + 1;
599 else if (moveangle[1])
601 for (i = 0;i < 3;i++)
605 mins[i] = pushermodel->yawmins[i] + pusher->v->origin[i] - 1;
606 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
610 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v->origin[i] - 1;
611 maxs[i] = pushermodel->yawmaxs[i] + pusher->v->origin[i] + 1;
617 for (i = 0;i < 3;i++)
621 mins[i] = pushermodel->normalmins[i] + pusher->v->origin[i] - 1;
622 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
626 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v->origin[i] - 1;
627 maxs[i] = pushermodel->normalmaxs[i] + pusher->v->origin[i] + 1;
632 VectorNegate (moveangle, a);
633 AngleVectorsFLU (a, forward, left, up);
635 VectorCopy (pusher->v->origin, pushorig);
636 VectorCopy (pusher->v->angles, pushang);
637 pushltime = pusher->v->ltime;
639 // move the pusher to it's final position
641 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
642 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
643 pusher->v->ltime += movetime;
644 SV_LinkEdict (pusher, false);
646 savesolid = pusher->v->solid;
648 // see if any solid entities are inside the final position
650 check = NEXT_EDICT(sv.edicts);
651 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
655 if (check->v->movetype == MOVETYPE_PUSH
656 || check->v->movetype == MOVETYPE_NONE
657 || check->v->movetype == MOVETYPE_FOLLOW
658 || check->v->movetype == MOVETYPE_NOCLIP)
661 // if the entity is standing on the pusher, it will definitely be moved
662 if (!(((int)check->v->flags & FL_ONGROUND) && PROG_TO_EDICT(check->v->groundentity) == pusher))
664 if (check->v->absmin[0] >= maxs[0]
665 || check->v->absmax[0] <= mins[0]
666 || check->v->absmin[1] >= maxs[1]
667 || check->v->absmax[1] <= mins[1]
668 || check->v->absmin[2] >= maxs[2]
669 || check->v->absmax[2] <= mins[2])
672 trace = SV_ClipMoveToEntity (pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin);
673 if (!trace.startsolid)
677 if (forward[0] < 0.999f) // quick way to check if any rotation is used
679 VectorSubtract (check->v->origin, pusher->v->origin, org);
680 org2[0] = DotProduct (org, forward);
681 org2[1] = DotProduct (org, left);
682 org2[2] = DotProduct (org, up);
683 VectorSubtract (org2, org, move);
684 VectorAdd (move, move1, move);
687 VectorCopy (move1, move);
689 // remove the onground flag for non-players
690 if (check->v->movetype != MOVETYPE_WALK)
691 check->v->flags = (int)check->v->flags & ~FL_ONGROUND;
693 VectorCopy (check->v->origin, check->e->moved_from);
694 VectorCopy (check->v->angles, check->e->moved_fromangles);
695 sv.moved_edicts[num_moved++] = check;
697 // try moving the contacted entity
698 pusher->v->solid = SOLID_NOT;
699 trace = SV_PushEntity (check, move, moveangle);
700 pusher->v->solid = savesolid; // was SOLID_BSP
702 // if it is still inside the pusher, block
703 if (SV_TestEntityPosition (check))
705 // try moving the contacted entity a tiny bit further to account for precision errors
706 pusher->v->solid = SOLID_NOT;
707 VectorScale(move, 0.1, move);
708 trace = SV_PushEntity (check, move, vec3_origin);
709 pusher->v->solid = savesolid;
710 if (SV_TestEntityPosition (check))
712 // still inside pusher, so it's really blocked
715 if (check->v->mins[0] == check->v->maxs[0])
717 if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
720 check->v->mins[0] = check->v->mins[1] = 0;
721 VectorCopy (check->v->mins, check->v->maxs);
725 VectorCopy (pushorig, pusher->v->origin);
726 VectorCopy (pushang, pusher->v->angles);
727 pusher->v->ltime = pushltime;
728 SV_LinkEdict (pusher, false);
730 // move back any entities we already moved
731 for (i = 0;i < num_moved;i++)
733 ed = sv.moved_edicts[i];
734 VectorCopy (ed->e->moved_from, ed->v->origin);
735 VectorCopy (ed->e->moved_fromangles, ed->v->angles);
736 SV_LinkEdict (ed, false);
739 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
740 if (pusher->v->blocked)
742 pr_global_struct->self = EDICT_TO_PROG(pusher);
743 pr_global_struct->other = EDICT_TO_PROG(check);
744 PR_ExecuteProgram (pusher->v->blocked, "");
758 void SV_Physics_Pusher (edict_t *ent)
760 float thinktime, oldltime, movetime;
762 oldltime = ent->v->ltime;
764 thinktime = ent->v->nextthink;
765 if (thinktime < ent->v->ltime + sv.frametime)
767 movetime = thinktime - ent->v->ltime;
772 movetime = sv.frametime;
775 // advances ent->v->ltime if not blocked
776 SV_PushMove (ent, movetime);
778 if (thinktime > oldltime && thinktime <= ent->v->ltime)
780 ent->v->nextthink = 0;
781 pr_global_struct->time = sv.time;
782 pr_global_struct->self = EDICT_TO_PROG(ent);
783 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
784 PR_ExecuteProgram (ent->v->think, "NULL think function");
790 ===============================================================================
794 ===============================================================================
801 This is a big hack to try and fix the rare case of getting stuck in the world
805 void SV_CheckStuck (edict_t *ent)
810 if (!SV_TestEntityPosition(ent))
812 VectorCopy (ent->v->origin, ent->v->oldorigin);
816 VectorCopy (ent->v->origin, org);
817 VectorCopy (ent->v->oldorigin, ent->v->origin);
818 if (!SV_TestEntityPosition(ent))
820 Con_DPrintf ("Unstuck.\n");
821 SV_LinkEdict (ent, true);
825 for (z=0 ; z< 18 ; z++)
826 for (i=-1 ; i <= 1 ; i++)
827 for (j=-1 ; j <= 1 ; j++)
829 ent->v->origin[0] = org[0] + i;
830 ent->v->origin[1] = org[1] + j;
831 ent->v->origin[2] = org[2] + z;
832 if (!SV_TestEntityPosition(ent))
834 Con_DPrintf ("Unstuck.\n");
835 SV_LinkEdict (ent, true);
840 VectorCopy (org, ent->v->origin);
841 Con_DPrintf ("player is stuck.\n");
850 qboolean SV_CheckWater (edict_t *ent)
855 point[0] = ent->v->origin[0];
856 point[1] = ent->v->origin[1];
857 point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
859 ent->v->waterlevel = 0;
860 ent->v->watertype = CONTENTS_EMPTY;
861 cont = SV_PointQ1Contents(point);
862 if (cont <= CONTENTS_WATER)
864 ent->v->watertype = cont;
865 ent->v->waterlevel = 1;
866 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
867 cont = SV_PointQ1Contents(point);
868 if (cont <= CONTENTS_WATER)
870 ent->v->waterlevel = 2;
871 point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
872 cont = SV_PointQ1Contents(point);
873 if (cont <= CONTENTS_WATER)
874 ent->v->waterlevel = 3;
878 return ent->v->waterlevel > 1;
887 void SV_WallFriction (edict_t *ent, float *stepnormal)
890 vec3_t forward, into, side;
892 AngleVectors (ent->v->v_angle, forward, NULL, NULL);
893 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
895 // cut the tangential velocity
896 i = DotProduct (stepnormal, ent->v->velocity);
897 VectorScale (stepnormal, i, into);
898 VectorSubtract (ent->v->velocity, into, side);
899 ent->v->velocity[0] = side[0] * (1 + d);
900 ent->v->velocity[1] = side[1] * (1 + d);
905 =====================
908 Player has come to a dead stop, possibly due to the problem with limited
909 float precision at some angle joins in the BSP hull.
911 Try fixing by pushing one pixel in each direction.
913 This is a hack, but in the interest of good gameplay...
914 ======================
916 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
921 VectorCopy (ent->v->origin, oldorg);
924 for (i=0 ; i<8 ; i++)
926 // try pushing a little in an axial direction
929 case 0: dir[0] = 2; dir[1] = 0; break;
930 case 1: dir[0] = 0; dir[1] = 2; break;
931 case 2: dir[0] = -2; dir[1] = 0; break;
932 case 3: dir[0] = 0; dir[1] = -2; break;
933 case 4: dir[0] = 2; dir[1] = 2; break;
934 case 5: dir[0] = -2; dir[1] = 2; break;
935 case 6: dir[0] = 2; dir[1] = -2; break;
936 case 7: dir[0] = -2; dir[1] = -2; break;
939 SV_PushEntity (ent, dir, vec3_origin);
941 // retry the original move
942 ent->v->velocity[0] = oldvel[0];
943 ent->v->velocity[1] = oldvel[1];
944 ent->v->velocity[2] = 0;
945 clip = SV_FlyMove (ent, 0.1, NULL);
947 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
948 || fabs(oldorg[0] - ent->v->origin[0]) > 4)
951 // go back to the original pos and try again
952 VectorCopy (oldorg, ent->v->origin);
956 VectorClear (ent->v->velocity);
961 =====================
965 ======================
967 void SV_WalkMove (edict_t *ent)
969 int clip, oldonground;
970 vec3_t upmove, downmove, oldorg, oldvel, nosteporg, nostepvel, stepnormal;
973 SV_CheckVelocity(ent);
975 // do a regular slide move unless it looks like you ran into a step
976 oldonground = (int)ent->v->flags & FL_ONGROUND;
977 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
979 VectorCopy (ent->v->origin, oldorg);
980 VectorCopy (ent->v->velocity, oldvel);
982 clip = SV_FlyMove (ent, sv.frametime, NULL);
984 SV_CheckVelocity(ent);
986 // if move didn't block on a step, return
990 if (ent->v->movetype != MOVETYPE_FLY)
992 if (!oldonground && ent->v->waterlevel == 0 && !sv_jumpstep.integer)
993 // don't stair up while jumping
996 if (ent->v->movetype != MOVETYPE_WALK)
997 // gibbed by a trigger
1001 SV_CheckVelocity(ent);
1003 if (sv_nostep.integer || (int)ent->v->flags & FL_WATERJUMP )
1006 VectorCopy (ent->v->origin, nosteporg);
1007 VectorCopy (ent->v->velocity, nostepvel);
1009 // try moving up and forward to go up a step
1010 // back to start pos
1011 VectorCopy (oldorg, ent->v->origin);
1013 VectorClear (upmove);
1014 VectorClear (downmove);
1015 upmove[2] = sv_stepheight.value;
1016 downmove[2] = -sv_stepheight.value + oldvel[2]*sv.frametime;
1019 // FIXME: don't link?
1020 SV_PushEntity (ent, upmove, vec3_origin);
1023 ent->v->velocity[0] = oldvel[0];
1024 ent->v->velocity[1] = oldvel[1];
1025 ent->v->velocity[2] = 0;
1026 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1027 ent->v->velocity[2] += oldvel[2];
1029 // check for stuckness, possibly due to the limited precision of floats
1030 // in the clipping hulls
1032 && fabs(oldorg[1] - ent->v->origin[1]) < 0.03125
1033 && fabs(oldorg[0] - ent->v->origin[0]) < 0.03125)
1034 // stepping up didn't make any progress
1035 clip = SV_TryUnstick (ent, oldvel);
1037 // extra friction based on view angle
1038 if (clip & 2 && sv_wallfriction.integer)
1039 SV_WallFriction (ent, stepnormal);
1042 // FIXME: don't link?
1043 downtrace = SV_PushEntity (ent, downmove, vec3_origin);
1045 if (downtrace.plane.normal[2] > 0.7)
1047 // LordHavoc: disabled this so you can walk on monsters/players
1048 //if (ent->v->solid == SOLID_BSP)
1050 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1051 ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
1056 // if the push down didn't end up on good ground, use the move without
1057 // the step up. This happens near wall / slope combinations, and can
1058 // cause the player to hop up higher on a slope too steep to climb
1059 VectorCopy (nosteporg, ent->v->origin);
1060 VectorCopy (nostepvel, ent->v->velocity);
1063 SV_CheckVelocity(ent);
1066 //============================================================================
1072 Entities that are "stuck" to another entity
1075 void SV_Physics_Follow (edict_t *ent)
1077 vec3_t vf, vr, vu, angles, v;
1081 if (!SV_RunThink (ent))
1084 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1085 e = PROG_TO_EDICT(ent->v->aiment);
1086 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])
1088 // quick case for no rotation
1089 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1093 angles[0] = -ent->v->punchangle[0];
1094 angles[1] = ent->v->punchangle[1];
1095 angles[2] = ent->v->punchangle[2];
1096 AngleVectors (angles, vf, vr, vu);
1097 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1098 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1099 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1100 angles[0] = -e->v->angles[0];
1101 angles[1] = e->v->angles[1];
1102 angles[2] = e->v->angles[2];
1103 AngleVectors (angles, vf, vr, vu);
1104 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1105 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1106 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1108 VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1109 SV_LinkEdict (ent, true);
1113 ==============================================================================
1117 ==============================================================================
1122 SV_CheckWaterTransition
1126 void SV_CheckWaterTransition (edict_t *ent)
1129 cont = SV_PointQ1Contents(ent->v->origin);
1130 if (!ent->v->watertype)
1132 // just spawned here
1133 ent->v->watertype = cont;
1134 ent->v->waterlevel = 1;
1138 // check if the entity crossed into or out of water
1139 if ((ent->v->watertype == CONTENTS_WATER || ent->v->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))
1140 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1142 if (cont <= CONTENTS_WATER)
1144 ent->v->watertype = cont;
1145 ent->v->waterlevel = 1;
1149 ent->v->watertype = CONTENTS_EMPTY;
1150 ent->v->waterlevel = 0;
1158 Toss, bounce, and fly movement. When onground, do nothing.
1161 void SV_Physics_Toss (edict_t *ent)
1165 edict_t *groundentity;
1168 if (!SV_RunThink (ent))
1171 // if onground, return without moving
1172 if ((int)ent->v->flags & FL_ONGROUND)
1174 if (ent->v->groundentity == 0)
1176 // if ent was supported by a brush model on previous frame,
1177 // and groundentity is now freed, set groundentity to 0 (floating)
1178 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1179 if (groundentity->v->solid == SOLID_BSP)
1181 ent->e->suspendedinairflag = true;
1184 else if (ent->e->suspendedinairflag && groundentity->e->free)
1186 // leave it suspended in the air
1187 ent->v->groundentity = 0;
1188 ent->e->suspendedinairflag = false;
1192 ent->e->suspendedinairflag = false;
1194 SV_CheckVelocity (ent);
1197 if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1198 SV_AddGravity (ent);
1201 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1204 VectorScale (ent->v->velocity, sv.frametime, move);
1205 trace = SV_PushEntity (ent, move, vec3_origin);
1209 if (trace.fraction < 1)
1211 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1213 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1214 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1216 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1218 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1219 // LordHavoc: fixed grenades not bouncing when fired down a slope
1220 if (trace.plane.normal[2] > 0.7 && DotProduct(trace.plane.normal, ent->v->velocity) < 60)
1222 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1223 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1224 VectorClear (ent->v->velocity);
1225 VectorClear (ent->v->avelocity);
1228 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1232 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1233 if (trace.plane.normal[2] > 0.7)
1235 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1236 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1237 VectorClear (ent->v->velocity);
1238 VectorClear (ent->v->avelocity);
1241 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1245 // check for in water
1246 SV_CheckWaterTransition (ent);
1250 ===============================================================================
1254 ===============================================================================
1261 Monsters freefall when they don't have a ground entity, otherwise
1262 all movement is done with discrete steps.
1264 This is also used for objects that have become still on the ground, but
1265 will fall if the floor is pulled out from under them.
1268 void SV_Physics_Step (edict_t *ent)
1270 // freefall if not onground/fly/swim
1271 if (!((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1273 int hitsound = ent->v->velocity[2] < sv_gravity.value * -0.1;
1276 SV_CheckVelocity(ent);
1277 SV_FlyMove(ent, sv.frametime, NULL);
1278 SV_LinkEdict(ent, false);
1281 if (hitsound && (int)ent->v->flags & FL_ONGROUND)
1282 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1288 SV_CheckWaterTransition(ent);
1291 //============================================================================
1299 void SV_Physics (void)
1304 // let the progs know that a new frame has started
1305 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1306 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1307 pr_global_struct->time = sv.time;
1308 PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1311 // treat each object in turn
1314 for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1319 if (pr_global_struct->force_retouch)
1320 SV_LinkEdict (ent, true); // force retouch even for stationary
1322 if (i > 0 && i <= svs.maxclients)
1324 if (!svs.clients[i-1].spawned)
1327 // call standard client pre-think
1328 SV_CheckVelocity (ent);
1329 pr_global_struct->time = sv.time;
1330 pr_global_struct->self = EDICT_TO_PROG(ent);
1331 PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1332 SV_CheckVelocity (ent);
1335 // LordHavoc: merged client and normal entity physics
1336 switch ((int) ent->v->movetype)
1339 SV_Physics_Pusher (ent);
1342 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1343 if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime)
1346 case MOVETYPE_FOLLOW:
1347 SV_Physics_Follow (ent);
1349 case MOVETYPE_NOCLIP:
1350 if (SV_RunThink(ent))
1353 VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1354 VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1356 // relink normal entities here, players always get relinked so don't relink twice
1357 if (!(i > 0 && i <= svs.maxclients))
1358 SV_LinkEdict(ent, false);
1361 SV_Physics_Step (ent);
1364 if (SV_RunThink (ent))
1366 if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1367 SV_AddGravity (ent);
1368 SV_CheckStuck (ent);
1370 SV_LinkEdict (ent, true);
1374 case MOVETYPE_BOUNCE:
1375 case MOVETYPE_BOUNCEMISSILE:
1376 case MOVETYPE_FLYMISSILE:
1377 SV_Physics_Toss (ent);
1380 if (i > 0 && i <= svs.maxclients)
1382 if (SV_RunThink (ent))
1384 SV_CheckWater (ent);
1389 SV_Physics_Toss (ent);
1392 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1396 if (i > 0 && i <= svs.maxclients && !ent->e->free)
1398 SV_CheckVelocity (ent);
1400 // call standard player post-think
1401 SV_LinkEdict (ent, true);
1403 SV_CheckVelocity (ent);
1405 pr_global_struct->time = sv.time;
1406 pr_global_struct->self = EDICT_TO_PROG(ent);
1407 PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1411 if (pr_global_struct->force_retouch)
1412 pr_global_struct->force_retouch--;
1414 // LordHavoc: endframe support
1417 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1418 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1419 pr_global_struct->time = sv.time;
1420 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "");
1423 sv.time += sv.frametime;
1427 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1430 float gravity, savesolid;
1432 edict_t tempent, *tent;
1437 // copy the vars over
1438 memcpy(&vars, tossent->v, sizeof(entvars_t));
1439 // set up the temp entity to point to the copied vars
1443 savesolid = tossent->v->solid;
1444 tossent->v->solid = SOLID_NOT;
1446 // this has to fetch the field from the original edict, since our copy is truncated
1447 val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1448 if (val != NULL && val->_float != 0)
1449 gravity = val->_float;
1452 gravity *= sv_gravity.value * 0.05;
1454 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1456 SV_CheckVelocity (tent);
1457 tent->v->velocity[2] -= gravity;
1458 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1459 VectorScale (tent->v->velocity, 0.05, move);
1460 VectorAdd (tent->v->origin, move, end);
1461 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1462 VectorCopy (trace.endpos, tent->v->origin);
1464 if (trace.fraction < 1 && trace.ent)
1465 if (trace.ent != ignore)
1468 tossent->v->solid = savesolid;
1469 trace.fraction = 0; // not relevant