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.001)
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.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;
550 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])
552 pusher->v->ltime += movetime;
556 switch ((int) pusher->v->solid)
558 // LordHavoc: valid pusher types
562 case SOLID_CORPSE: // LordHavoc: this would be weird...
564 // LordHavoc: no collisions
567 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
568 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
569 pusher->v->angles[0] -= 360.0 * floor(pusher->v->angles[0] * (1.0 / 360.0));
570 pusher->v->angles[1] -= 360.0 * floor(pusher->v->angles[1] * (1.0 / 360.0));
571 pusher->v->angles[2] -= 360.0 * floor(pusher->v->angles[2] * (1.0 / 360.0));
572 pusher->v->ltime += movetime;
573 SV_LinkEdict (pusher, false);
576 Host_Error("SV_PushMove: unrecognized solid type %f\n", pusher->v->solid);
578 index = (int) pusher->v->modelindex;
579 if (index < 1 || index >= MAX_MODELS)
580 Host_Error("SV_PushMove: invalid modelindex %f\n", pusher->v->modelindex);
581 pushermodel = sv.models[index];
583 movetime2 = movetime;
584 VectorScale(pusher->v->velocity, movetime2, move1);
585 VectorScale(pusher->v->avelocity, movetime2, moveangle);
586 if (moveangle[0] || moveangle[2])
588 for (i = 0;i < 3;i++)
592 mins[i] = pushermodel->rotatedmins[i] + pusher->v->origin[i] - 1;
593 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
597 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v->origin[i] - 1;
598 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v->origin[i] + 1;
602 else if (moveangle[1])
604 for (i = 0;i < 3;i++)
608 mins[i] = pushermodel->yawmins[i] + pusher->v->origin[i] - 1;
609 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
613 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v->origin[i] - 1;
614 maxs[i] = pushermodel->yawmaxs[i] + pusher->v->origin[i] + 1;
620 for (i = 0;i < 3;i++)
624 mins[i] = pushermodel->normalmins[i] + pusher->v->origin[i] - 1;
625 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
629 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v->origin[i] - 1;
630 maxs[i] = pushermodel->normalmaxs[i] + pusher->v->origin[i] + 1;
635 VectorNegate (moveangle, a);
636 AngleVectorsFLU (a, forward, left, up);
638 VectorCopy (pusher->v->origin, pushorig);
639 VectorCopy (pusher->v->angles, pushang);
640 pushltime = pusher->v->ltime;
642 // move the pusher to it's final position
644 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
645 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
646 pusher->v->ltime += movetime;
647 SV_LinkEdict (pusher, false);
649 savesolid = pusher->v->solid;
651 // see if any solid entities are inside the final position
653 check = NEXT_EDICT(sv.edicts);
654 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
658 if (check->v->movetype == MOVETYPE_PUSH
659 || check->v->movetype == MOVETYPE_NONE
660 || check->v->movetype == MOVETYPE_FOLLOW
661 || check->v->movetype == MOVETYPE_NOCLIP)
664 // if the entity is standing on the pusher, it will definitely be moved
665 if (!(((int)check->v->flags & FL_ONGROUND) && PROG_TO_EDICT(check->v->groundentity) == pusher))
667 if (check->v->absmin[0] >= maxs[0]
668 || check->v->absmax[0] <= mins[0]
669 || check->v->absmin[1] >= maxs[1]
670 || check->v->absmax[1] <= mins[1]
671 || check->v->absmin[2] >= maxs[2]
672 || check->v->absmax[2] <= mins[2])
675 if (!SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
679 if (forward[0] != 1) // quick way to check if any rotation is used
681 VectorSubtract (check->v->origin, pusher->v->origin, org);
682 org2[0] = DotProduct (org, forward);
683 org2[1] = DotProduct (org, left);
684 org2[2] = DotProduct (org, up);
685 VectorSubtract (org2, org, move);
686 VectorAdd (move, move1, move);
689 VectorCopy (move1, move);
691 // remove the onground flag for non-players
692 if (check->v->movetype != MOVETYPE_WALK)
693 check->v->flags = (int)check->v->flags & ~FL_ONGROUND;
695 VectorCopy (check->v->origin, check->e->moved_from);
696 VectorCopy (check->v->angles, check->e->moved_fromangles);
697 sv.moved_edicts[num_moved++] = check;
699 // try moving the contacted entity
700 pusher->v->solid = SOLID_NOT;
701 SV_PushEntity (check, move, moveangle);
702 pusher->v->solid = savesolid; // was SOLID_BSP
704 // if it is still inside the pusher, block
705 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
707 // try moving the contacted entity a tiny bit further to account for precision errors
708 pusher->v->solid = SOLID_NOT;
709 VectorScale(move, 0.1, move);
710 SV_PushEntity (check, move, vec3_origin);
711 pusher->v->solid = savesolid;
712 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
714 // still inside pusher, so it's really blocked
717 if (check->v->mins[0] == check->v->maxs[0])
719 if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
722 check->v->mins[0] = check->v->mins[1] = 0;
723 VectorCopy (check->v->mins, check->v->maxs);
727 VectorCopy (pushorig, pusher->v->origin);
728 VectorCopy (pushang, pusher->v->angles);
729 pusher->v->ltime = pushltime;
730 SV_LinkEdict (pusher, false);
732 // move back any entities we already moved
733 for (i = 0;i < num_moved;i++)
735 ed = sv.moved_edicts[i];
736 VectorCopy (ed->e->moved_from, ed->v->origin);
737 VectorCopy (ed->e->moved_fromangles, ed->v->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->v->blocked)
744 pr_global_struct->self = EDICT_TO_PROG(pusher);
745 pr_global_struct->other = EDICT_TO_PROG(check);
746 PR_ExecuteProgram (pusher->v->blocked, "");
752 pusher->v->angles[0] -= 360.0 * floor(pusher->v->angles[0] * (1.0 / 360.0));
753 pusher->v->angles[1] -= 360.0 * floor(pusher->v->angles[1] * (1.0 / 360.0));
754 pusher->v->angles[2] -= 360.0 * floor(pusher->v->angles[2] * (1.0 / 360.0));
763 void SV_Physics_Pusher (edict_t *ent)
765 float thinktime, oldltime, movetime;
767 oldltime = ent->v->ltime;
769 thinktime = ent->v->nextthink;
770 if (thinktime < ent->v->ltime + sv.frametime)
772 movetime = thinktime - ent->v->ltime;
777 movetime = sv.frametime;
780 // advances ent->v->ltime if not blocked
781 SV_PushMove (ent, movetime);
783 if (thinktime > oldltime && thinktime <= ent->v->ltime)
785 ent->v->nextthink = 0;
786 pr_global_struct->time = sv.time;
787 pr_global_struct->self = EDICT_TO_PROG(ent);
788 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
789 PR_ExecuteProgram (ent->v->think, "NULL think function");
795 ===============================================================================
799 ===============================================================================
806 This is a big hack to try and fix the rare case of getting stuck in the world
810 void SV_CheckStuck (edict_t *ent)
815 if (!SV_TestEntityPosition(ent))
817 VectorCopy (ent->v->origin, ent->v->oldorigin);
821 VectorCopy (ent->v->origin, org);
822 VectorCopy (ent->v->oldorigin, ent->v->origin);
823 if (!SV_TestEntityPosition(ent))
825 Con_DPrintf ("Unstuck.\n");
826 SV_LinkEdict (ent, true);
830 for (z=0 ; z< 18 ; z++)
831 for (i=-1 ; i <= 1 ; i++)
832 for (j=-1 ; j <= 1 ; j++)
834 ent->v->origin[0] = org[0] + i;
835 ent->v->origin[1] = org[1] + j;
836 ent->v->origin[2] = org[2] + z;
837 if (!SV_TestEntityPosition(ent))
839 Con_DPrintf ("Unstuck.\n");
840 SV_LinkEdict (ent, true);
845 VectorCopy (org, ent->v->origin);
846 Con_DPrintf ("player is stuck.\n");
855 qboolean SV_CheckWater (edict_t *ent)
860 point[0] = ent->v->origin[0];
861 point[1] = ent->v->origin[1];
862 point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
864 ent->v->waterlevel = 0;
865 ent->v->watertype = CONTENTS_EMPTY;
866 cont = SV_PointQ1Contents(point);
867 if (cont <= CONTENTS_WATER)
869 ent->v->watertype = cont;
870 ent->v->waterlevel = 1;
871 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
872 cont = SV_PointQ1Contents(point);
873 if (cont <= CONTENTS_WATER)
875 ent->v->waterlevel = 2;
876 point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
877 cont = SV_PointQ1Contents(point);
878 if (cont <= CONTENTS_WATER)
879 ent->v->waterlevel = 3;
883 return ent->v->waterlevel > 1;
892 void SV_WallFriction (edict_t *ent, float *stepnormal)
895 vec3_t forward, into, side;
897 AngleVectors (ent->v->v_angle, forward, NULL, NULL);
898 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
900 // cut the tangential velocity
901 i = DotProduct (stepnormal, ent->v->velocity);
902 VectorScale (stepnormal, i, into);
903 VectorSubtract (ent->v->velocity, into, side);
904 ent->v->velocity[0] = side[0] * (1 + d);
905 ent->v->velocity[1] = side[1] * (1 + d);
910 =====================
913 Player has come to a dead stop, possibly due to the problem with limited
914 float precision at some angle joins in the BSP hull.
916 Try fixing by pushing one pixel in each direction.
918 This is a hack, but in the interest of good gameplay...
919 ======================
921 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
926 VectorCopy (ent->v->origin, oldorg);
929 for (i=0 ; i<8 ; i++)
931 // try pushing a little in an axial direction
934 case 0: dir[0] = 2; dir[1] = 0; break;
935 case 1: dir[0] = 0; dir[1] = 2; break;
936 case 2: dir[0] = -2; dir[1] = 0; break;
937 case 3: dir[0] = 0; dir[1] = -2; break;
938 case 4: dir[0] = 2; dir[1] = 2; break;
939 case 5: dir[0] = -2; dir[1] = 2; break;
940 case 6: dir[0] = 2; dir[1] = -2; break;
941 case 7: dir[0] = -2; dir[1] = -2; break;
944 SV_PushEntity (ent, dir, vec3_origin);
946 // retry the original move
947 ent->v->velocity[0] = oldvel[0];
948 ent->v->velocity[1] = oldvel[1];
949 ent->v->velocity[2] = 0;
950 clip = SV_FlyMove (ent, 0.1, NULL);
952 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
953 || fabs(oldorg[0] - ent->v->origin[0]) > 4)
955 Con_DPrintf("TryUnstick - success.\n");
959 // go back to the original pos and try again
960 VectorCopy (oldorg, ent->v->origin);
964 VectorClear (ent->v->velocity);
965 Con_DPrintf("TryUnstick - failure.\n");
970 =====================
974 ======================
976 void SV_WalkMove (edict_t *ent)
978 int clip, oldonground;
979 vec3_t upmove, downmove, oldorg, oldvel, nosteporg, nostepvel, stepnormal;
982 SV_CheckVelocity(ent);
984 // do a regular slide move unless it looks like you ran into a step
985 oldonground = (int)ent->v->flags & FL_ONGROUND;
986 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
988 VectorCopy (ent->v->origin, oldorg);
989 VectorCopy (ent->v->velocity, oldvel);
991 clip = SV_FlyMove (ent, sv.frametime, NULL);
993 SV_CheckVelocity(ent);
995 // if move didn't block on a step, return
999 if (ent->v->movetype != MOVETYPE_FLY)
1001 if (!oldonground && ent->v->waterlevel == 0 && !sv_jumpstep.integer)
1002 // don't stair up while jumping
1005 if (ent->v->movetype != MOVETYPE_WALK)
1006 // gibbed by a trigger
1010 SV_CheckVelocity(ent);
1012 if (sv_nostep.integer || (int)ent->v->flags & FL_WATERJUMP )
1015 VectorCopy (ent->v->origin, nosteporg);
1016 VectorCopy (ent->v->velocity, nostepvel);
1018 // try moving up and forward to go up a step
1019 // back to start pos
1020 VectorCopy (oldorg, ent->v->origin);
1022 VectorClear (upmove);
1023 VectorClear (downmove);
1024 upmove[2] = sv_stepheight.value;
1025 downmove[2] = -sv_stepheight.value + oldvel[2]*sv.frametime;
1028 // FIXME: don't link?
1029 SV_PushEntity (ent, upmove, vec3_origin);
1032 ent->v->velocity[0] = oldvel[0];
1033 ent->v->velocity[1] = oldvel[1];
1034 ent->v->velocity[2] = 0;
1035 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1036 ent->v->velocity[2] += oldvel[2];
1038 // check for stuckness, possibly due to the limited precision of floats
1039 // in the clipping hulls
1041 && fabs(oldorg[1] - ent->v->origin[1]) < 0.03125
1042 && fabs(oldorg[0] - ent->v->origin[0]) < 0.03125)
1043 // stepping up didn't make any progress
1044 clip = SV_TryUnstick (ent, oldvel);
1046 // extra friction based on view angle
1047 if (clip & 2 && sv_wallfriction.integer)
1048 SV_WallFriction (ent, stepnormal);
1051 // FIXME: don't link?
1052 downtrace = SV_PushEntity (ent, downmove, vec3_origin);
1054 if (downtrace.plane.normal[2] > 0.7)
1056 // LordHavoc: disabled this so you can walk on monsters/players
1057 //if (ent->v->solid == SOLID_BSP)
1059 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1060 ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
1065 // if the push down didn't end up on good ground, use the move without
1066 // the step up. This happens near wall / slope combinations, and can
1067 // cause the player to hop up higher on a slope too steep to climb
1068 VectorCopy (nosteporg, ent->v->origin);
1069 VectorCopy (nostepvel, ent->v->velocity);
1072 SV_CheckVelocity(ent);
1075 //============================================================================
1081 Entities that are "stuck" to another entity
1084 void SV_Physics_Follow (edict_t *ent)
1086 vec3_t vf, vr, vu, angles, v;
1090 if (!SV_RunThink (ent))
1093 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1094 e = PROG_TO_EDICT(ent->v->aiment);
1095 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])
1097 // quick case for no rotation
1098 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1102 angles[0] = -ent->v->punchangle[0];
1103 angles[1] = ent->v->punchangle[1];
1104 angles[2] = ent->v->punchangle[2];
1105 AngleVectors (angles, vf, vr, vu);
1106 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1107 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1108 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1109 angles[0] = -e->v->angles[0];
1110 angles[1] = e->v->angles[1];
1111 angles[2] = e->v->angles[2];
1112 AngleVectors (angles, vf, vr, vu);
1113 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1114 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1115 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1117 VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1118 SV_LinkEdict (ent, true);
1122 ==============================================================================
1126 ==============================================================================
1131 SV_CheckWaterTransition
1135 void SV_CheckWaterTransition (edict_t *ent)
1138 cont = SV_PointQ1Contents(ent->v->origin);
1139 if (!ent->v->watertype)
1141 // just spawned here
1142 ent->v->watertype = cont;
1143 ent->v->waterlevel = 1;
1147 // check if the entity crossed into or out of water
1148 if ((ent->v->watertype == CONTENTS_WATER || ent->v->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))
1149 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1151 if (cont <= CONTENTS_WATER)
1153 ent->v->watertype = cont;
1154 ent->v->waterlevel = 1;
1158 ent->v->watertype = CONTENTS_EMPTY;
1159 ent->v->waterlevel = 0;
1167 Toss, bounce, and fly movement. When onground, do nothing.
1170 void SV_Physics_Toss (edict_t *ent)
1174 edict_t *groundentity;
1177 if (!SV_RunThink (ent))
1180 // if onground, return without moving
1181 if ((int)ent->v->flags & FL_ONGROUND)
1183 if (ent->v->groundentity == 0)
1185 // if ent was supported by a brush model on previous frame,
1186 // and groundentity is now freed, set groundentity to 0 (floating)
1187 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1188 if (groundentity->v->solid == SOLID_BSP)
1190 ent->e->suspendedinairflag = true;
1193 else if (ent->e->suspendedinairflag && groundentity->e->free)
1195 // leave it suspended in the air
1196 ent->v->groundentity = 0;
1197 ent->e->suspendedinairflag = false;
1201 ent->e->suspendedinairflag = false;
1203 SV_CheckVelocity (ent);
1206 if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1207 SV_AddGravity (ent);
1210 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1213 VectorScale (ent->v->velocity, sv.frametime, move);
1214 trace = SV_PushEntity (ent, move, vec3_origin);
1218 if (trace.fraction < 1)
1220 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1222 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1223 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1225 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1227 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1228 // LordHavoc: fixed grenades not bouncing when fired down a slope
1229 if (trace.plane.normal[2] > 0.7 && DotProduct(trace.plane.normal, ent->v->velocity) < 60)
1231 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1232 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1233 VectorClear (ent->v->velocity);
1234 VectorClear (ent->v->avelocity);
1237 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1241 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1242 if (trace.plane.normal[2] > 0.7)
1244 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1245 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1246 VectorClear (ent->v->velocity);
1247 VectorClear (ent->v->avelocity);
1250 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1254 // check for in water
1255 SV_CheckWaterTransition (ent);
1259 ===============================================================================
1263 ===============================================================================
1270 Monsters freefall when they don't have a ground entity, otherwise
1271 all movement is done with discrete steps.
1273 This is also used for objects that have become still on the ground, but
1274 will fall if the floor is pulled out from under them.
1277 void SV_Physics_Step (edict_t *ent)
1279 // freefall if not onground/fly/swim
1280 if (!((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1282 int hitsound = ent->v->velocity[2] < sv_gravity.value * -0.1;
1285 SV_CheckVelocity(ent);
1286 SV_FlyMove(ent, sv.frametime, NULL);
1287 SV_LinkEdict(ent, true);
1290 if (hitsound && (int)ent->v->flags & FL_ONGROUND)
1291 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1297 SV_CheckWaterTransition(ent);
1300 //============================================================================
1308 void SV_Physics (void)
1313 // let the progs know that a new frame has started
1314 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1315 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1316 pr_global_struct->time = sv.time;
1317 PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1320 // treat each object in turn
1323 for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1328 if (pr_global_struct->force_retouch)
1329 SV_LinkEdict (ent, true); // force retouch even for stationary
1331 if (i > 0 && i <= svs.maxclients)
1333 if (!svs.clients[i-1].spawned)
1336 // call standard client pre-think
1337 SV_CheckVelocity (ent);
1338 pr_global_struct->time = sv.time;
1339 pr_global_struct->self = EDICT_TO_PROG(ent);
1340 PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1341 SV_CheckVelocity (ent);
1344 // LordHavoc: merged client and normal entity physics
1345 switch ((int) ent->v->movetype)
1348 SV_Physics_Pusher (ent);
1351 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1352 if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime)
1355 case MOVETYPE_FOLLOW:
1356 SV_Physics_Follow (ent);
1358 case MOVETYPE_NOCLIP:
1359 if (SV_RunThink(ent))
1362 VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1363 VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1365 // relink normal entities here, players always get relinked so don't relink twice
1366 if (!(i > 0 && i <= svs.maxclients))
1367 SV_LinkEdict(ent, false);
1370 SV_Physics_Step (ent);
1373 if (SV_RunThink (ent))
1375 if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1376 SV_AddGravity (ent);
1377 SV_CheckStuck (ent);
1379 // relink normal entities here, players always get relinked so don't relink twice
1380 if (!(i > 0 && i <= svs.maxclients))
1381 SV_LinkEdict (ent, true);
1385 case MOVETYPE_BOUNCE:
1386 case MOVETYPE_BOUNCEMISSILE:
1387 case MOVETYPE_FLYMISSILE:
1388 SV_Physics_Toss (ent);
1391 if (i > 0 && i <= svs.maxclients)
1393 if (SV_RunThink (ent))
1395 SV_CheckWater (ent);
1400 SV_Physics_Toss (ent);
1403 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1407 if (i > 0 && i <= svs.maxclients && !ent->e->free)
1409 SV_CheckVelocity (ent);
1411 // call standard player post-think
1412 SV_LinkEdict (ent, true);
1414 SV_CheckVelocity (ent);
1416 pr_global_struct->time = sv.time;
1417 pr_global_struct->self = EDICT_TO_PROG(ent);
1418 PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1422 if (pr_global_struct->force_retouch)
1423 pr_global_struct->force_retouch--;
1425 // LordHavoc: endframe support
1428 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1429 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1430 pr_global_struct->time = sv.time;
1431 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "");
1434 sv.time += sv.frametime;
1438 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1441 float gravity, savesolid;
1443 edict_t tempent, *tent;
1448 // copy the vars over
1449 memcpy(&vars, tossent->v, sizeof(entvars_t));
1450 // set up the temp entity to point to the copied vars
1454 savesolid = tossent->v->solid;
1455 tossent->v->solid = SOLID_NOT;
1457 // this has to fetch the field from the original edict, since our copy is truncated
1458 val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1459 if (val != NULL && val->_float != 0)
1460 gravity = val->_float;
1463 gravity *= sv_gravity.value * 0.05;
1465 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1467 SV_CheckVelocity (tent);
1468 tent->v->velocity[2] -= gravity;
1469 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1470 VectorScale (tent->v->velocity, 0.05, move);
1471 VectorAdd (tent->v->origin, move, end);
1472 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1473 VectorCopy (trace.endpos, tent->v->origin);
1475 if (trace.fraction < 1 && trace.ent)
1476 if (trace.ent != ignore)
1479 tossent->v->solid = savesolid;
1480 trace.fraction = 0; // not relevant