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 = {0, "sv_stopspeed","100"};
44 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800"};
45 cvar_t sv_maxvelocity = {0, "sv_maxvelocity","2000"};
46 cvar_t sv_nostep = {0, "sv_nostep","0"};
48 #define MOVE_EPSILON 0.01
50 void SV_Physics_Toss (edict_t *ent);
57 void SV_CheckAllEnts (void)
62 // see if any solid entities are inside the final position
63 check = NEXT_EDICT(sv.edicts);
64 for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
68 if (check->v.movetype == MOVETYPE_PUSH
69 || check->v.movetype == MOVETYPE_NONE
70 || check->v.movetype == MOVETYPE_FOLLOW
71 || check->v.movetype == MOVETYPE_NOCLIP)
74 if (SV_TestEntityPosition (check))
75 Con_Printf ("entity in invalid position\n");
84 void SV_CheckVelocity (edict_t *ent)
94 if (IS_NAN(ent->v.velocity[i]))
96 Con_Printf ("Got a NaN velocity on %s\n", pr_strings + ent->v.classname);
97 ent->v.velocity[i] = 0;
99 if (IS_NAN(ent->v.origin[i]))
101 Con_Printf ("Got a NaN origin on %s\n", pr_strings + ent->v.classname);
102 ent->v.origin[i] = 0;
104 // LordHavoc: maxvelocity fix, see below
106 if (ent->v.velocity[i] > sv_maxvelocity.value)
107 ent->v.velocity[i] = sv_maxvelocity.value;
108 else if (ent->v.velocity[i] < -sv_maxvelocity.value)
109 ent->v.velocity[i] = -sv_maxvelocity.value;
113 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
114 wishspeed = DotProduct(ent->v.velocity, ent->v.velocity);
115 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
117 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
118 ent->v.velocity[0] *= wishspeed;
119 ent->v.velocity[1] *= wishspeed;
120 ent->v.velocity[2] *= wishspeed;
121 wishspeed = sv_maxvelocity.value;
129 Runs thinking code if time. There is some play in the exact time the think
130 function will be called, because it is called before any movement is done
131 in a frame. Not used for pushmove objects, because they must be exact.
132 Returns false if the entity removed itself.
135 qboolean SV_RunThink (edict_t *ent)
139 thinktime = ent->v.nextthink;
140 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
143 if (thinktime < sv.time)
144 thinktime = sv.time; // don't let things stay in the past.
145 // it is possible to start that way
146 // by a trigger with a local time.
147 ent->v.nextthink = 0;
148 pr_global_struct->time = thinktime;
149 pr_global_struct->self = EDICT_TO_PROG(ent);
150 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
151 PR_ExecuteProgram (ent->v.think, "NULL think function");
159 Two entities have touched, so run their touch functions
162 void SV_Impact (edict_t *e1, edict_t *e2)
164 int old_self, old_other;
166 old_self = pr_global_struct->self;
167 old_other = pr_global_struct->other;
169 pr_global_struct->time = sv.time;
170 if (e1->v.touch && e1->v.solid != SOLID_NOT)
172 pr_global_struct->self = EDICT_TO_PROG(e1);
173 pr_global_struct->other = EDICT_TO_PROG(e2);
174 PR_ExecuteProgram (e1->v.touch, "");
177 if (e2->v.touch && e2->v.solid != SOLID_NOT)
179 pr_global_struct->self = EDICT_TO_PROG(e2);
180 pr_global_struct->other = EDICT_TO_PROG(e1);
181 PR_ExecuteProgram (e2->v.touch, "");
184 pr_global_struct->self = old_self;
185 pr_global_struct->other = old_other;
193 Slide off of the impacting object
194 returns the blocked flags (1 = floor, 2 = step / wall)
197 #define STOP_EPSILON 0.1
199 int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
207 blocked |= 1; // floor
209 blocked |= 2; // step
211 backoff = DotProduct (in, normal) * overbounce;
213 for (i=0 ; i<3 ; i++)
215 change = normal[i]*backoff;
216 out[i] = in[i] - change;
217 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
229 The basic solid body movement clip that slides along multiple planes
230 Returns the clipflags if the velocity was modified (hit something solid)
234 If steptrace is not NULL, the trace of any vertical wall hit will be stored
237 // LordHavoc: increased from 5 to 20, to partially fix angled corner sticking
238 // (example - start.bsp hall to e1m4, leading to the pool there are two
239 // angled corners, which you could get stuck on, now they are just a one
241 #define MAX_CLIP_PLANES 20
242 int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace)
244 int bumpcount, numbumps;
248 vec3_t planes[MAX_CLIP_PLANES];
249 vec3_t primal_velocity, original_velocity, new_velocity;
259 VectorCopy (ent->v.velocity, original_velocity);
260 VectorCopy (ent->v.velocity, primal_velocity);
265 for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
267 if (!ent->v.velocity[0] && !ent->v.velocity[1] && !ent->v.velocity[2])
270 for (i=0 ; i<3 ; i++)
271 end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i];
273 trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent);
276 { // entity is trapped in another solid
277 VectorClear(ent->v.velocity);
281 if (trace.fraction > 0)
282 { // actually covered some distance
283 VectorCopy (trace.endpos, ent->v.origin);
284 VectorCopy (ent->v.velocity, original_velocity);
288 if (trace.fraction == 1)
289 break; // moved the entire distance
292 Host_Error ("SV_FlyMove: !trace.ent");
294 if (trace.plane.normal[2] > 0.7)
296 blocked |= 1; // floor
297 if (trace.ent->v.solid == SOLID_BSP)
299 ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
300 ent->v.groundentity = EDICT_TO_PROG(trace.ent);
303 if (!trace.plane.normal[2])
305 blocked |= 2; // step
307 *steptrace = trace; // save for player extrafriction
311 // run the impact function
313 SV_Impact (ent, trace.ent);
315 break; // removed by the impact function
318 time_left -= time_left * trace.fraction;
320 // cliped to another plane
321 if (numplanes >= MAX_CLIP_PLANES)
322 { // this shouldn't really happen
323 VectorClear(ent->v.velocity);
327 VectorCopy (trace.plane.normal, planes[numplanes]);
331 // modify original_velocity so it parallels all of the clip planes
333 for (i=0 ; i<numplanes ; i++)
335 ClipVelocity (original_velocity, planes[i], new_velocity, 1);
336 for (j=0 ; j<numplanes ; j++)
339 if (DotProduct (new_velocity, planes[j]) < 0)
347 { // go along this plane
348 VectorCopy (new_velocity, ent->v.velocity);
351 { // go along the crease
354 // Con_Printf ("clip velocity, numplanes == %i\n",numplanes);
355 VectorClear(ent->v.velocity);
358 CrossProduct (planes[0], planes[1], dir);
359 d = DotProduct (dir, ent->v.velocity);
360 VectorScale (dir, d, ent->v.velocity);
364 // if original velocity is against the original velocity, stop dead
365 // to avoid tiny occilations in sloping corners
367 if (DotProduct (ent->v.velocity, primal_velocity) <= 0)
369 VectorClear(ent->v.velocity);
384 void SV_AddGravity (edict_t *ent)
390 val = GETEDICTFIELDVALUE(ent, eval_gravity);
391 if (val!=0 && val->_float)
392 ent_gravity = val->_float;
395 ent->v.velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
400 ===============================================================================
404 ===============================================================================
411 Does not change the entities velocity at all
414 trace_t SV_PushEntity (edict_t *ent, vec3_t push)
419 VectorAdd (ent->v.origin, push, end);
421 if (ent->v.movetype == MOVETYPE_FLYMISSILE)
422 trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent);
423 else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT)
424 // only clip against bmodels
425 trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent);
427 trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent);
429 VectorCopy (trace.endpos, ent->v.origin);
430 SV_LinkEdict (ent, true);
433 SV_Impact (ent, trace.ent);
445 void SV_PushMove (edict_t *pusher, float movetime)
449 vec3_t mins, maxs, move;
450 vec3_t entorig, pushorig;
452 edict_t *moved_edict[MAX_EDICTS];
453 vec3_t moved_from[MAX_EDICTS];
456 switch ((int) pusher->v.solid)
458 // LordHavoc: valid pusher types
462 case SOLID_CORPSE: // LordHavoc: this would be weird...
464 // LordHavoc: no collisions
467 VectorMA (pusher->v.origin, movetime, pusher->v.velocity, pusher->v.origin);
468 pusher->v.ltime += movetime;
469 SV_LinkEdict (pusher, false);
472 Host_Error("SV_PushMove: unrecognized solid type %f\n", pusher->v.solid);
474 if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2])
476 pusher->v.ltime += movetime;
480 for (i=0 ; i<3 ; i++)
482 move[i] = pusher->v.velocity[i] * movetime;
483 mins[i] = pusher->v.absmin[i] + move[i];
484 maxs[i] = pusher->v.absmax[i] + move[i];
487 VectorCopy (pusher->v.origin, pushorig);
489 // move the pusher to it's final position
491 VectorAdd (pusher->v.origin, move, pusher->v.origin);
492 pusher->v.ltime += movetime;
493 SV_LinkEdict (pusher, false);
496 // see if any solid entities are inside the final position
498 check = NEXT_EDICT(sv.edicts);
499 for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
503 if (check->v.movetype == MOVETYPE_PUSH
504 || check->v.movetype == MOVETYPE_NONE
505 || check->v.movetype == MOVETYPE_FOLLOW
506 || check->v.movetype == MOVETYPE_NOCLIP)
509 // if the entity is standing on the pusher, it will definitely be moved
510 if (!(((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher))
512 if (check->v.absmin[0] >= maxs[0]
513 || check->v.absmin[1] >= maxs[1]
514 || check->v.absmin[2] >= maxs[2]
515 || check->v.absmax[0] <= mins[0]
516 || check->v.absmax[1] <= mins[1]
517 || check->v.absmax[2] <= mins[2])
520 // see if the ent's bbox is inside the pusher's final position
521 if (!SV_TestEntityPosition (check))
525 // remove the onground flag for non-players
526 if (check->v.movetype != MOVETYPE_WALK)
527 check->v.flags = (int)check->v.flags & ~FL_ONGROUND;
529 VectorCopy (check->v.origin, entorig);
530 VectorCopy (check->v.origin, moved_from[num_moved]);
531 moved_edict[num_moved] = check;
534 // LordHavoc: pusher fixes (teleport train bug, etc)
535 savesolid = pusher->v.solid;
536 if (savesolid == SOLID_BSP || savesolid == SOLID_BBOX || savesolid == SOLID_SLIDEBOX)
538 // try moving the contacted entity
539 pusher->v.solid = SOLID_NOT;
540 SV_PushEntity (check, move);
541 pusher->v.solid = savesolid; // was SOLID_BSP
543 // if it is still inside the pusher, block
544 if (SV_TestEntityPosition (check))
546 if (check->v.mins[0] == check->v.maxs[0])
548 if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
550 check->v.mins[0] = check->v.mins[1] = 0;
551 VectorCopy (check->v.mins, check->v.maxs);
555 VectorCopy (entorig, check->v.origin);
556 SV_LinkEdict (check, true);
558 VectorCopy (pushorig, pusher->v.origin);
559 SV_LinkEdict (pusher, false);
560 pusher->v.ltime -= movetime;
562 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
563 if (pusher->v.blocked)
565 pr_global_struct->self = EDICT_TO_PROG(pusher);
566 pr_global_struct->other = EDICT_TO_PROG(check);
567 PR_ExecuteProgram (pusher->v.blocked, "");
570 // move back any entities we already moved
571 num_moved--; // LordHavoc: pop off check, because it was already restored
572 for (i=0 ; i<num_moved ; i++)
574 VectorCopy (moved_from[i], moved_edict[i]->v.origin);
575 SV_LinkEdict (moved_edict[i], false);
591 void SV_PushRotate (edict_t *pusher, float movetime)
595 vec3_t move, a, amove;
596 vec3_t entorigin, entangles, pushorigin, pushangles;
598 edict_t *moved_edict[MAX_EDICTS];
599 vec3_t moved_from[MAX_EDICTS];
600 vec3_t angled_from[MAX_EDICTS];
602 vec3_t forward, right, up;
605 switch ((int) pusher->v.solid)
607 // LordHavoc: valid pusher types
611 case SOLID_CORPSE: // LordHavoc: this would be weird...
613 // LordHavoc: no collisions
616 VectorMA (pusher->v.angles, movetime, pusher->v.avelocity, pusher->v.angles);
617 pusher->v.ltime += movetime;
618 SV_LinkEdict (pusher, false);
621 Host_Error("SV_PushRotate: unrecognized solid type %f\n", pusher->v.solid);
623 if (!pusher->v.avelocity[0] && !pusher->v.avelocity[1] && !pusher->v.avelocity[2])
625 pusher->v.ltime += movetime;
629 for (i=0 ; i<3 ; i++)
630 amove[i] = pusher->v.avelocity[i] * movetime;
632 VectorNegate (amove, a);
633 AngleVectors (a, forward, right, up);
635 VectorCopy (pusher->v.origin, pushorigin);
636 VectorCopy (pusher->v.angles, pushangles);
638 // move the pusher to it's final position
640 VectorAdd (pusher->v.angles, amove, pusher->v.angles);
641 pusher->v.ltime += movetime;
642 SV_LinkEdict (pusher, false);
645 // see if any solid entities are inside the final position
647 check = NEXT_EDICT(sv.edicts);
648 for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
652 if (check->v.movetype == MOVETYPE_PUSH
653 || check->v.movetype == MOVETYPE_NONE
654 || check->v.movetype == MOVETYPE_FOLLOW
655 || check->v.movetype == MOVETYPE_NOCLIP)
658 // if the entity is standing on the pusher, it will definately be moved
659 if (!(((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher))
661 if (check->v.absmin[0] >= pusher->v.absmax[0]
662 || check->v.absmin[1] >= pusher->v.absmax[1]
663 || check->v.absmin[2] >= pusher->v.absmax[2]
664 || check->v.absmax[0] <= pusher->v.absmin[0]
665 || check->v.absmax[1] <= pusher->v.absmin[1]
666 || check->v.absmax[2] <= pusher->v.absmin[2])
669 // see if the ent's bbox is inside the pusher's final position
670 if (!SV_TestEntityPosition (check))
674 // remove the onground flag for non-players
675 if (check->v.movetype != MOVETYPE_WALK)
676 check->v.flags = (int)check->v.flags & ~FL_ONGROUND;
678 VectorCopy (check->v.origin, entorigin);
679 VectorCopy (check->v.origin, moved_from[num_moved]);
680 VectorCopy (check->v.angles, entangles);
681 VectorCopy (check->v.angles, angled_from[num_moved]);
682 moved_edict[num_moved] = check;
685 // calculate destination position
686 VectorSubtract (check->v.origin, pusher->v.origin, org);
687 org2[0] = DotProduct (org, forward);
688 org2[1] = -DotProduct (org, right);
689 org2[2] = DotProduct (org, up);
690 VectorSubtract (org2, org, move);
692 // try moving the contacted entity
693 savesolid = pusher->v.solid; // LordHavoc: restore to correct solid type
694 pusher->v.solid = SOLID_NOT;
695 SV_PushEntity (check, move);
696 pusher->v.solid = savesolid; // LordHavoc: restore to correct solid type
698 VectorAdd (check->v.angles, amove, check->v.angles);
700 // if it is still inside the pusher, block
701 if (SV_TestEntityPosition (check))
703 if (check->v.mins[0] == check->v.maxs[0])
705 if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
707 check->v.mins[0] = check->v.mins[1] = 0;
708 VectorCopy (check->v.mins, check->v.maxs);
712 VectorCopy (entorigin, check->v.origin);
713 VectorCopy (entangles, check->v.angles);
714 SV_LinkEdict (check, true);
716 VectorCopy (pushorigin, pusher->v.origin);
717 VectorCopy (pushangles, pusher->v.angles);
718 SV_LinkEdict (pusher, false);
719 pusher->v.ltime -= movetime;
721 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
722 if (pusher->v.blocked)
724 pr_global_struct->self = EDICT_TO_PROG(pusher);
725 pr_global_struct->other = EDICT_TO_PROG(check);
726 PR_ExecuteProgram (pusher->v.blocked, "");
729 // move back any entities we already moved
730 num_moved--; // LordHavoc: pop off check, because it was already restored
731 for (i=0 ; i<num_moved ; i++)
733 VectorCopy (moved_from[i], moved_edict[i]->v.origin);
734 VectorCopy (angled_from[i], moved_edict[i]->v.angles);
735 SV_LinkEdict (moved_edict[i], false);
748 void SV_Physics_Pusher (edict_t *ent)
754 oldltime = ent->v.ltime;
756 thinktime = ent->v.nextthink;
757 if (thinktime < ent->v.ltime + sv.frametime)
759 movetime = thinktime - ent->v.ltime;
764 movetime = sv.frametime;
768 if (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2])
769 SV_PushRotate (ent, movetime);
771 SV_PushMove (ent, movetime); // advances ent->v.ltime if not blocked
774 if (thinktime > oldltime && thinktime <= ent->v.ltime)
776 ent->v.nextthink = 0;
777 pr_global_struct->time = sv.time;
778 pr_global_struct->self = EDICT_TO_PROG(ent);
779 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
780 PR_ExecuteProgram (ent->v.think, "NULL think function");
789 ===============================================================================
793 ===============================================================================
800 This is a big hack to try and fix the rare case of getting stuck in the world
804 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_PointContents (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_PointContents (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_PointContents (point);
873 if (cont <= CONTENTS_WATER)
874 ent->v.waterlevel = 3;
878 return ent->v.waterlevel > 1;
887 void SV_WallFriction (edict_t *ent, trace_t *trace)
893 AngleVectors (ent->v.v_angle, forward, NULL, NULL);
894 d = DotProduct (trace->plane.normal, forward);
900 // cut the tangential velocity
901 i = DotProduct (trace->plane.normal, ent->v.velocity);
902 VectorScale (trace->plane.normal, i, into);
903 VectorSubtract (ent->v.velocity, into, side);
905 ent->v.velocity[0] = side[0] * (1 + d);
906 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)
929 VectorCopy (ent->v.origin, oldorg);
932 for (i=0 ; i<8 ; i++)
934 // try pushing a little in an axial direction
937 case 0: dir[0] = 2; dir[1] = 0; break;
938 case 1: dir[0] = 0; dir[1] = 2; break;
939 case 2: dir[0] = -2; dir[1] = 0; break;
940 case 3: dir[0] = 0; dir[1] = -2; break;
941 case 4: dir[0] = 2; dir[1] = 2; break;
942 case 5: dir[0] = -2; dir[1] = 2; break;
943 case 6: dir[0] = 2; dir[1] = -2; break;
944 case 7: dir[0] = -2; dir[1] = -2; break;
947 SV_PushEntity (ent, dir);
949 // retry the original move
950 ent->v.velocity[0] = oldvel[0];
951 ent->v.velocity[1] = oldvel[1];
952 ent->v.velocity[2] = 0;
953 clip = SV_FlyMove (ent, 0.1, &steptrace);
955 if ( fabs(oldorg[1] - ent->v.origin[1]) > 4
956 || fabs(oldorg[0] - ent->v.origin[0]) > 4 )
958 //Con_DPrintf ("unstuck!\n");
962 // go back to the original pos and try again
963 VectorCopy (oldorg, ent->v.origin);
966 VectorClear (ent->v.velocity);
967 return 7; // still not moving
971 =====================
975 ======================
978 void SV_WalkMove (edict_t *ent)
980 vec3_t upmove, downmove;
981 vec3_t oldorg, oldvel;
982 vec3_t nosteporg, nostepvel;
985 trace_t steptrace, downtrace;
988 // do a regular slide move unless it looks like you ran into a step
990 oldonground = (int)ent->v.flags & FL_ONGROUND;
991 ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
993 VectorCopy (ent->v.origin, oldorg);
994 VectorCopy (ent->v.velocity, oldvel);
996 clip = SV_FlyMove (ent, sv.frametime, &steptrace);
999 return; // move didn't block on a step
1001 if (!oldonground && ent->v.waterlevel == 0)
1002 return; // don't stair up while jumping
1004 if (ent->v.movetype != MOVETYPE_WALK)
1005 return; // gibbed by a trigger
1007 if (sv_nostep.value)
1010 if ( (int)sv_player->v.flags & FL_WATERJUMP )
1013 VectorCopy (ent->v.origin, nosteporg);
1014 VectorCopy (ent->v.velocity, nostepvel);
1017 // try moving up and forward to go up a step
1019 VectorCopy (oldorg, ent->v.origin); // back to start pos
1021 VectorClear (upmove);
1022 VectorClear (downmove);
1023 upmove[2] = STEPSIZE;
1024 downmove[2] = -STEPSIZE + oldvel[2]*sv.frametime;
1027 SV_PushEntity (ent, upmove); // FIXME: don't link?
1030 ent->v.velocity[0] = oldvel[0];
1031 ent->v. velocity[1] = oldvel[1];
1032 ent->v. velocity[2] = 0;
1033 clip = SV_FlyMove (ent, sv.frametime, &steptrace);
1035 // check for stuckness, possibly due to the limited precision of floats
1036 // in the clipping hulls
1039 if ( fabs(oldorg[1] - ent->v.origin[1]) < 0.03125
1040 && fabs(oldorg[0] - ent->v.origin[0]) < 0.03125 )
1041 { // stepping up didn't make any progress
1042 clip = SV_TryUnstick (ent, oldvel);
1046 // extra friction based on view angle
1048 SV_WallFriction (ent, &steptrace);
1051 downtrace = SV_PushEntity (ent, downmove); // FIXME: don't link?
1053 if (downtrace.plane.normal[2] > 0.7)
1055 if (ent->v.solid == SOLID_BSP)
1057 ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
1058 ent->v.groundentity = EDICT_TO_PROG(downtrace.ent);
1063 // if the push down didn't end up on good ground, use the move without
1064 // the step up. This happens near wall / slope combinations, and can
1065 // cause the player to hop up higher on a slope too steep to climb
1066 VectorCopy (nosteporg, ent->v.origin);
1067 VectorCopy (nostepvel, ent->v.velocity);
1076 Player character actions
1079 void SV_Physics_Client (edict_t *ent, int num)
1081 if ( ! svs.clients[num-1].active )
1082 return; // unconnected slot
1085 // call standard client pre-think
1087 pr_global_struct->time = sv.time;
1088 pr_global_struct->self = EDICT_TO_PROG(ent);
1089 PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1094 SV_CheckVelocity (ent);
1097 // decide which move function to call
1099 switch ((int)ent->v.movetype)
1102 if (!SV_RunThink (ent))
1107 if (!SV_RunThink (ent))
1109 if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) )
1110 SV_AddGravity (ent);
1111 SV_CheckStuck (ent);
1116 case MOVETYPE_BOUNCE:
1117 SV_Physics_Toss (ent);
1121 if (!SV_RunThink (ent))
1123 SV_CheckWater (ent);
1124 SV_FlyMove (ent, sv.frametime, NULL);
1127 case MOVETYPE_NOCLIP:
1128 if (!SV_RunThink (ent))
1130 SV_CheckWater (ent);
1131 VectorMA (ent->v.origin, sv.frametime, ent->v.velocity, ent->v.origin);
1135 Host_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype);
1139 // call standard player post-think
1141 SV_LinkEdict (ent, true);
1143 pr_global_struct->time = sv.time;
1144 pr_global_struct->self = EDICT_TO_PROG(ent);
1145 PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1148 //============================================================================
1154 Non moving objects can only think
1157 // LordHavoc: inlined manually because it was a real time waster
1159 void SV_Physics_None (edict_t *ent)
1170 Entities that are "stuck" to another entity
1173 void SV_Physics_Follow (edict_t *ent)
1175 vec3_t vf, vr, vu, angles, v;
1178 if (!SV_RunThink (ent))
1180 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1181 e = PROG_TO_EDICT(ent->v.aiment);
1182 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])
1184 // quick case for no rotation
1185 VectorAdd(e->v.origin, ent->v.view_ofs, ent->v.origin);
1189 angles[0] = -ent->v.punchangle[0];
1190 angles[1] = ent->v.punchangle[1];
1191 angles[2] = ent->v.punchangle[2];
1192 AngleVectors (angles, vf, vr, vu);
1193 v[0] = ent->v.view_ofs[0] * vf[0] + ent->v.view_ofs[1] * vr[0] + ent->v.view_ofs[2] * vu[0];
1194 v[1] = ent->v.view_ofs[0] * vf[1] + ent->v.view_ofs[1] * vr[1] + ent->v.view_ofs[2] * vu[1];
1195 v[2] = ent->v.view_ofs[0] * vf[2] + ent->v.view_ofs[1] * vr[2] + ent->v.view_ofs[2] * vu[2];
1196 angles[0] = -e->v.angles[0];
1197 angles[1] = e->v.angles[1];
1198 angles[2] = e->v.angles[2];
1199 AngleVectors (angles, vf, vr, vu);
1200 ent->v.origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v.origin[0];
1201 ent->v.origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v.origin[1];
1202 ent->v.origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v.origin[2];
1204 ent->v.origin[0] = ent->v.view_ofs[0] * vf[0] + ent->v.view_ofs[0] * vf[1] + ent->v.view_ofs[0] * vf[2] + e->v.origin[0];
1205 ent->v.origin[1] = ent->v.view_ofs[1] * vr[0] + ent->v.view_ofs[1] * vr[1] + ent->v.view_ofs[1] * vr[2] + e->v.origin[1];
1206 ent->v.origin[2] = ent->v.view_ofs[2] * vu[0] + ent->v.view_ofs[2] * vu[1] + ent->v.view_ofs[2] * vu[2] + e->v.origin[2];
1209 VectorAdd (e->v.angles, ent->v.v_angle, ent->v.angles);
1210 // VectorAdd (PROG_TO_EDICT(ent->v.aiment)->v.origin, ent->v.v_angle, ent->v.origin);
1211 SV_LinkEdict (ent, true);
1218 A moving object that doesn't obey physics
1221 void SV_Physics_Noclip (edict_t *ent)
1224 if (!SV_RunThink (ent))
1227 VectorMA (ent->v.angles, sv.frametime, ent->v.avelocity, ent->v.angles);
1228 VectorMA (ent->v.origin, sv.frametime, ent->v.velocity, ent->v.origin);
1230 SV_LinkEdict (ent, false);
1234 ==============================================================================
1238 ==============================================================================
1243 SV_CheckWaterTransition
1247 void SV_CheckWaterTransition (edict_t *ent)
1250 cont = SV_PointContents (ent->v.origin);
1251 if (!ent->v.watertype)
1252 { // just spawned here
1253 ent->v.watertype = cont;
1254 ent->v.waterlevel = 1;
1258 if (cont <= CONTENTS_WATER)
1260 if (ent->v.watertype == CONTENTS_EMPTY)
1261 { // just crossed into water
1262 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1264 ent->v.watertype = cont;
1265 ent->v.waterlevel = 1;
1269 if (ent->v.watertype != CONTENTS_EMPTY)
1270 { // just crossed into water
1271 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1273 ent->v.watertype = CONTENTS_EMPTY;
1274 ent->v.waterlevel = cont;
1282 Toss, bounce, and fly movement. When onground, do nothing.
1285 void SV_Physics_Toss (edict_t *ent)
1291 if (!SV_RunThink (ent))
1294 // if onground, return without moving
1295 if ( ((int)ent->v.flags & FL_ONGROUND) )
1298 SV_CheckVelocity (ent);
1301 if (ent->v.movetype != MOVETYPE_FLY
1302 && ent->v.movetype != MOVETYPE_BOUNCEMISSILE // LordHavoc: enabled MOVETYPE_BOUNCEMISSILE
1303 && ent->v.movetype != MOVETYPE_FLYMISSILE)
1304 SV_AddGravity (ent);
1307 VectorMA (ent->v.angles, sv.frametime, ent->v.avelocity, ent->v.angles);
1310 VectorScale (ent->v.velocity, sv.frametime, move);
1311 trace = SV_PushEntity (ent, move);
1312 if (trace.fraction == 1)
1317 if (ent->v.movetype == MOVETYPE_BOUNCE)
1319 else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE)
1324 ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff);
1326 // stop if on ground
1327 if (trace.plane.normal[2] > 0.7)
1329 if (ent->v.velocity[2] < 60 || (ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE))
1331 ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
1332 ent->v.groundentity = EDICT_TO_PROG(trace.ent);
1333 VectorClear (ent->v.velocity);
1334 VectorClear (ent->v.avelocity);
1338 // check for in water
1339 SV_CheckWaterTransition (ent);
1343 ===============================================================================
1347 ===============================================================================
1354 Monsters freefall when they don't have a ground entity, otherwise
1355 all movement is done with discrete steps.
1357 This is also used for objects that have become still on the ground, but
1358 will fall if the floor is pulled out from under them.
1361 void SV_Physics_Step (edict_t *ent)
1365 // freefall if not onground
1366 if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) )
1368 if (ent->v.velocity[2] < sv_gravity.value*-0.1)
1373 SV_AddGravity (ent);
1374 SV_CheckVelocity (ent);
1375 SV_FlyMove (ent, sv.frametime, NULL);
1376 SV_LinkEdict (ent, true);
1378 if ( (int)ent->v.flags & FL_ONGROUND ) // just hit ground
1381 SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1);
1388 SV_CheckWaterTransition (ent);
1391 //============================================================================
1399 void SV_Physics (void)
1404 // let the progs know that a new frame has started
1405 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1406 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1407 pr_global_struct->time = sv.time;
1408 PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1410 //SV_CheckAllEnts ();
1413 // treat each object in turn
1416 for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1421 if (pr_global_struct->force_retouch)
1422 SV_LinkEdict (ent, true); // force retouch even for stationary
1424 if (i > 0 && i <= svs.maxclients)
1426 SV_Physics_Client (ent, i);
1430 switch ((int) ent->v.movetype)
1433 SV_Physics_Pusher (ent);
1436 // SV_Physics_None (ent);
1437 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1438 if (ent->v.nextthink > 0 && ent->v.nextthink <= sv.time + sv.frametime)
1441 case MOVETYPE_FOLLOW:
1442 SV_Physics_Follow (ent);
1444 case MOVETYPE_NOCLIP:
1445 SV_Physics_Noclip (ent);
1448 SV_Physics_Step (ent);
1450 // LordHavoc: added support for MOVETYPE_WALK on normal entities! :)
1452 if (SV_RunThink (ent))
1454 if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) )
1455 SV_AddGravity (ent);
1456 SV_CheckStuck (ent);
1458 SV_LinkEdict (ent, true);
1462 case MOVETYPE_BOUNCE:
1463 case MOVETYPE_BOUNCEMISSILE:
1465 case MOVETYPE_FLYMISSILE:
1466 SV_Physics_Toss (ent);
1469 Host_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype);
1474 if (pr_global_struct->force_retouch)
1475 pr_global_struct->force_retouch--;
1477 // LordHavoc: endframe support
1480 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1481 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1482 pr_global_struct->time = sv.time;
1483 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "");
1486 sv.time += sv.frametime;
1490 trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore)
1493 edict_t tempent, *tent;
1498 memcpy(&tempent, ent, sizeof(edict_t));
1501 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1503 SV_CheckVelocity (tent);
1504 SV_AddGravity (tent);
1505 VectorMA (tent->v.angles, 0.05, tent->v.avelocity, tent->v.angles);
1506 VectorScale (tent->v.velocity, 0.05, move);
1507 VectorAdd (tent->v.origin, move, end);
1508 trace = SV_Move (tent->v.origin, tent->v.mins, tent->v.maxs, end, MOVE_NORMAL, tent);
1509 VectorCopy (trace.endpos, tent->v.origin);
1512 if (trace.ent != ignore)
1515 trace.fraction = 0; // not relevant