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"};
51 #define MOVE_EPSILON 0.01
53 void SV_Physics_Toss (edict_t *ent);
55 void SV_Phys_Init (void)
57 Cvar_RegisterVariable(&sv_stepheight);
58 Cvar_RegisterVariable(&sv_jumpstep);
59 Cvar_RegisterVariable(&sv_wallfriction);
67 void SV_CheckAllEnts (void)
72 // see if any solid entities are inside the final position
73 check = NEXT_EDICT(sv.edicts);
74 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
78 if (check->v->movetype == MOVETYPE_PUSH
79 || check->v->movetype == MOVETYPE_NONE
80 || check->v->movetype == MOVETYPE_FOLLOW
81 || check->v->movetype == MOVETYPE_NOCLIP)
84 if (SV_TestEntityPosition (check))
85 Con_Printf ("entity in invalid position\n");
94 void SV_CheckVelocity (edict_t *ent)
102 for (i=0 ; i<3 ; i++)
104 if (IS_NAN(ent->v->velocity[i]))
106 Con_Printf ("Got a NaN velocity on %s\n", PR_GetString(ent->v->classname));
107 ent->v->velocity[i] = 0;
109 if (IS_NAN(ent->v->origin[i]))
111 Con_Printf ("Got a NaN origin on %s\n", PR_GetString(ent->v->classname));
112 ent->v->origin[i] = 0;
116 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
117 wishspeed = DotProduct(ent->v->velocity, ent->v->velocity);
118 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
120 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
121 ent->v->velocity[0] *= wishspeed;
122 ent->v->velocity[1] *= wishspeed;
123 ent->v->velocity[2] *= wishspeed;
131 Runs thinking code if time. There is some play in the exact time the think
132 function will be called, because it is called before any movement is done
133 in a frame. Not used for pushmove objects, because they must be exact.
134 Returns false if the entity removed itself.
137 qboolean SV_RunThink (edict_t *ent)
141 thinktime = ent->v->nextthink;
142 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
145 // don't let things stay in the past.
146 // it is possible to start that way by a trigger with a local time.
147 if (thinktime < sv.time)
150 ent->v->nextthink = 0;
151 pr_global_struct->time = thinktime;
152 pr_global_struct->self = EDICT_TO_PROG(ent);
153 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
154 PR_ExecuteProgram (ent->v->think, "NULL think function");
162 Two entities have touched, so run their touch functions
165 void SV_Impact (edict_t *e1, edict_t *e2)
167 int old_self, old_other;
169 old_self = pr_global_struct->self;
170 old_other = pr_global_struct->other;
172 pr_global_struct->time = sv.time;
173 if (e1->v->touch && e1->v->solid != SOLID_NOT)
175 pr_global_struct->self = EDICT_TO_PROG(e1);
176 pr_global_struct->other = EDICT_TO_PROG(e2);
177 PR_ExecuteProgram (e1->v->touch, "");
180 if (e2->v->touch && e2->v->solid != SOLID_NOT)
182 pr_global_struct->self = EDICT_TO_PROG(e2);
183 pr_global_struct->other = EDICT_TO_PROG(e1);
184 PR_ExecuteProgram (e2->v->touch, "");
187 pr_global_struct->self = old_self;
188 pr_global_struct->other = old_other;
196 Slide off of the impacting object
197 returns the blocked flags (1 = floor, 2 = step / wall)
200 #define STOP_EPSILON 0.1
201 int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
204 float backoff, change;
207 // flag if it's a floor
210 // flag if it's a step
214 backoff = DotProduct (in, normal) * overbounce;
216 for (i = 0;i < 3;i++)
218 change = normal[i] * backoff;
219 out[i] = in[i] - change;
220 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
232 The basic solid body movement clip that slides along multiple planes
233 Returns the clipflags if the velocity was modified (hit something solid)
237 If steptrace is not NULL, the trace of any vertical wall hit will be stored
240 // LordHavoc: increased from 5 to 20
241 #define MAX_CLIP_PLANES 20
242 int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace)
244 int i, j, blocked, impact, numplanes, bumpcount, numbumps;
246 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
252 VectorCopy (ent->v->velocity, original_velocity);
253 VectorCopy (ent->v->velocity, primal_velocity);
258 for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
260 if (!ent->v->velocity[0] && !ent->v->velocity[1] && !ent->v->velocity[2])
263 for (i=0 ; i<3 ; i++)
264 end[i] = ent->v->origin[i] + time_left * ent->v->velocity[i];
266 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
270 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
271 // entity is trapped in another solid
272 VectorClear(ent->v->velocity);
276 if (trace.fraction > 0)
278 // actually covered some distance
279 VectorCopy (trace.endpos, ent->v->origin);
280 VectorCopy (ent->v->velocity, original_velocity);
284 // break if it moved the entire distance
285 if (trace.fraction == 1)
289 Host_Error ("SV_FlyMove: !trace.ent");
291 if ((int) ent->v->flags & FL_ONGROUND)
293 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
297 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
304 if (trace.plane.normal[2] > 0.7)
308 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
309 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
311 if (!trace.plane.normal[2])
315 // save the trace for player extrafriction
320 // run the impact function
323 SV_Impact (ent, trace.ent);
325 // break if removed by the impact function
331 time_left -= time_left * trace.fraction;
333 // clipped to another plane
334 if (numplanes >= MAX_CLIP_PLANES)
336 // this shouldn't really happen
337 VectorClear(ent->v->velocity);
341 VectorCopy (trace.plane.normal, planes[numplanes]);
344 // modify original_velocity so it parallels all of the clip planes
345 for (i=0 ; i<numplanes ; i++)
347 ClipVelocity (original_velocity, planes[i], new_velocity, 1);
348 for (j=0 ; j<numplanes ; j++)
352 if (DotProduct (new_velocity, planes[j]) < 0)
361 // go along this plane
362 VectorCopy (new_velocity, ent->v->velocity);
366 // go along the crease
369 VectorClear(ent->v->velocity);
372 CrossProduct (planes[0], planes[1], dir);
373 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
374 VectorNormalize(dir);
375 d = DotProduct (dir, ent->v->velocity);
376 VectorScale (dir, d, ent->v->velocity);
379 // if original velocity is against the original velocity,
380 // stop dead to avoid tiny occilations in sloping corners
381 if (DotProduct (ent->v->velocity, primal_velocity) <= 0)
383 VectorClear(ent->v->velocity);
398 void SV_AddGravity (edict_t *ent)
403 val = GETEDICTFIELDVALUE(ent, eval_gravity);
404 if (val!=0 && val->_float)
405 ent_gravity = val->_float;
408 ent->v->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
413 ===============================================================================
417 ===============================================================================
424 Does not change the entities velocity at all
427 trace_t SV_PushEntity (edict_t *ent, vec3_t push, vec3_t pushangles)
432 VectorAdd (ent->v->origin, push, end);
434 if (ent->v->movetype == MOVETYPE_FLYMISSILE)
435 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_MISSILE, ent);
436 else if (ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_NOT)
437 // only clip against bmodels
438 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NOMONSTERS, ent);
440 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
442 VectorCopy (trace.endpos, ent->v->origin);
443 // FIXME: turn players specially
444 ent->v->angles[1] += trace.fraction * pushangles[1];
445 SV_LinkEdict (ent, true);
447 if (trace.ent && (!((int)ent->v->flags & FL_ONGROUND) || ent->v->groundentity != EDICT_TO_PROG(trace.ent)))
448 SV_Impact (ent, trace.ent);
459 trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
460 void SV_PushMove (edict_t *pusher, float movetime)
464 float savesolid, movetime2, pushltime;
465 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
467 model_t *pushermodel;
470 switch ((int) pusher->v->solid)
472 // LordHavoc: valid pusher types
476 case SOLID_CORPSE: // LordHavoc: this would be weird...
478 // LordHavoc: no collisions
481 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
482 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
483 pusher->v->ltime += movetime;
484 SV_LinkEdict (pusher, false);
487 Host_Error("SV_PushMove: unrecognized solid type %f\n", pusher->v->solid);
489 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])
491 pusher->v->ltime += movetime;
494 index = (int) pusher->v->modelindex;
495 if (index < 1 || index >= MAX_MODELS)
496 Host_Error("SV_PushMove: invalid modelindex %f\n", pusher->v->modelindex);
497 pushermodel = sv.models[index];
499 movetime2 = movetime;
500 VectorScale(pusher->v->velocity, movetime2, move1);
501 VectorScale(pusher->v->avelocity, movetime2, moveangle);
502 if (moveangle[0] || moveangle[2])
504 for (i = 0;i < 3;i++)
508 mins[i] = pushermodel->rotatedmins[i] + pusher->v->origin[i] - 1;
509 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
513 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v->origin[i] - 1;
514 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v->origin[i] + 1;
518 else if (moveangle[1])
520 for (i = 0;i < 3;i++)
524 mins[i] = pushermodel->yawmins[i] + pusher->v->origin[i] - 1;
525 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
529 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v->origin[i] - 1;
530 maxs[i] = pushermodel->yawmaxs[i] + pusher->v->origin[i] + 1;
536 for (i = 0;i < 3;i++)
540 mins[i] = pushermodel->normalmins[i] + pusher->v->origin[i] - 1;
541 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
545 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v->origin[i] - 1;
546 maxs[i] = pushermodel->normalmaxs[i] + pusher->v->origin[i] + 1;
551 VectorNegate (moveangle, a);
552 AngleVectorsFLU (a, forward, left, up);
554 VectorCopy (pusher->v->origin, pushorig);
555 VectorCopy (pusher->v->angles, pushang);
556 pushltime = pusher->v->ltime;
558 // move the pusher to it's final position
560 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
561 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
562 pusher->v->ltime += movetime;
563 SV_LinkEdict (pusher, false);
565 savesolid = pusher->v->solid;
567 // see if any solid entities are inside the final position
569 check = NEXT_EDICT(sv.edicts);
570 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
574 if (check->v->movetype == MOVETYPE_PUSH
575 || check->v->movetype == MOVETYPE_NONE
576 || check->v->movetype == MOVETYPE_FOLLOW
577 || check->v->movetype == MOVETYPE_NOCLIP)
580 // if the entity is standing on the pusher, it will definitely be moved
581 if (!(((int)check->v->flags & FL_ONGROUND) && PROG_TO_EDICT(check->v->groundentity) == pusher))
583 if (check->v->absmin[0] >= maxs[0]
584 || check->v->absmax[0] <= mins[0]
585 || check->v->absmin[1] >= maxs[1]
586 || check->v->absmax[1] <= mins[1]
587 || check->v->absmin[2] >= maxs[2]
588 || check->v->absmax[2] <= mins[2])
591 trace = SV_ClipMoveToEntity (pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin);
592 if (!trace.startsolid)
596 if (forward[0] < 0.999f) // quick way to check if any rotation is used
598 VectorSubtract (check->v->origin, pusher->v->origin, org);
599 org2[0] = DotProduct (org, forward);
600 org2[1] = DotProduct (org, left);
601 org2[2] = DotProduct (org, up);
602 VectorSubtract (org2, org, move);
603 VectorAdd (move, move1, move);
606 VectorCopy (move1, move);
608 // remove the onground flag for non-players
609 if (check->v->movetype != MOVETYPE_WALK)
610 check->v->flags = (int)check->v->flags & ~FL_ONGROUND;
612 VectorCopy (check->v->origin, check->moved_from);
613 VectorCopy (check->v->angles, check->moved_fromangles);
614 sv.moved_edicts[num_moved++] = check;
616 // try moving the contacted entity
617 pusher->v->solid = SOLID_NOT;
618 trace = SV_PushEntity (check, move, moveangle);
619 pusher->v->solid = savesolid; // was SOLID_BSP
621 // if it is still inside the pusher, block
622 if (SV_TestEntityPosition (check))
624 // try moving the contacted entity a tiny bit further to account for precision errors
625 pusher->v->solid = SOLID_NOT;
626 VectorScale(move, 0.1, move);
627 trace = SV_PushEntity (check, move, vec3_origin);
628 pusher->v->solid = savesolid;
629 if (SV_TestEntityPosition (check))
631 // still inside pusher, so it's really blocked
634 if (check->v->mins[0] == check->v->maxs[0])
636 if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
639 check->v->mins[0] = check->v->mins[1] = 0;
640 VectorCopy (check->v->mins, check->v->maxs);
644 VectorCopy (pushorig, pusher->v->origin);
645 VectorCopy (pushang, pusher->v->angles);
646 pusher->v->ltime = pushltime;
647 SV_LinkEdict (pusher, false);
649 // move back any entities we already moved
650 for (i = 0;i < num_moved;i++)
652 ed = sv.moved_edicts[i];
653 VectorCopy (ed->moved_from, ed->v->origin);
654 VectorCopy (ed->moved_fromangles, ed->v->angles);
655 SV_LinkEdict (ed, false);
658 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
659 if (pusher->v->blocked)
661 pr_global_struct->self = EDICT_TO_PROG(pusher);
662 pr_global_struct->other = EDICT_TO_PROG(check);
663 PR_ExecuteProgram (pusher->v->blocked, "");
677 void SV_Physics_Pusher (edict_t *ent)
679 float thinktime, oldltime, movetime;
681 oldltime = ent->v->ltime;
683 thinktime = ent->v->nextthink;
684 if (thinktime < ent->v->ltime + sv.frametime)
686 movetime = thinktime - ent->v->ltime;
691 movetime = sv.frametime;
694 // advances ent->v->ltime if not blocked
695 SV_PushMove (ent, movetime);
697 if (thinktime > oldltime && thinktime <= ent->v->ltime)
699 ent->v->nextthink = 0;
700 pr_global_struct->time = sv.time;
701 pr_global_struct->self = EDICT_TO_PROG(ent);
702 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
703 PR_ExecuteProgram (ent->v->think, "NULL think function");
712 ===============================================================================
716 ===============================================================================
723 This is a big hack to try and fix the rare case of getting stuck in the world
727 void SV_CheckStuck (edict_t *ent)
732 if (!SV_TestEntityPosition(ent))
734 VectorCopy (ent->v->origin, ent->v->oldorigin);
738 VectorCopy (ent->v->origin, org);
739 VectorCopy (ent->v->oldorigin, ent->v->origin);
740 if (!SV_TestEntityPosition(ent))
742 Con_DPrintf ("Unstuck.\n");
743 SV_LinkEdict (ent, true);
747 for (z=0 ; z< 18 ; z++)
748 for (i=-1 ; i <= 1 ; i++)
749 for (j=-1 ; j <= 1 ; j++)
751 ent->v->origin[0] = org[0] + i;
752 ent->v->origin[1] = org[1] + j;
753 ent->v->origin[2] = org[2] + z;
754 if (!SV_TestEntityPosition(ent))
756 Con_DPrintf ("Unstuck.\n");
757 SV_LinkEdict (ent, true);
762 VectorCopy (org, ent->v->origin);
763 Con_DPrintf ("player is stuck.\n");
772 qboolean SV_CheckWater (edict_t *ent)
777 point[0] = ent->v->origin[0];
778 point[1] = ent->v->origin[1];
779 point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
781 ent->v->waterlevel = 0;
782 ent->v->watertype = CONTENTS_EMPTY;
783 cont = Mod_PointContents(point, sv.worldmodel);
784 if (cont <= CONTENTS_WATER)
786 ent->v->watertype = cont;
787 ent->v->waterlevel = 1;
788 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
789 cont = Mod_PointContents(point, sv.worldmodel);
790 if (cont <= CONTENTS_WATER)
792 ent->v->waterlevel = 2;
793 point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
794 cont = Mod_PointContents(point, sv.worldmodel);
795 if (cont <= CONTENTS_WATER)
796 ent->v->waterlevel = 3;
800 return ent->v->waterlevel > 1;
809 void SV_WallFriction (edict_t *ent, trace_t *trace)
812 vec3_t forward, into, side;
814 AngleVectors (ent->v->v_angle, forward, NULL, NULL);
815 d = DotProduct (trace->plane.normal, forward);
821 // cut the tangential velocity
822 i = DotProduct (trace->plane.normal, ent->v->velocity);
823 VectorScale (trace->plane.normal, i, into);
824 VectorSubtract (ent->v->velocity, into, side);
826 ent->v->velocity[0] = side[0] * (1 + d);
827 ent->v->velocity[1] = side[1] * (1 + d);
831 =====================
834 Player has come to a dead stop, possibly due to the problem with limited
835 float precision at some angle joins in the BSP hull.
837 Try fixing by pushing one pixel in each direction.
839 This is a hack, but in the interest of good gameplay...
840 ======================
842 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
848 VectorCopy (ent->v->origin, oldorg);
851 for (i=0 ; i<8 ; i++)
853 // try pushing a little in an axial direction
856 case 0: dir[0] = 2; dir[1] = 0; break;
857 case 1: dir[0] = 0; dir[1] = 2; break;
858 case 2: dir[0] = -2; dir[1] = 0; break;
859 case 3: dir[0] = 0; dir[1] = -2; break;
860 case 4: dir[0] = 2; dir[1] = 2; break;
861 case 5: dir[0] = -2; dir[1] = 2; break;
862 case 6: dir[0] = 2; dir[1] = -2; break;
863 case 7: dir[0] = -2; dir[1] = -2; break;
866 SV_PushEntity (ent, dir, vec3_origin);
868 // retry the original move
869 ent->v->velocity[0] = oldvel[0];
870 ent->v->velocity[1] = oldvel[1];
871 ent->v->velocity[2] = 0;
872 clip = SV_FlyMove (ent, 0.1, &steptrace);
874 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
875 || fabs(oldorg[0] - ent->v->origin[0]) > 4)
878 // go back to the original pos and try again
879 VectorCopy (oldorg, ent->v->origin);
883 VectorClear (ent->v->velocity);
888 =====================
892 ======================
894 void SV_WalkMove (edict_t *ent)
896 int clip, oldonground;
897 vec3_t upmove, downmove, oldorg, oldvel, nosteporg, nostepvel;
898 trace_t steptrace, downtrace;
900 // do a regular slide move unless it looks like you ran into a step
901 oldonground = (int)ent->v->flags & FL_ONGROUND;
902 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
904 VectorCopy (ent->v->origin, oldorg);
905 VectorCopy (ent->v->velocity, oldvel);
907 clip = SV_FlyMove (ent, sv.frametime, &steptrace);
909 // if move didn't block on a step, return
913 if (ent->v->movetype != MOVETYPE_FLY)
915 if (!oldonground && ent->v->waterlevel == 0 && !sv_jumpstep.integer)
916 // don't stair up while jumping
919 if (ent->v->movetype != MOVETYPE_WALK)
920 // gibbed by a trigger
924 if (sv_nostep.integer)
927 if ( (int)sv_player->v->flags & FL_WATERJUMP )
930 VectorCopy (ent->v->origin, nosteporg);
931 VectorCopy (ent->v->velocity, nostepvel);
933 // try moving up and forward to go up a step
935 VectorCopy (oldorg, ent->v->origin);
937 VectorClear (upmove);
938 VectorClear (downmove);
939 upmove[2] = sv_stepheight.value;
940 downmove[2] = -sv_stepheight.value + oldvel[2]*sv.frametime;
943 // FIXME: don't link?
944 SV_PushEntity (ent, upmove, vec3_origin);
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, sv.frametime, &steptrace);
951 ent->v->velocity[2] += oldvel[2];
953 // check for stuckness, possibly due to the limited precision of floats
954 // in the clipping hulls
956 && fabs(oldorg[1] - ent->v->origin[1]) < 0.03125
957 && fabs(oldorg[0] - ent->v->origin[0]) < 0.03125)
958 // stepping up didn't make any progress
959 clip = SV_TryUnstick (ent, oldvel);
961 // extra friction based on view angle
962 if (clip & 2 && sv_wallfriction.integer)
963 SV_WallFriction (ent, &steptrace);
966 // FIXME: don't link?
967 downtrace = SV_PushEntity (ent, downmove, vec3_origin);
969 if (downtrace.plane.normal[2] > 0.7)
971 // LordHavoc: disabled this so you can walk on monsters/players
972 //if (ent->v->solid == SOLID_BSP)
974 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
975 ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
980 // if the push down didn't end up on good ground, use the move without
981 // the step up. This happens near wall / slope combinations, and can
982 // cause the player to hop up higher on a slope too steep to climb
983 VectorCopy (nosteporg, ent->v->origin);
984 VectorCopy (nostepvel, ent->v->velocity);
993 Player character actions
996 void SV_Physics_Client (edict_t *ent, int num)
998 if (!svs.clients[num-1].active)
999 return; // unconnected slot
1001 // call standard client pre-think
1002 pr_global_struct->time = sv.time;
1003 pr_global_struct->self = EDICT_TO_PROG(ent);
1004 PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1007 SV_CheckVelocity (ent);
1009 // decide which move function to call
1010 switch ((int)ent->v->movetype)
1013 if (!SV_RunThink (ent))
1018 if (!SV_RunThink (ent))
1020 if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1021 SV_AddGravity (ent);
1022 SV_CheckStuck (ent);
1027 case MOVETYPE_BOUNCE:
1028 SV_Physics_Toss (ent);
1032 if (!SV_RunThink (ent))
1034 SV_CheckWater (ent);
1035 //SV_FlyMove (ent, sv.frametime, NULL);
1039 case MOVETYPE_NOCLIP:
1040 if (!SV_RunThink (ent))
1042 SV_CheckWater (ent);
1043 VectorMA (ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1044 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1048 Host_Error ("SV_Physics_client: bad movetype %i", (int)ent->v->movetype);
1051 // call standard player post-think
1052 SV_LinkEdict (ent, true);
1054 pr_global_struct->time = sv.time;
1055 pr_global_struct->self = EDICT_TO_PROG(ent);
1056 PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1059 //============================================================================
1065 Entities that are "stuck" to another entity
1068 void SV_Physics_Follow (edict_t *ent)
1070 vec3_t vf, vr, vu, angles, v;
1074 if (!SV_RunThink (ent))
1077 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1078 e = PROG_TO_EDICT(ent->v->aiment);
1079 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])
1081 // quick case for no rotation
1082 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1086 angles[0] = -ent->v->punchangle[0];
1087 angles[1] = ent->v->punchangle[1];
1088 angles[2] = ent->v->punchangle[2];
1089 AngleVectors (angles, vf, vr, vu);
1090 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1091 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1092 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1093 angles[0] = -e->v->angles[0];
1094 angles[1] = e->v->angles[1];
1095 angles[2] = e->v->angles[2];
1096 AngleVectors (angles, vf, vr, vu);
1097 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1098 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1099 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1101 VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1102 SV_LinkEdict (ent, true);
1109 A moving object that doesn't obey physics
1112 void SV_Physics_Noclip (edict_t *ent)
1115 if (!SV_RunThink (ent))
1118 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1119 VectorMA (ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1121 SV_LinkEdict (ent, false);
1125 ==============================================================================
1129 ==============================================================================
1134 SV_CheckWaterTransition
1138 void SV_CheckWaterTransition (edict_t *ent)
1141 cont = Mod_PointContents(ent->v->origin, sv.worldmodel);
1142 if (!ent->v->watertype)
1144 // just spawned here
1145 ent->v->watertype = cont;
1146 ent->v->waterlevel = 1;
1150 if (cont <= CONTENTS_WATER)
1152 if (ent->v->watertype == CONTENTS_EMPTY && cont != CONTENTS_LAVA)
1153 // just crossed into water
1154 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1156 ent->v->watertype = cont;
1157 ent->v->waterlevel = 1;
1161 if (ent->v->watertype != CONTENTS_EMPTY && ent->v->watertype != CONTENTS_LAVA)
1162 // just crossed into water
1163 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1165 ent->v->watertype = CONTENTS_EMPTY;
1166 ent->v->waterlevel = cont;
1174 Toss, bounce, and fly movement. When onground, do nothing.
1177 void SV_Physics_Toss (edict_t *ent)
1181 edict_t *groundentity;
1184 if (!SV_RunThink (ent))
1187 // if onground, return without moving
1188 if ((int)ent->v->flags & FL_ONGROUND)
1190 VectorClear(ent->v->velocity);
1191 if (ent->v->groundentity == 0)
1193 // if ent was supported by a brush model on previous frame,
1194 // and groundentity is now freed, set groundentity to 0 (floating)
1195 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1196 if (groundentity->v->solid == SOLID_BSP)
1198 ent->suspendedinairflag = true;
1201 else if (ent->suspendedinairflag && groundentity->free)
1203 // leave it suspended in the air
1204 ent->v->groundentity = 0;
1205 ent->suspendedinairflag = false;
1209 ent->suspendedinairflag = false;
1211 SV_CheckVelocity (ent);
1214 if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1215 SV_AddGravity (ent);
1218 VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1221 VectorScale (ent->v->velocity, sv.frametime, move);
1222 trace = SV_PushEntity (ent, move, vec3_origin);
1226 if (trace.fraction < 1)
1228 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1230 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1231 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1233 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1235 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1236 // LordHavoc: fixed grenades not bouncing when fired down a slope
1237 if (trace.plane.normal[2] > 0.7 && DotProduct(trace.plane.normal, ent->v->velocity) < 60)
1239 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1240 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1241 VectorClear (ent->v->velocity);
1242 VectorClear (ent->v->avelocity);
1245 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1249 ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1250 if (trace.plane.normal[2] > 0.7)
1252 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1253 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1254 VectorClear (ent->v->velocity);
1255 VectorClear (ent->v->avelocity);
1258 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1262 // check for in water
1263 SV_CheckWaterTransition (ent);
1267 ===============================================================================
1271 ===============================================================================
1278 Monsters freefall when they don't have a ground entity, otherwise
1279 all movement is done with discrete steps.
1281 This is also used for objects that have become still on the ground, but
1282 will fall if the floor is pulled out from under them.
1285 void SV_Physics_Step (edict_t *ent)
1287 int flags, fall, hitsound;
1289 // freefall if not fly/swim
1291 flags = (int)ent->v->flags;
1292 if (flags & (FL_FLY | FL_SWIM))
1296 else if ((flags & FL_SWIM) && Mod_PointContents(ent->v->origin, sv.worldmodel) != CONTENTS_EMPTY)
1299 if (fall && (flags & FL_ONGROUND) && ent->v->groundentity == 0)
1304 if (ent->v->velocity[2] < sv_gravity.value*-0.1)
1307 if (flags & FL_ONGROUND)
1313 SV_AddGravity (ent);
1314 SV_CheckVelocity (ent);
1315 SV_FlyMove (ent, sv.frametime, NULL);
1316 SV_LinkEdict (ent, false);
1319 if ((int)ent->v->flags & FL_ONGROUND)
1321 VectorClear(ent->v->velocity);
1323 SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1);
1330 SV_CheckWaterTransition (ent);
1333 //============================================================================
1341 void SV_Physics (void)
1346 // let the progs know that a new frame has started
1347 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1348 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1349 pr_global_struct->time = sv.time;
1350 PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1353 // treat each object in turn
1356 for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1361 if (pr_global_struct->force_retouch)
1362 SV_LinkEdict (ent, true); // force retouch even for stationary
1364 if (i > 0 && i <= svs.maxclients)
1366 SV_Physics_Client (ent, i);
1370 switch ((int) ent->v->movetype)
1373 SV_Physics_Pusher (ent);
1376 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1377 if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime)
1380 case MOVETYPE_FOLLOW:
1381 SV_Physics_Follow (ent);
1383 case MOVETYPE_NOCLIP:
1384 SV_Physics_Noclip (ent);
1387 SV_Physics_Step (ent);
1389 // LordHavoc: added support for MOVETYPE_WALK on normal entities! :)
1391 if (SV_RunThink (ent))
1393 if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1394 SV_AddGravity (ent);
1395 SV_CheckStuck (ent);
1397 SV_LinkEdict (ent, true);
1401 case MOVETYPE_BOUNCE:
1402 case MOVETYPE_BOUNCEMISSILE:
1404 case MOVETYPE_FLYMISSILE:
1405 SV_Physics_Toss (ent);
1408 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1413 if (pr_global_struct->force_retouch)
1414 pr_global_struct->force_retouch--;
1416 // LordHavoc: endframe support
1419 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1420 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1421 pr_global_struct->time = sv.time;
1422 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "");
1425 sv.time += sv.frametime;
1429 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1432 float gravity, savesolid;
1434 edict_t tempent, *tent;
1439 // copy the vars over
1440 memcpy(&vars, tossent->v, sizeof(entvars_t));
1441 // set up the temp entity to point to the copied vars
1445 savesolid = tossent->v->solid;
1446 tossent->v->solid = SOLID_NOT;
1448 // this has to fetch the field from the original edict, since our copy is truncated
1449 val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1450 if (val != NULL && val->_float != 0)
1451 gravity = val->_float;
1454 gravity *= sv_gravity.value * 0.05;
1456 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1458 SV_CheckVelocity (tent);
1459 tent->v->velocity[2] -= gravity;
1460 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1461 VectorScale (tent->v->velocity, 0.05, move);
1462 VectorAdd (tent->v->origin, move, end);
1463 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1464 VectorCopy (trace.endpos, tent->v->origin);
1466 if (trace.fraction < 1 && trace.ent)
1467 if (trace.ent != ignore)
1470 tossent->v->solid = savesolid;
1471 trace.fraction = 0; // not relevant